)]}'
{"version": 3, "sources": ["/web/static/lib/luxon/luxon.js", "/web/static/src/polyfills/object.js", "/web/static/src/polyfills/array.js", "/web_editor/static/src/js/frontend/loader_loading.js", "/web/static/lib/owl/owl.js", "/web/static/lib/owl/odoo_module.js", "/web/static/lib/jquery/jquery.js", "/web/static/lib/popper/popper.js", "/web/static/lib/bootstrap/js/dist/util/index.js", "/web/static/lib/bootstrap/js/dist/dom/data.js", "/web/static/lib/bootstrap/js/dist/dom/event-handler.js", "/web/static/lib/bootstrap/js/dist/dom/manipulator.js", "/web/static/lib/bootstrap/js/dist/dom/selector-engine.js", "/web/static/lib/bootstrap/js/dist/util/config.js", "/web/static/lib/bootstrap/js/dist/util/component-functions.js", "/web/static/lib/bootstrap/js/dist/util/backdrop.js", "/web/static/lib/bootstrap/js/dist/util/focustrap.js", "/web/static/lib/bootstrap/js/dist/util/sanitizer.js", "/web/static/lib/bootstrap/js/dist/util/scrollbar.js", "/web/static/lib/bootstrap/js/dist/util/swipe.js", "/web/static/lib/bootstrap/js/dist/util/template-factory.js", "/web/static/lib/bootstrap/js/dist/base-component.js", "/web/static/lib/bootstrap/js/dist/alert.js", "/web/static/lib/bootstrap/js/dist/button.js", "/web/static/lib/bootstrap/js/dist/carousel.js", "/web/static/lib/bootstrap/js/dist/collapse.js", "/web/static/lib/bootstrap/js/dist/dropdown.js", "/web/static/lib/bootstrap/js/dist/modal.js", "/web/static/lib/bootstrap/js/dist/offcanvas.js", "/web/static/lib/bootstrap/js/dist/tooltip.js", "/web/static/lib/bootstrap/js/dist/popover.js", "/web/static/lib/bootstrap/js/dist/scrollspy.js", "/web/static/lib/bootstrap/js/dist/tab.js", "/web/static/lib/bootstrap/js/dist/toast.js", "/web/static/src/libs/bootstrap.js", "/web/static/src/legacy/js/libs/jquery.js", "/web/static/src/legacy/js/core/class.js", "/web/static/src/env.js", "/web/static/src/core/action_swiper/action_swiper.js", "/web/static/src/core/anchor_scroll_prevention.js", "/web/static/src/core/assets.js", "/web/static/src/core/autocomplete/autocomplete.js", "/web/static/src/core/barcode/ZXingBarcodeDetector.js", "/web/static/src/core/barcode/barcode_dialog.js", "/web/static/src/core/barcode/barcode_video_scanner.js", "/web/static/src/core/barcode/crop_overlay.js", "/web/static/src/core/browser/browser.js", "/web/static/src/core/browser/feature_detection.js", "/web/static/src/core/browser/router.js", "/web/static/src/core/browser/title_service.js", "/web/static/src/core/checkbox/checkbox.js", "/web/static/src/core/code_editor/code_editor.js", "/web/static/src/core/colorlist/colorlist.js", "/web/static/src/core/colorpicker/colorpicker.js", "/web/static/src/core/colors/colors.js", "/web/static/src/core/confirmation_dialog/confirmation_dialog.js", "/web/static/src/core/context.js", "/web/static/src/core/copy_button/copy_button.js", "/web/static/src/core/currency.js", "/web/static/src/core/datetime/datetime_hook.js", "/web/static/src/core/datetime/datetime_input.js", "/web/static/src/core/datetime/datetime_picker.js", "/web/static/src/core/datetime/datetime_picker_popover.js", "/web/static/src/core/datetime/datetimepicker_service.js", "/web/static/src/core/debug/debug_context.js", "/web/static/src/core/debug/debug_menu_basic.js", "/web/static/src/core/debug/debug_menu_items.js", "/web/static/src/core/debug/debug_providers.js", "/web/static/src/core/debug/debug_utils.js", "/web/static/src/core/dialog/dialog.js", "/web/static/src/core/dialog/dialog_service.js", "/web/static/src/core/domain.js", "/web/static/src/core/domain_selector/domain_selector.js", "/web/static/src/core/domain_selector/domain_selector_operator_editor.js", "/web/static/src/core/domain_selector/utils.js", "/web/static/src/core/domain_selector_dialog/domain_selector_dialog.js", "/web/static/src/core/dropdown/_behaviours/dropdown_group_hook.js", "/web/static/src/core/dropdown/_behaviours/dropdown_nesting.js", "/web/static/src/core/dropdown/_behaviours/dropdown_popover.js", "/web/static/src/core/dropdown/accordion_item.js", "/web/static/src/core/dropdown/checkbox_item.js", "/web/static/src/core/dropdown/dropdown.js", "/web/static/src/core/dropdown/dropdown_group.js", "/web/static/src/core/dropdown/dropdown_hooks.js", "/web/static/src/core/dropdown/dropdown_item.js", "/web/static/src/core/dropzone/dropzone.js", "/web/static/src/core/dropzone/dropzone_hook.js", "/web/static/src/core/effects/effect_service.js", "/web/static/src/core/effects/rainbow_man.js", "/web/static/src/core/emoji_picker/emoji_picker.js", "/web/static/src/core/ensure_jquery.js", "/web/static/src/core/errors/error_dialogs.js", "/web/static/src/core/errors/error_handlers.js", "/web/static/src/core/errors/error_service.js", "/web/static/src/core/errors/error_utils.js", "/web/static/src/core/errors/scss_error_dialog.js", "/web/static/src/core/expression_editor/expression_editor.js", "/web/static/src/core/expression_editor/expression_editor_operator_editor.js", "/web/static/src/core/expression_editor_dialog/expression_editor_dialog.js", "/web/static/src/core/field_service.js", "/web/static/src/core/file_input/file_input.js", "/web/static/src/core/file_upload/file_upload_progress_bar.js", "/web/static/src/core/file_upload/file_upload_progress_container.js", "/web/static/src/core/file_upload/file_upload_progress_record.js", "/web/static/src/core/file_upload/file_upload_service.js", "/web/static/src/core/file_viewer/file_model.js", "/web/static/src/core/file_viewer/file_viewer.js", "/web/static/src/core/file_viewer/file_viewer_hook.js", "/web/static/src/core/hotkeys/hotkey_hook.js", "/web/static/src/core/hotkeys/hotkey_service.js", "/web/static/src/core/install_scoped_app/install_scoped_app.js", "/web/static/src/core/l10n/dates.js", "/web/static/src/core/l10n/localization.js", "/web/static/src/core/l10n/localization_service.js", "/web/static/src/core/l10n/translation.js", "/web/static/src/core/l10n/utils.js", "/web/static/src/core/l10n/utils/format_list.js", "/web/static/src/core/l10n/utils/locales.js", "/web/static/src/core/macro.js", "/web/static/src/core/main_components_container.js", "/web/static/src/core/model_field_selector/model_field_selector.js", "/web/static/src/core/model_field_selector/model_field_selector_popover.js", "/web/static/src/core/model_field_selector/utils.js", "/web/static/src/core/model_selector/model_selector.js", "/web/static/src/core/name_service.js", "/web/static/src/core/navigation/navigation.js", "/web/static/src/core/network/download.js", "/web/static/src/core/network/http_service.js", "/web/static/src/core/network/rpc.js", "/web/static/src/core/notebook/notebook.js", "/web/static/src/core/notifications/notification.js", "/web/static/src/core/notifications/notification_container.js", "/web/static/src/core/notifications/notification_service.js", "/web/static/src/core/orm_service.js", "/web/static/src/core/overlay/overlay_container.js", "/web/static/src/core/overlay/overlay_service.js", "/web/static/src/core/pager/pager.js", "/web/static/src/core/pager/pager_indicator.js", "/web/static/src/core/popover/popover.js", "/web/static/src/core/popover/popover_hook.js", "/web/static/src/core/popover/popover_service.js", "/web/static/src/core/position/position_hook.js", "/web/static/src/core/position/utils.js", "/web/static/src/core/pwa/install_prompt.js", "/web/static/src/core/pwa/pwa_service.js", "/web/static/src/core/py_js/py.js", "/web/static/src/core/py_js/py_builtin.js", "/web/static/src/core/py_js/py_date.js", "/web/static/src/core/py_js/py_interpreter.js", "/web/static/src/core/py_js/py_parser.js", "/web/static/src/core/py_js/py_tokenizer.js", "/web/static/src/core/py_js/py_utils.js", "/web/static/src/core/record_selectors/multi_record_selector.js", "/web/static/src/core/record_selectors/record_autocomplete.js", "/web/static/src/core/record_selectors/record_selector.js", "/web/static/src/core/record_selectors/tag_navigation_hook.js", "/web/static/src/core/registry.js", "/web/static/src/core/registry_hook.js", "/web/static/src/core/resizable_panel/resizable_panel.js", "/web/static/src/core/select_menu/select_menu.js", "/web/static/src/core/signature/name_and_signature.js", "/web/static/src/core/signature/signature_dialog.js", "/web/static/src/core/tags_list/tags_list.js", "/web/static/src/core/template_inheritance.js", "/web/static/src/core/templates.js", "/web/static/src/core/tooltip/tooltip.js", "/web/static/src/core/tooltip/tooltip_hook.js", "/web/static/src/core/tooltip/tooltip_service.js", "/web/static/src/core/transition.js", "/web/static/src/core/tree_editor/condition_tree.js", "/web/static/src/core/tree_editor/tree_editor.js", "/web/static/src/core/tree_editor/tree_editor_autocomplete.js", "/web/static/src/core/tree_editor/tree_editor_components.js", "/web/static/src/core/tree_editor/tree_editor_operator_editor.js", "/web/static/src/core/tree_editor/tree_editor_value_editors.js", "/web/static/src/core/tree_editor/utils.js", "/web/static/src/core/ui/block_ui.js", "/web/static/src/core/ui/ui_service.js", "/web/static/src/core/user.js", "/web/static/src/core/user_switch/user_switch.js", "/web/static/src/core/utils/arrays.js", "/web/static/src/core/utils/autoresize.js", "/web/static/src/core/utils/binary.js", "/web/static/src/core/utils/cache.js", "/web/static/src/core/utils/classname.js", "/web/static/src/core/utils/colors.js", "/web/static/src/core/utils/components.js", "/web/static/src/core/utils/concurrency.js", "/web/static/src/core/utils/draggable.js", "/web/static/src/core/utils/draggable_hook_builder.js", "/web/static/src/core/utils/draggable_hook_builder_owl.js", "/web/static/src/core/utils/files.js", "/web/static/src/core/utils/functions.js", "/web/static/src/core/utils/hooks.js", "/web/static/src/core/utils/misc.js", "/web/static/src/core/utils/nested_sortable.js", "/web/static/src/core/utils/numbers.js", "/web/static/src/core/utils/objects.js", "/web/static/src/core/utils/patch.js", "/web/static/src/core/utils/reactive.js", "/web/static/src/core/utils/render.js", "/web/static/src/core/utils/scrolling.js", "/web/static/src/core/utils/search.js", "/web/static/src/core/utils/sortable.js", "/web/static/src/core/utils/sortable_owl.js", "/web/static/src/core/utils/sortable_service.js", "/web/static/src/core/utils/strings.js", "/web/static/src/core/utils/timing.js", "/web/static/src/core/utils/urls.js", "/web/static/src/core/utils/xml.js", "/web/static/src/core/virtual_grid_hook.js", "/web/static/src/core/commands/default_providers.js", "/web/static/src/core/commands/command_palette.js", "/web/static/src/public/error_notifications.js", "/web/static/src/public/public_component_service.js", "/web/static/src/public/datetime_picker_widget.js", "/web/static/src/libs/pdfjs.js", "/web/static/src/legacy/js/public/public_root.js", "/web/static/src/legacy/js/public/public_root_instance.js", "/web/static/src/legacy/js/public/public_widget.js", "/web/static/src/legacy/js/public/signin.js", "/bus/static/src/bus_parameters_service.js", "/bus/static/src/im_status_service.js", "/bus/static/src/misc.js", "/bus/static/src/multi_tab_service.js", "/bus/static/src/outdated_page_watcher_service.js", "/bus/static/src/services/bus_monitoring_service.js", "/bus/static/src/services/bus_service.js", "/bus/static/src/services/presence_service.js", "/bus/static/src/workers/websocket_worker.js", "/bus/static/src/workers/websocket_worker_utils.js", "/web_tour/static/src/tour_pointer/tour_pointer.js", "/web_tour/static/src/tour_service/tour_automatic.js", "/web_tour/static/src/tour_service/tour_helpers.js", "/web_tour/static/src/tour_service/tour_interactive.js", "/web_tour/static/src/tour_service/tour_pointer_state.js", "/web_tour/static/src/tour_service/tour_recorder/tour_recorder.js", "/web_tour/static/src/tour_service/tour_recorder/tour_recorder_state.js", "/web_tour/static/src/tour_service/tour_service.js", "/web_tour/static/src/tour_service/tour_state.js", "/web_tour/static/src/tour_service/tour_step.js", "/web_tour/static/src/tour_service/tour_step_automatic.js", "/web_tour/static/src/tour_service/tour_utils.js", "/web/static/lib/hoot-dom/helpers/dom.js", "/web/static/lib/hoot-dom/helpers/events.js", "/web/static/lib/hoot-dom/helpers/time.js", "/web/static/lib/hoot-dom/hoot-dom.js", "/web/static/lib/hoot-dom/hoot_dom_utils.js", "/html_editor/static/src/main/media/media_dialog/document_selector.js", "/html_editor/static/src/main/media/media_dialog/file_documents_selector.js", "/html_editor/static/src/main/media/media_dialog/file_media_dialog.js", "/html_editor/static/src/main/media/media_dialog/file_selector.js", "/html_editor/static/src/main/media/media_dialog/icon_selector.js", "/html_editor/static/src/main/media/media_dialog/image_selector.js", "/html_editor/static/src/main/media/media_dialog/media_dialog.js", "/html_editor/static/src/main/media/media_dialog/search_media.js", "/html_editor/static/src/main/media/media_dialog/upload_progress_toast/upload_progress_toast.js", "/html_editor/static/src/main/media/media_dialog/upload_progress_toast/upload_service.js", "/html_editor/static/src/main/media/media_dialog/video_selector.js", "/html_editor/static/src/utils/blocks.js", "/html_editor/static/src/utils/color.js", "/html_editor/static/src/utils/content_types.js", "/html_editor/static/src/utils/dom.js", "/html_editor/static/src/utils/dom_info.js", "/html_editor/static/src/utils/dom_state.js", "/html_editor/static/src/utils/dom_traversal.js", "/html_editor/static/src/utils/drag_and_drop.js", "/html_editor/static/src/utils/fonts.js", "/html_editor/static/src/utils/formatting.js", "/html_editor/static/src/utils/html.js", "/html_editor/static/src/utils/image.js", "/html_editor/static/src/utils/image_processing.js", "/html_editor/static/src/utils/list.js", "/html_editor/static/src/utils/perspective_utils.js", "/html_editor/static/src/utils/position.js", "/html_editor/static/src/utils/regex.js", "/html_editor/static/src/utils/resource.js", "/html_editor/static/src/utils/sanitize.js", "/html_editor/static/src/utils/selection.js", "/html_editor/static/src/utils/table.js", "/html_editor/static/src/utils/url.js", "/web_unsplash/static/src/media_dialog/image_selector_patch.js", "/web_unsplash/static/src/media_dialog/media_dialog_patch.js", "/web_unsplash/static/src/unsplash_credentials/unsplash_credentials.js", "/web_unsplash/static/src/unsplash_error/unsplash_error.js", "/web_unsplash/static/src/unsplash_service.js", "/web_editor/static/src/components/media_dialog/document_selector.js", "/web_editor/static/src/components/media_dialog/file_selector.js", "/web_editor/static/src/components/media_dialog/icon_selector.js", "/web_editor/static/src/components/media_dialog/image_selector.js", "/web_editor/static/src/components/media_dialog/media_dialog.js", "/web_editor/static/src/components/media_dialog/search_media.js", "/web_editor/static/src/components/media_dialog/video_selector.js", "/web_editor/static/src/components/upload_progress_toast/upload_progress_toast.js", "/web_editor/static/src/components/upload_progress_toast/upload_service.js", "/web_unsplash/static/src/media_dialog_legacy/image_selector.js", "/web_editor/static/src/js/common/browser_extensions.js", "/web_editor/static/src/js/common/column_layout_mixin.js", "/web_editor/static/src/js/common/grid_layout_utils.js", "/web_editor/static/src/js/common/scrolling.js", "/web_editor/static/src/js/common/utils.js", "/web_editor/static/src/js/common/wysiwyg_utils.js", "/web_editor/static/src/js/core/owl_utils.js", "/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js", "/web_editor/static/src/js/wysiwyg/fonts.js", "/web_editor/static/src/js/frontend/loadWysiwygFromTextarea.js", "/auth_signup/static/src/js/reset_password.js", "/auth_signup/static/src/js/signup.js", "/portal/static/src/js/portal.js", "/portal/static/src/js/portal_composer.js", "/portal/static/src/js/portal_security.js", "/portal/static/src/js/portal_sidebar.js", "/portal/static/src/js/components/input_confirmation_dialog/input_confirmation_dialog.js", "/portal/static/src/signature_form/signature_form.js", "/portal/static/src/chatter/boot/boot_service.js", "/account/static/src/js/account_portal_sidebar.js", "/account/static/src/js/account_portal.js", "/account/static/src/components/tests_shared_js_python/tests_shared_js_python.js", "/account/static/src/helpers/account_tax.js", "/account/static/src/core/utils/product_and_label_autoresize.js", "/payment/static/lib/jquery.payment/jquery.payment.js", "/payment/static/src/js/express_checkout_form.js", "/payment/static/src/js/payment_button.js", "/payment/static/src/js/payment_form.js", "/payment/static/src/js/post_processing.js", "/account_payment/static/src/js/payment_form.js", "/account_payment/static/src/js/portal_invoice_page_payment.js", "/account_payment/static/src/js/portal_my_invoices_payment.js", "/sale/static/src/js/sale_portal_sidebar.js", "/sale/static/src/js/sale_portal_prepayment.js", "/sale/static/src/js/sale_portal.js", "/sale_management/static/src/js/sale_management.js", "/account_online_synchronization/static/src/js/online_sync_portal.js", "/auth_totp_portal/static/src/js/totp_frontend.js", "/web_unsplash/static/src/frontend/unsplash_beacon.js"], "mappings": "AAAA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/iPA;;;;;AAAA;AACA;AACA;AACA;AACA;ACJA;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACZA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACnCA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AClgMA;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;ACLA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AChvVA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjyDA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACzRA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/DA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7OA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACxEA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACxGA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACpEA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC1CA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC3IA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjHA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AClHA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjHA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvIA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvJA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACpFA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC1FA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/EA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACpYA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACzPA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACnZA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AChUA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACtPA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACliBA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AChGA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACnRA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7RA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClHA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjOA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACTA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9NA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7aA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3LA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChNA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzJA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7ZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpLA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACldA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzNA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpsBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/fA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxDA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACXA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjJA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpbA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtGA;;;;;;;;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;;;;ACzBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClJA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtCA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChVA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtDA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtiBA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxOA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1LA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjHA;;;;;;;;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;;;;ACzBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9GA;;;;;;;;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;AACA;;;;AC5BA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACVA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1PA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrCA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjeA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpnBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChFA;;;;;;;;AAAA;AACA;AACA;;;;ACFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3EA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxWA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1SA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjTA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7jBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5HA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3CA;;;;;;;;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;;;;ACxBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3UA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/EA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClQA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3EA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3HA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvOA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjLA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjHA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC73BA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3eA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxYA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5TA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7EA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7MA;;;;;;;;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;;;;ACzBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/VA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3UA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnTA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9KA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACXA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzOA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9kCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/QA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5EA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9VA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1XA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5OA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClRA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;;;;AChCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1QA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACXA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpjCA;;;;;;;;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;;;;ACxBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClRA;;;;;;;;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;AACA;;;;AC5BA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9YA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChQA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzHA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1IA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvVA;;;;;;;;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;;;;ACxBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrWA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClNA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChKA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpLA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5YA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClVA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;ACPA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/4BA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnCA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACbA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxOA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjSA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvgBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3KA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5MA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1UA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7ZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/MA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtRA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;;;;AChCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClRA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7JA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtQA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACx3DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClhFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChcA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC3FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7DA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/dA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC1FA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnbA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxTA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzLA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtRA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5LA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3RA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5pBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtjBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnWA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjNA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9OA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxlBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7EA;;;;;;;;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;AACA;AACA;AACA;;;;AC9BA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACRA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC9QA;;;;;;;;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;;;;ACxBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzMA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvCA;;;;;;;;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;;;;AC3BA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClIA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxcA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/SA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;;;;AChCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACxQA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClLA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnNA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClHA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AClWA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtJA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChkBA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnvGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACtGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnEA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvBA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7KA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5NA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3LA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC/CA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC7FA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACnBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvEA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACvGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC58BA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjCA;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC5oBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChGA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjFA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpmBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AChEA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5CA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACpBA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACrCA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzHA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACjFA;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACZA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC3GA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACzDA;;;;;;;;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;AC5QA;;;;;;;;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;AACA;AACA;AACA;AACA", "sourcesContent": ["var luxon = (function (exports) {\n  'use strict';\n\n  // these aren't really private, but nor are they really useful to document\n\n  /**\n   * @private\n   */\n  class LuxonError extends Error {}\n\n  /**\n   * @private\n   */\n  class InvalidDateTimeError extends LuxonError {\n    constructor(reason) {\n      super(`Invalid DateTime: ${reason.toMessage()}`);\n    }\n  }\n\n  /**\n   * @private\n   */\n  class InvalidIntervalError extends LuxonError {\n    constructor(reason) {\n      super(`Invalid Interval: ${reason.toMessage()}`);\n    }\n  }\n\n  /**\n   * @private\n   */\n  class InvalidDurationError extends LuxonError {\n    constructor(reason) {\n      super(`Invalid Duration: ${reason.toMessage()}`);\n    }\n  }\n\n  /**\n   * @private\n   */\n  class ConflictingSpecificationError extends LuxonError {}\n\n  /**\n   * @private\n   */\n  class InvalidUnitError extends LuxonError {\n    constructor(unit) {\n      super(`Invalid unit ${unit}`);\n    }\n  }\n\n  /**\n   * @private\n   */\n  class InvalidArgumentError extends LuxonError {}\n\n  /**\n   * @private\n   */\n  class ZoneIsAbstractError extends LuxonError {\n    constructor() {\n      super(\"Zone is an abstract class\");\n    }\n  }\n\n  /**\n   * @private\n   */\n\n  const n = \"numeric\",\n    s = \"short\",\n    l = \"long\";\n\n  const DATE_SHORT = {\n    year: n,\n    month: n,\n    day: n,\n  };\n\n  const DATE_MED = {\n    year: n,\n    month: s,\n    day: n,\n  };\n\n  const DATE_MED_WITH_WEEKDAY = {\n    year: n,\n    month: s,\n    day: n,\n    weekday: s,\n  };\n\n  const DATE_FULL = {\n    year: n,\n    month: l,\n    day: n,\n  };\n\n  const DATE_HUGE = {\n    year: n,\n    month: l,\n    day: n,\n    weekday: l,\n  };\n\n  const TIME_SIMPLE = {\n    hour: n,\n    minute: n,\n  };\n\n  const TIME_WITH_SECONDS = {\n    hour: n,\n    minute: n,\n    second: n,\n  };\n\n  const TIME_WITH_SHORT_OFFSET = {\n    hour: n,\n    minute: n,\n    second: n,\n    timeZoneName: s,\n  };\n\n  const TIME_WITH_LONG_OFFSET = {\n    hour: n,\n    minute: n,\n    second: n,\n    timeZoneName: l,\n  };\n\n  const TIME_24_SIMPLE = {\n    hour: n,\n    minute: n,\n    hourCycle: \"h23\",\n  };\n\n  const TIME_24_WITH_SECONDS = {\n    hour: n,\n    minute: n,\n    second: n,\n    hourCycle: \"h23\",\n  };\n\n  const TIME_24_WITH_SHORT_OFFSET = {\n    hour: n,\n    minute: n,\n    second: n,\n    hourCycle: \"h23\",\n    timeZoneName: s,\n  };\n\n  const TIME_24_WITH_LONG_OFFSET = {\n    hour: n,\n    minute: n,\n    second: n,\n    hourCycle: \"h23\",\n    timeZoneName: l,\n  };\n\n  const DATETIME_SHORT = {\n    year: n,\n    month: n,\n    day: n,\n    hour: n,\n    minute: n,\n  };\n\n  const DATETIME_SHORT_WITH_SECONDS = {\n    year: n,\n    month: n,\n    day: n,\n    hour: n,\n    minute: n,\n    second: n,\n  };\n\n  const DATETIME_MED = {\n    year: n,\n    month: s,\n    day: n,\n    hour: n,\n    minute: n,\n  };\n\n  const DATETIME_MED_WITH_SECONDS = {\n    year: n,\n    month: s,\n    day: n,\n    hour: n,\n    minute: n,\n    second: n,\n  };\n\n  const DATETIME_MED_WITH_WEEKDAY = {\n    year: n,\n    month: s,\n    day: n,\n    weekday: s,\n    hour: n,\n    minute: n,\n  };\n\n  const DATETIME_FULL = {\n    year: n,\n    month: l,\n    day: n,\n    hour: n,\n    minute: n,\n    timeZoneName: s,\n  };\n\n  const DATETIME_FULL_WITH_SECONDS = {\n    year: n,\n    month: l,\n    day: n,\n    hour: n,\n    minute: n,\n    second: n,\n    timeZoneName: s,\n  };\n\n  const DATETIME_HUGE = {\n    year: n,\n    month: l,\n    day: n,\n    weekday: l,\n    hour: n,\n    minute: n,\n    timeZoneName: l,\n  };\n\n  const DATETIME_HUGE_WITH_SECONDS = {\n    year: n,\n    month: l,\n    day: n,\n    weekday: l,\n    hour: n,\n    minute: n,\n    second: n,\n    timeZoneName: l,\n  };\n\n  /**\n   * @interface\n   */\n  class Zone {\n    /**\n     * The type of zone\n     * @abstract\n     * @type {string}\n     */\n    get type() {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * The name of this zone.\n     * @abstract\n     * @type {string}\n     */\n    get name() {\n      throw new ZoneIsAbstractError();\n    }\n\n    get ianaName() {\n      return this.name;\n    }\n\n    /**\n     * Returns whether the offset is known to be fixed for the whole year.\n     * @abstract\n     * @type {boolean}\n     */\n    get isUniversal() {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * Returns the offset's common name (such as EST) at the specified timestamp\n     * @abstract\n     * @param {number} ts - Epoch milliseconds for which to get the name\n     * @param {Object} opts - Options to affect the format\n     * @param {string} opts.format - What style of offset to return. Accepts 'long' or 'short'.\n     * @param {string} opts.locale - What locale to return the offset name in.\n     * @return {string}\n     */\n    offsetName(ts, opts) {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * Returns the offset's value as a string\n     * @abstract\n     * @param {number} ts - Epoch milliseconds for which to get the offset\n     * @param {string} format - What style of offset to return.\n     *                          Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively\n     * @return {string}\n     */\n    formatOffset(ts, format) {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * Return the offset in minutes for this zone at the specified timestamp.\n     * @abstract\n     * @param {number} ts - Epoch milliseconds for which to compute the offset\n     * @return {number}\n     */\n    offset(ts) {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * Return whether this Zone is equal to another zone\n     * @abstract\n     * @param {Zone} otherZone - the zone to compare\n     * @return {boolean}\n     */\n    equals(otherZone) {\n      throw new ZoneIsAbstractError();\n    }\n\n    /**\n     * Return whether this Zone is valid.\n     * @abstract\n     * @type {boolean}\n     */\n    get isValid() {\n      throw new ZoneIsAbstractError();\n    }\n  }\n\n  let singleton$1 = null;\n\n  /**\n   * Represents the local zone for this JavaScript environment.\n   * @implements {Zone}\n   */\n  class SystemZone extends Zone {\n    /**\n     * Get a singleton instance of the local zone\n     * @return {SystemZone}\n     */\n    static get instance() {\n      if (singleton$1 === null) {\n        singleton$1 = new SystemZone();\n      }\n      return singleton$1;\n    }\n\n    /** @override **/\n    get type() {\n      return \"system\";\n    }\n\n    /** @override **/\n    get name() {\n      return new Intl.DateTimeFormat().resolvedOptions().timeZone;\n    }\n\n    /** @override **/\n    get isUniversal() {\n      return false;\n    }\n\n    /** @override **/\n    offsetName(ts, { format, locale }) {\n      return parseZoneInfo(ts, format, locale);\n    }\n\n    /** @override **/\n    formatOffset(ts, format) {\n      return formatOffset(this.offset(ts), format);\n    }\n\n    /** @override **/\n    offset(ts) {\n      return -new Date(ts).getTimezoneOffset();\n    }\n\n    /** @override **/\n    equals(otherZone) {\n      return otherZone.type === \"system\";\n    }\n\n    /** @override **/\n    get isValid() {\n      return true;\n    }\n  }\n\n  let dtfCache = {};\n  function makeDTF(zone) {\n    if (!dtfCache[zone]) {\n      dtfCache[zone] = new Intl.DateTimeFormat(\"en-US\", {\n        hour12: false,\n        timeZone: zone,\n        year: \"numeric\",\n        month: \"2-digit\",\n        day: \"2-digit\",\n        hour: \"2-digit\",\n        minute: \"2-digit\",\n        second: \"2-digit\",\n        era: \"short\",\n      });\n    }\n    return dtfCache[zone];\n  }\n\n  const typeToPos = {\n    year: 0,\n    month: 1,\n    day: 2,\n    era: 3,\n    hour: 4,\n    minute: 5,\n    second: 6,\n  };\n\n  function hackyOffset(dtf, date) {\n    const formatted = dtf.format(date).replace(/\\u200E/g, \"\"),\n      parsed = /(\\d+)\\/(\\d+)\\/(\\d+) (AD|BC),? (\\d+):(\\d+):(\\d+)/.exec(formatted),\n      [, fMonth, fDay, fYear, fadOrBc, fHour, fMinute, fSecond] = parsed;\n    return [fYear, fMonth, fDay, fadOrBc, fHour, fMinute, fSecond];\n  }\n\n  function partsOffset(dtf, date) {\n    const formatted = dtf.formatToParts(date);\n    const filled = [];\n    for (let i = 0; i < formatted.length; i++) {\n      const { type, value } = formatted[i];\n      const pos = typeToPos[type];\n\n      if (type === \"era\") {\n        filled[pos] = value;\n      } else if (!isUndefined(pos)) {\n        filled[pos] = parseInt(value, 10);\n      }\n    }\n    return filled;\n  }\n\n  let ianaZoneCache = {};\n  /**\n   * A zone identified by an IANA identifier, like America/New_York\n   * @implements {Zone}\n   */\n  class IANAZone extends Zone {\n    /**\n     * @param {string} name - Zone name\n     * @return {IANAZone}\n     */\n    static create(name) {\n      if (!ianaZoneCache[name]) {\n        ianaZoneCache[name] = new IANAZone(name);\n      }\n      return ianaZoneCache[name];\n    }\n\n    /**\n     * Reset local caches. Should only be necessary in testing scenarios.\n     * @return {void}\n     */\n    static resetCache() {\n      ianaZoneCache = {};\n      dtfCache = {};\n    }\n\n    /**\n     * Returns whether the provided string is a valid specifier. This only checks the string's format, not that the specifier identifies a known zone; see isValidZone for that.\n     * @param {string} s - The string to check validity on\n     * @example IANAZone.isValidSpecifier(\"America/New_York\") //=> true\n     * @example IANAZone.isValidSpecifier(\"Sport~~blorp\") //=> false\n     * @deprecated This method returns false for some valid IANA names. Use isValidZone instead.\n     * @return {boolean}\n     */\n    static isValidSpecifier(s) {\n      return this.isValidZone(s);\n    }\n\n    /**\n     * Returns whether the provided string identifies a real zone\n     * @param {string} zone - The string to check\n     * @example IANAZone.isValidZone(\"America/New_York\") //=> true\n     * @example IANAZone.isValidZone(\"Fantasia/Castle\") //=> false\n     * @example IANAZone.isValidZone(\"Sport~~blorp\") //=> false\n     * @return {boolean}\n     */\n    static isValidZone(zone) {\n      if (!zone) {\n        return false;\n      }\n      try {\n        new Intl.DateTimeFormat(\"en-US\", { timeZone: zone }).format();\n        return true;\n      } catch (e) {\n        return false;\n      }\n    }\n\n    constructor(name) {\n      super();\n      /** @private **/\n      this.zoneName = name;\n      /** @private **/\n      this.valid = IANAZone.isValidZone(name);\n    }\n\n    /** @override **/\n    get type() {\n      return \"iana\";\n    }\n\n    /** @override **/\n    get name() {\n      return this.zoneName;\n    }\n\n    /** @override **/\n    get isUniversal() {\n      return false;\n    }\n\n    /** @override **/\n    offsetName(ts, { format, locale }) {\n      return parseZoneInfo(ts, format, locale, this.name);\n    }\n\n    /** @override **/\n    formatOffset(ts, format) {\n      return formatOffset(this.offset(ts), format);\n    }\n\n    /** @override **/\n    offset(ts) {\n      const date = new Date(ts);\n\n      if (isNaN(date)) return NaN;\n\n      const dtf = makeDTF(this.name);\n      let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts\n        ? partsOffset(dtf, date)\n        : hackyOffset(dtf, date);\n\n      if (adOrBc === \"BC\") {\n        year = -Math.abs(year) + 1;\n      }\n\n      // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat\n      const adjustedHour = hour === 24 ? 0 : hour;\n\n      const asUTC = objToLocalTS({\n        year,\n        month,\n        day,\n        hour: adjustedHour,\n        minute,\n        second,\n        millisecond: 0,\n      });\n\n      let asTS = +date;\n      const over = asTS % 1000;\n      asTS -= over >= 0 ? over : 1000 + over;\n      return (asUTC - asTS) / (60 * 1000);\n    }\n\n    /** @override **/\n    equals(otherZone) {\n      return otherZone.type === \"iana\" && otherZone.name === this.name;\n    }\n\n    /** @override **/\n    get isValid() {\n      return this.valid;\n    }\n  }\n\n  // todo - remap caching\n\n  let intlLFCache = {};\n  function getCachedLF(locString, opts = {}) {\n    const key = JSON.stringify([locString, opts]);\n    let dtf = intlLFCache[key];\n    if (!dtf) {\n      dtf = new Intl.ListFormat(locString, opts);\n      intlLFCache[key] = dtf;\n    }\n    return dtf;\n  }\n\n  let intlDTCache = {};\n  function getCachedDTF(locString, opts = {}) {\n    const key = JSON.stringify([locString, opts]);\n    let dtf = intlDTCache[key];\n    if (!dtf) {\n      dtf = new Intl.DateTimeFormat(locString, opts);\n      intlDTCache[key] = dtf;\n    }\n    return dtf;\n  }\n\n  let intlNumCache = {};\n  function getCachedINF(locString, opts = {}) {\n    const key = JSON.stringify([locString, opts]);\n    let inf = intlNumCache[key];\n    if (!inf) {\n      inf = new Intl.NumberFormat(locString, opts);\n      intlNumCache[key] = inf;\n    }\n    return inf;\n  }\n\n  let intlRelCache = {};\n  function getCachedRTF(locString, opts = {}) {\n    const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options\n    const key = JSON.stringify([locString, cacheKeyOpts]);\n    let inf = intlRelCache[key];\n    if (!inf) {\n      inf = new Intl.RelativeTimeFormat(locString, opts);\n      intlRelCache[key] = inf;\n    }\n    return inf;\n  }\n\n  let sysLocaleCache = null;\n  function systemLocale() {\n    if (sysLocaleCache) {\n      return sysLocaleCache;\n    } else {\n      sysLocaleCache = new Intl.DateTimeFormat().resolvedOptions().locale;\n      return sysLocaleCache;\n    }\n  }\n\n  let weekInfoCache = {};\n  function getCachedWeekInfo(locString) {\n    let data = weekInfoCache[locString];\n    if (!data) {\n      const locale = new Intl.Locale(locString);\n      // browsers currently implement this as a property, but spec says it should be a getter function\n      data = \"getWeekInfo\" in locale ? locale.getWeekInfo() : locale.weekInfo;\n      weekInfoCache[locString] = data;\n    }\n    return data;\n  }\n\n  function parseLocaleString(localeStr) {\n    // I really want to avoid writing a BCP 47 parser\n    // see, e.g. https://github.com/wooorm/bcp-47\n    // Instead, we'll do this:\n\n    // a) if the string has no -u extensions, just leave it alone\n    // b) if it does, use Intl to resolve everything\n    // c) if Intl fails, try again without the -u\n\n    // private subtags and unicode subtags have ordering requirements,\n    // and we're not properly parsing this, so just strip out the\n    // private ones if they exist.\n    const xIndex = localeStr.indexOf(\"-x-\");\n    if (xIndex !== -1) {\n      localeStr = localeStr.substring(0, xIndex);\n    }\n\n    const uIndex = localeStr.indexOf(\"-u-\");\n    if (uIndex === -1) {\n      return [localeStr];\n    } else {\n      let options;\n      let selectedStr;\n      try {\n        options = getCachedDTF(localeStr).resolvedOptions();\n        selectedStr = localeStr;\n      } catch (e) {\n        const smaller = localeStr.substring(0, uIndex);\n        options = getCachedDTF(smaller).resolvedOptions();\n        selectedStr = smaller;\n      }\n\n      const { numberingSystem, calendar } = options;\n      return [selectedStr, numberingSystem, calendar];\n    }\n  }\n\n  function intlConfigString(localeStr, numberingSystem, outputCalendar) {\n    if (outputCalendar || numberingSystem) {\n      if (!localeStr.includes(\"-u-\")) {\n        localeStr += \"-u\";\n      }\n\n      if (outputCalendar) {\n        localeStr += `-ca-${outputCalendar}`;\n      }\n\n      if (numberingSystem) {\n        localeStr += `-nu-${numberingSystem}`;\n      }\n      return localeStr;\n    } else {\n      return localeStr;\n    }\n  }\n\n  function mapMonths(f) {\n    const ms = [];\n    for (let i = 1; i <= 12; i++) {\n      const dt = DateTime.utc(2009, i, 1);\n      ms.push(f(dt));\n    }\n    return ms;\n  }\n\n  function mapWeekdays(f) {\n    const ms = [];\n    for (let i = 1; i <= 7; i++) {\n      const dt = DateTime.utc(2016, 11, 13 + i);\n      ms.push(f(dt));\n    }\n    return ms;\n  }\n\n  function listStuff(loc, length, englishFn, intlFn) {\n    const mode = loc.listingMode();\n\n    if (mode === \"error\") {\n      return null;\n    } else if (mode === \"en\") {\n      return englishFn(length);\n    } else {\n      return intlFn(length);\n    }\n  }\n\n  function supportsFastNumbers(loc) {\n    if (loc.numberingSystem && loc.numberingSystem !== \"latn\") {\n      return false;\n    } else {\n      return (\n        loc.numberingSystem === \"latn\" ||\n        !loc.locale ||\n        loc.locale.startsWith(\"en\") ||\n        new Intl.DateTimeFormat(loc.intl).resolvedOptions().numberingSystem === \"latn\"\n      );\n    }\n  }\n\n  /**\n   * @private\n   */\n\n  class PolyNumberFormatter {\n    constructor(intl, forceSimple, opts) {\n      this.padTo = opts.padTo || 0;\n      this.floor = opts.floor || false;\n\n      const { padTo, floor, ...otherOpts } = opts;\n\n      if (!forceSimple || Object.keys(otherOpts).length > 0) {\n        const intlOpts = { useGrouping: false, ...opts };\n        if (opts.padTo > 0) intlOpts.minimumIntegerDigits = opts.padTo;\n        this.inf = getCachedINF(intl, intlOpts);\n      }\n    }\n\n    format(i) {\n      if (this.inf) {\n        const fixed = this.floor ? Math.floor(i) : i;\n        return this.inf.format(fixed);\n      } else {\n        // to match the browser's numberformatter defaults\n        const fixed = this.floor ? Math.floor(i) : roundTo(i, 3);\n        return padStart(fixed, this.padTo);\n      }\n    }\n  }\n\n  /**\n   * @private\n   */\n\n  class PolyDateFormatter {\n    constructor(dt, intl, opts) {\n      this.opts = opts;\n      this.originalZone = undefined;\n\n      let z = undefined;\n      if (this.opts.timeZone) {\n        // Don't apply any workarounds if a timeZone is explicitly provided in opts\n        this.dt = dt;\n      } else if (dt.zone.type === \"fixed\") {\n        // UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.\n        // That is why fixed-offset TZ is set to that unless it is:\n        // 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.\n        // 2. Unsupported by the browser:\n        //    - some do not support Etc/\n        //    - < Etc/GMT-14, > Etc/GMT+12, and 30-minute or 45-minute offsets are not part of tzdata\n        const gmtOffset = -1 * (dt.offset / 60);\n        const offsetZ = gmtOffset >= 0 ? `Etc/GMT+${gmtOffset}` : `Etc/GMT${gmtOffset}`;\n        if (dt.offset !== 0 && IANAZone.create(offsetZ).valid) {\n          z = offsetZ;\n          this.dt = dt;\n        } else {\n          // Not all fixed-offset zones like Etc/+4:30 are present in tzdata so\n          // we manually apply the offset and substitute the zone as needed.\n          z = \"UTC\";\n          this.dt = dt.offset === 0 ? dt : dt.setZone(\"UTC\").plus({ minutes: dt.offset });\n          this.originalZone = dt.zone;\n        }\n      } else if (dt.zone.type === \"system\") {\n        this.dt = dt;\n      } else if (dt.zone.type === \"iana\") {\n        this.dt = dt;\n        z = dt.zone.name;\n      } else {\n        // Custom zones can have any offset / offsetName so we just manually\n        // apply the offset and substitute the zone as needed.\n        z = \"UTC\";\n        this.dt = dt.setZone(\"UTC\").plus({ minutes: dt.offset });\n        this.originalZone = dt.zone;\n      }\n\n      const intlOpts = { ...this.opts };\n      intlOpts.timeZone = intlOpts.timeZone || z;\n      this.dtf = getCachedDTF(intl, intlOpts);\n    }\n\n    format() {\n      if (this.originalZone) {\n        // If we have to substitute in the actual zone name, we have to use\n        // formatToParts so that the timezone can be replaced.\n        return this.formatToParts()\n          .map(({ value }) => value)\n          .join(\"\");\n      }\n      return this.dtf.format(this.dt.toJSDate());\n    }\n\n    formatToParts() {\n      const parts = this.dtf.formatToParts(this.dt.toJSDate());\n      if (this.originalZone) {\n        return parts.map((part) => {\n          if (part.type === \"timeZoneName\") {\n            const offsetName = this.originalZone.offsetName(this.dt.ts, {\n              locale: this.dt.locale,\n              format: this.opts.timeZoneName,\n            });\n            return {\n              ...part,\n              value: offsetName,\n            };\n          } else {\n            return part;\n          }\n        });\n      }\n      return parts;\n    }\n\n    resolvedOptions() {\n      return this.dtf.resolvedOptions();\n    }\n  }\n\n  /**\n   * @private\n   */\n  class PolyRelFormatter {\n    constructor(intl, isEnglish, opts) {\n      this.opts = { style: \"long\", ...opts };\n      if (!isEnglish && hasRelative()) {\n        this.rtf = getCachedRTF(intl, opts);\n      }\n    }\n\n    format(count, unit) {\n      if (this.rtf) {\n        return this.rtf.format(count, unit);\n      } else {\n        return formatRelativeTime(unit, count, this.opts.numeric, this.opts.style !== \"long\");\n      }\n    }\n\n    formatToParts(count, unit) {\n      if (this.rtf) {\n        return this.rtf.formatToParts(count, unit);\n      } else {\n        return [];\n      }\n    }\n  }\n\n  const fallbackWeekSettings = {\n    firstDay: 1,\n    minimalDays: 4,\n    weekend: [6, 7],\n  };\n\n  /**\n   * @private\n   */\n\n  class Locale {\n    static fromOpts(opts) {\n      return Locale.create(\n        opts.locale,\n        opts.numberingSystem,\n        opts.outputCalendar,\n        opts.weekSettings,\n        opts.defaultToEN\n      );\n    }\n\n    static create(locale, numberingSystem, outputCalendar, weekSettings, defaultToEN = false) {\n      const specifiedLocale = locale || Settings.defaultLocale;\n      // the system locale is useful for human readable strings but annoying for parsing/formatting known formats\n      const localeR = specifiedLocale || (defaultToEN ? \"en-US\" : systemLocale());\n      const numberingSystemR = numberingSystem || Settings.defaultNumberingSystem;\n      const outputCalendarR = outputCalendar || Settings.defaultOutputCalendar;\n      const weekSettingsR = validateWeekSettings(weekSettings) || Settings.defaultWeekSettings;\n      return new Locale(localeR, numberingSystemR, outputCalendarR, weekSettingsR, specifiedLocale);\n    }\n\n    static resetCache() {\n      sysLocaleCache = null;\n      intlDTCache = {};\n      intlNumCache = {};\n      intlRelCache = {};\n    }\n\n    static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {\n      return Locale.create(locale, numberingSystem, outputCalendar, weekSettings);\n    }\n\n    constructor(locale, numbering, outputCalendar, weekSettings, specifiedLocale) {\n      const [parsedLocale, parsedNumberingSystem, parsedOutputCalendar] = parseLocaleString(locale);\n\n      this.locale = parsedLocale;\n      this.numberingSystem = numbering || parsedNumberingSystem || null;\n      this.outputCalendar = outputCalendar || parsedOutputCalendar || null;\n      this.weekSettings = weekSettings;\n      this.intl = intlConfigString(this.locale, this.numberingSystem, this.outputCalendar);\n\n      this.weekdaysCache = { format: {}, standalone: {} };\n      this.monthsCache = { format: {}, standalone: {} };\n      this.meridiemCache = null;\n      this.eraCache = {};\n\n      this.specifiedLocale = specifiedLocale;\n      this.fastNumbersCached = null;\n    }\n\n    get fastNumbers() {\n      if (this.fastNumbersCached == null) {\n        this.fastNumbersCached = supportsFastNumbers(this);\n      }\n\n      return this.fastNumbersCached;\n    }\n\n    listingMode() {\n      const isActuallyEn = this.isEnglish();\n      const hasNoWeirdness =\n        (this.numberingSystem === null || this.numberingSystem === \"latn\") &&\n        (this.outputCalendar === null || this.outputCalendar === \"gregory\");\n      return isActuallyEn && hasNoWeirdness ? \"en\" : \"intl\";\n    }\n\n    clone(alts) {\n      if (!alts || Object.getOwnPropertyNames(alts).length === 0) {\n        return this;\n      } else {\n        return Locale.create(\n          alts.locale || this.specifiedLocale,\n          alts.numberingSystem || this.numberingSystem,\n          alts.outputCalendar || this.outputCalendar,\n          validateWeekSettings(alts.weekSettings) || this.weekSettings,\n          alts.defaultToEN || false\n        );\n      }\n    }\n\n    redefaultToEN(alts = {}) {\n      return this.clone({ ...alts, defaultToEN: true });\n    }\n\n    redefaultToSystem(alts = {}) {\n      return this.clone({ ...alts, defaultToEN: false });\n    }\n\n    months(length, format = false) {\n      return listStuff(this, length, months, () => {\n        const intl = format ? { month: length, day: \"numeric\" } : { month: length },\n          formatStr = format ? \"format\" : \"standalone\";\n        if (!this.monthsCache[formatStr][length]) {\n          this.monthsCache[formatStr][length] = mapMonths((dt) => this.extract(dt, intl, \"month\"));\n        }\n        return this.monthsCache[formatStr][length];\n      });\n    }\n\n    weekdays(length, format = false) {\n      return listStuff(this, length, weekdays, () => {\n        const intl = format\n            ? { weekday: length, year: \"numeric\", month: \"long\", day: \"numeric\" }\n            : { weekday: length },\n          formatStr = format ? \"format\" : \"standalone\";\n        if (!this.weekdaysCache[formatStr][length]) {\n          this.weekdaysCache[formatStr][length] = mapWeekdays((dt) =>\n            this.extract(dt, intl, \"weekday\")\n          );\n        }\n        return this.weekdaysCache[formatStr][length];\n      });\n    }\n\n    meridiems() {\n      return listStuff(\n        this,\n        undefined,\n        () => meridiems,\n        () => {\n          // In theory there could be aribitrary day periods. We're gonna assume there are exactly two\n          // for AM and PM. This is probably wrong, but it's makes parsing way easier.\n          if (!this.meridiemCache) {\n            const intl = { hour: \"numeric\", hourCycle: \"h12\" };\n            this.meridiemCache = [DateTime.utc(2016, 11, 13, 9), DateTime.utc(2016, 11, 13, 19)].map(\n              (dt) => this.extract(dt, intl, \"dayperiod\")\n            );\n          }\n\n          return this.meridiemCache;\n        }\n      );\n    }\n\n    eras(length) {\n      return listStuff(this, length, eras, () => {\n        const intl = { era: length };\n\n        // This is problematic. Different calendars are going to define eras totally differently. What I need is the minimum set of dates\n        // to definitely enumerate them.\n        if (!this.eraCache[length]) {\n          this.eraCache[length] = [DateTime.utc(-40, 1, 1), DateTime.utc(2017, 1, 1)].map((dt) =>\n            this.extract(dt, intl, \"era\")\n          );\n        }\n\n        return this.eraCache[length];\n      });\n    }\n\n    extract(dt, intlOpts, field) {\n      const df = this.dtFormatter(dt, intlOpts),\n        results = df.formatToParts(),\n        matching = results.find((m) => m.type.toLowerCase() === field);\n      return matching ? matching.value : null;\n    }\n\n    numberFormatter(opts = {}) {\n      // this forcesimple option is never used (the only caller short-circuits on it, but it seems safer to leave)\n      // (in contrast, the rest of the condition is used heavily)\n      return new PolyNumberFormatter(this.intl, opts.forceSimple || this.fastNumbers, opts);\n    }\n\n    dtFormatter(dt, intlOpts = {}) {\n      return new PolyDateFormatter(dt, this.intl, intlOpts);\n    }\n\n    relFormatter(opts = {}) {\n      return new PolyRelFormatter(this.intl, this.isEnglish(), opts);\n    }\n\n    listFormatter(opts = {}) {\n      return getCachedLF(this.intl, opts);\n    }\n\n    isEnglish() {\n      return (\n        this.locale === \"en\" ||\n        this.locale.toLowerCase() === \"en-us\" ||\n        new Intl.DateTimeFormat(this.intl).resolvedOptions().locale.startsWith(\"en-us\")\n      );\n    }\n\n    getWeekSettings() {\n      if (this.weekSettings) {\n        return this.weekSettings;\n      } else if (!hasLocaleWeekInfo()) {\n        return fallbackWeekSettings;\n      } else {\n        return getCachedWeekInfo(this.locale);\n      }\n    }\n\n    getStartOfWeek() {\n      return this.getWeekSettings().firstDay;\n    }\n\n    getMinDaysInFirstWeek() {\n      return this.getWeekSettings().minimalDays;\n    }\n\n    getWeekendDays() {\n      return this.getWeekSettings().weekend;\n    }\n\n    equals(other) {\n      return (\n        this.locale === other.locale &&\n        this.numberingSystem === other.numberingSystem &&\n        this.outputCalendar === other.outputCalendar\n      );\n    }\n  }\n\n  let singleton = null;\n\n  /**\n   * A zone with a fixed offset (meaning no DST)\n   * @implements {Zone}\n   */\n  class FixedOffsetZone extends Zone {\n    /**\n     * Get a singleton instance of UTC\n     * @return {FixedOffsetZone}\n     */\n    static get utcInstance() {\n      if (singleton === null) {\n        singleton = new FixedOffsetZone(0);\n      }\n      return singleton;\n    }\n\n    /**\n     * Get an instance with a specified offset\n     * @param {number} offset - The offset in minutes\n     * @return {FixedOffsetZone}\n     */\n    static instance(offset) {\n      return offset === 0 ? FixedOffsetZone.utcInstance : new FixedOffsetZone(offset);\n    }\n\n    /**\n     * Get an instance of FixedOffsetZone from a UTC offset string, like \"UTC+6\"\n     * @param {string} s - The offset string to parse\n     * @example FixedOffsetZone.parseSpecifier(\"UTC+6\")\n     * @example FixedOffsetZone.parseSpecifier(\"UTC+06\")\n     * @example FixedOffsetZone.parseSpecifier(\"UTC-6:00\")\n     * @return {FixedOffsetZone}\n     */\n    static parseSpecifier(s) {\n      if (s) {\n        const r = s.match(/^utc(?:([+-]\\d{1,2})(?::(\\d{2}))?)?$/i);\n        if (r) {\n          return new FixedOffsetZone(signedOffset(r[1], r[2]));\n        }\n      }\n      return null;\n    }\n\n    constructor(offset) {\n      super();\n      /** @private **/\n      this.fixed = offset;\n    }\n\n    /** @override **/\n    get type() {\n      return \"fixed\";\n    }\n\n    /** @override **/\n    get name() {\n      return this.fixed === 0 ? \"UTC\" : `UTC${formatOffset(this.fixed, \"narrow\")}`;\n    }\n\n    get ianaName() {\n      if (this.fixed === 0) {\n        return \"Etc/UTC\";\n      } else {\n        return `Etc/GMT${formatOffset(-this.fixed, \"narrow\")}`;\n      }\n    }\n\n    /** @override **/\n    offsetName() {\n      return this.name;\n    }\n\n    /** @override **/\n    formatOffset(ts, format) {\n      return formatOffset(this.fixed, format);\n    }\n\n    /** @override **/\n    get isUniversal() {\n      return true;\n    }\n\n    /** @override **/\n    offset() {\n      return this.fixed;\n    }\n\n    /** @override **/\n    equals(otherZone) {\n      return otherZone.type === \"fixed\" && otherZone.fixed === this.fixed;\n    }\n\n    /** @override **/\n    get isValid() {\n      return true;\n    }\n  }\n\n  /**\n   * A zone that failed to parse. You should never need to instantiate this.\n   * @implements {Zone}\n   */\n  class InvalidZone extends Zone {\n    constructor(zoneName) {\n      super();\n      /**  @private */\n      this.zoneName = zoneName;\n    }\n\n    /** @override **/\n    get type() {\n      return \"invalid\";\n    }\n\n    /** @override **/\n    get name() {\n      return this.zoneName;\n    }\n\n    /** @override **/\n    get isUniversal() {\n      return false;\n    }\n\n    /** @override **/\n    offsetName() {\n      return null;\n    }\n\n    /** @override **/\n    formatOffset() {\n      return \"\";\n    }\n\n    /** @override **/\n    offset() {\n      return NaN;\n    }\n\n    /** @override **/\n    equals() {\n      return false;\n    }\n\n    /** @override **/\n    get isValid() {\n      return false;\n    }\n  }\n\n  /**\n   * @private\n   */\n\n  function normalizeZone(input, defaultZone) {\n    if (isUndefined(input) || input === null) {\n      return defaultZone;\n    } else if (input instanceof Zone) {\n      return input;\n    } else if (isString(input)) {\n      const lowered = input.toLowerCase();\n      if (lowered === \"default\") return defaultZone;\n      else if (lowered === \"local\" || lowered === \"system\") return SystemZone.instance;\n      else if (lowered === \"utc\" || lowered === \"gmt\") return FixedOffsetZone.utcInstance;\n      else return FixedOffsetZone.parseSpecifier(lowered) || IANAZone.create(input);\n    } else if (isNumber(input)) {\n      return FixedOffsetZone.instance(input);\n    } else if (typeof input === \"object\" && \"offset\" in input && typeof input.offset === \"function\") {\n      // This is dumb, but the instanceof check above doesn't seem to really work\n      // so we're duck checking it\n      return input;\n    } else {\n      return new InvalidZone(input);\n    }\n  }\n\n  let now = () => Date.now(),\n    defaultZone = \"system\",\n    defaultLocale = null,\n    defaultNumberingSystem = null,\n    defaultOutputCalendar = null,\n    twoDigitCutoffYear = 60,\n    throwOnInvalid,\n    defaultWeekSettings = null;\n\n  /**\n   * Settings contains static getters and setters that control Luxon's overall behavior. Luxon is a simple library with few options, but the ones it does have live here.\n   */\n  class Settings {\n    /**\n     * Get the callback for returning the current timestamp.\n     * @type {function}\n     */\n    static get now() {\n      return now;\n    }\n\n    /**\n     * Set the callback for returning the current timestamp.\n     * The function should return a number, which will be interpreted as an Epoch millisecond count\n     * @type {function}\n     * @example Settings.now = () => Date.now() + 3000 // pretend it is 3 seconds in the future\n     * @example Settings.now = () => 0 // always pretend it's Jan 1, 1970 at midnight in UTC time\n     */\n    static set now(n) {\n      now = n;\n    }\n\n    /**\n     * Set the default time zone to create DateTimes in. Does not affect existing instances.\n     * Use the value \"system\" to reset this value to the system's time zone.\n     * @type {string}\n     */\n    static set defaultZone(zone) {\n      defaultZone = zone;\n    }\n\n    /**\n     * Get the default time zone object currently used to create DateTimes. Does not affect existing instances.\n     * The default value is the system's time zone (the one set on the machine that runs this code).\n     * @type {Zone}\n     */\n    static get defaultZone() {\n      return normalizeZone(defaultZone, SystemZone.instance);\n    }\n\n    /**\n     * Get the default locale to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static get defaultLocale() {\n      return defaultLocale;\n    }\n\n    /**\n     * Set the default locale to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static set defaultLocale(locale) {\n      defaultLocale = locale;\n    }\n\n    /**\n     * Get the default numbering system to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static get defaultNumberingSystem() {\n      return defaultNumberingSystem;\n    }\n\n    /**\n     * Set the default numbering system to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static set defaultNumberingSystem(numberingSystem) {\n      defaultNumberingSystem = numberingSystem;\n    }\n\n    /**\n     * Get the default output calendar to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static get defaultOutputCalendar() {\n      return defaultOutputCalendar;\n    }\n\n    /**\n     * Set the default output calendar to create DateTimes with. Does not affect existing instances.\n     * @type {string}\n     */\n    static set defaultOutputCalendar(outputCalendar) {\n      defaultOutputCalendar = outputCalendar;\n    }\n\n    /**\n     * @typedef {Object} WeekSettings\n     * @property {number} firstDay\n     * @property {number} minimalDays\n     * @property {number[]} weekend\n     */\n\n    /**\n     * @return {WeekSettings|null}\n     */\n    static get defaultWeekSettings() {\n      return defaultWeekSettings;\n    }\n\n    /**\n     * Allows overriding the default locale week settings, i.e. the start of the week, the weekend and\n     * how many days are required in the first week of a year.\n     * Does not affect existing instances.\n     *\n     * @param {WeekSettings|null} weekSettings\n     */\n    static set defaultWeekSettings(weekSettings) {\n      defaultWeekSettings = validateWeekSettings(weekSettings);\n    }\n\n    /**\n     * Get the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.\n     * @type {number}\n     */\n    static get twoDigitCutoffYear() {\n      return twoDigitCutoffYear;\n    }\n\n    /**\n     * Set the cutoff year after which a string encoding a year as two digits is interpreted to occur in the current century.\n     * @type {number}\n     * @example Settings.twoDigitCutoffYear = 0 // cut-off year is 0, so all 'yy' are interpreted as current century\n     * @example Settings.twoDigitCutoffYear = 50 // '49' -> 1949; '50' -> 2050\n     * @example Settings.twoDigitCutoffYear = 1950 // interpreted as 50\n     * @example Settings.twoDigitCutoffYear = 2050 // ALSO interpreted as 50\n     */\n    static set twoDigitCutoffYear(cutoffYear) {\n      twoDigitCutoffYear = cutoffYear % 100;\n    }\n\n    /**\n     * Get whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals\n     * @type {boolean}\n     */\n    static get throwOnInvalid() {\n      return throwOnInvalid;\n    }\n\n    /**\n     * Set whether Luxon will throw when it encounters invalid DateTimes, Durations, or Intervals\n     * @type {boolean}\n     */\n    static set throwOnInvalid(t) {\n      throwOnInvalid = t;\n    }\n\n    /**\n     * Reset Luxon's global caches. Should only be necessary in testing scenarios.\n     * @return {void}\n     */\n    static resetCaches() {\n      Locale.resetCache();\n      IANAZone.resetCache();\n    }\n  }\n\n  class Invalid {\n    constructor(reason, explanation) {\n      this.reason = reason;\n      this.explanation = explanation;\n    }\n\n    toMessage() {\n      if (this.explanation) {\n        return `${this.reason}: ${this.explanation}`;\n      } else {\n        return this.reason;\n      }\n    }\n  }\n\n  const nonLeapLadder = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],\n    leapLadder = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];\n\n  function unitOutOfRange(unit, value) {\n    return new Invalid(\n      \"unit out of range\",\n      `you specified ${value} (of type ${typeof value}) as a ${unit}, which is invalid`\n    );\n  }\n\n  function dayOfWeek(year, month, day) {\n    const d = new Date(Date.UTC(year, month - 1, day));\n\n    if (year < 100 && year >= 0) {\n      d.setUTCFullYear(d.getUTCFullYear() - 1900);\n    }\n\n    const js = d.getUTCDay();\n\n    return js === 0 ? 7 : js;\n  }\n\n  function computeOrdinal(year, month, day) {\n    return day + (isLeapYear(year) ? leapLadder : nonLeapLadder)[month - 1];\n  }\n\n  function uncomputeOrdinal(year, ordinal) {\n    const table = isLeapYear(year) ? leapLadder : nonLeapLadder,\n      month0 = table.findIndex((i) => i < ordinal),\n      day = ordinal - table[month0];\n    return { month: month0 + 1, day };\n  }\n\n  function isoWeekdayToLocal(isoWeekday, startOfWeek) {\n    return ((isoWeekday - startOfWeek + 7) % 7) + 1;\n  }\n\n  /**\n   * @private\n   */\n\n  function gregorianToWeek(gregObj, minDaysInFirstWeek = 4, startOfWeek = 1) {\n    const { year, month, day } = gregObj,\n      ordinal = computeOrdinal(year, month, day),\n      weekday = isoWeekdayToLocal(dayOfWeek(year, month, day), startOfWeek);\n\n    let weekNumber = Math.floor((ordinal - weekday + 14 - minDaysInFirstWeek) / 7),\n      weekYear;\n\n    if (weekNumber < 1) {\n      weekYear = year - 1;\n      weekNumber = weeksInWeekYear(weekYear, minDaysInFirstWeek, startOfWeek);\n    } else if (weekNumber > weeksInWeekYear(year, minDaysInFirstWeek, startOfWeek)) {\n      weekYear = year + 1;\n      weekNumber = 1;\n    } else {\n      weekYear = year;\n    }\n\n    return { weekYear, weekNumber, weekday, ...timeObject(gregObj) };\n  }\n\n  function weekToGregorian(weekData, minDaysInFirstWeek = 4, startOfWeek = 1) {\n    const { weekYear, weekNumber, weekday } = weekData,\n      weekdayOfJan4 = isoWeekdayToLocal(dayOfWeek(weekYear, 1, minDaysInFirstWeek), startOfWeek),\n      yearInDays = daysInYear(weekYear);\n\n    let ordinal = weekNumber * 7 + weekday - weekdayOfJan4 - 7 + minDaysInFirstWeek,\n      year;\n\n    if (ordinal < 1) {\n      year = weekYear - 1;\n      ordinal += daysInYear(year);\n    } else if (ordinal > yearInDays) {\n      year = weekYear + 1;\n      ordinal -= daysInYear(weekYear);\n    } else {\n      year = weekYear;\n    }\n\n    const { month, day } = uncomputeOrdinal(year, ordinal);\n    return { year, month, day, ...timeObject(weekData) };\n  }\n\n  function gregorianToOrdinal(gregData) {\n    const { year, month, day } = gregData;\n    const ordinal = computeOrdinal(year, month, day);\n    return { year, ordinal, ...timeObject(gregData) };\n  }\n\n  function ordinalToGregorian(ordinalData) {\n    const { year, ordinal } = ordinalData;\n    const { month, day } = uncomputeOrdinal(year, ordinal);\n    return { year, month, day, ...timeObject(ordinalData) };\n  }\n\n  /**\n   * Check if local week units like localWeekday are used in obj.\n   * If so, validates that they are not mixed with ISO week units and then copies them to the normal week unit properties.\n   * Modifies obj in-place!\n   * @param obj the object values\n   */\n  function usesLocalWeekValues(obj, loc) {\n    const hasLocaleWeekData =\n      !isUndefined(obj.localWeekday) ||\n      !isUndefined(obj.localWeekNumber) ||\n      !isUndefined(obj.localWeekYear);\n    if (hasLocaleWeekData) {\n      const hasIsoWeekData =\n        !isUndefined(obj.weekday) || !isUndefined(obj.weekNumber) || !isUndefined(obj.weekYear);\n\n      if (hasIsoWeekData) {\n        throw new ConflictingSpecificationError(\n          \"Cannot mix locale-based week fields with ISO-based week fields\"\n        );\n      }\n      if (!isUndefined(obj.localWeekday)) obj.weekday = obj.localWeekday;\n      if (!isUndefined(obj.localWeekNumber)) obj.weekNumber = obj.localWeekNumber;\n      if (!isUndefined(obj.localWeekYear)) obj.weekYear = obj.localWeekYear;\n      delete obj.localWeekday;\n      delete obj.localWeekNumber;\n      delete obj.localWeekYear;\n      return {\n        minDaysInFirstWeek: loc.getMinDaysInFirstWeek(),\n        startOfWeek: loc.getStartOfWeek(),\n      };\n    } else {\n      return { minDaysInFirstWeek: 4, startOfWeek: 1 };\n    }\n  }\n\n  function hasInvalidWeekData(obj, minDaysInFirstWeek = 4, startOfWeek = 1) {\n    const validYear = isInteger(obj.weekYear),\n      validWeek = integerBetween(\n        obj.weekNumber,\n        1,\n        weeksInWeekYear(obj.weekYear, minDaysInFirstWeek, startOfWeek)\n      ),\n      validWeekday = integerBetween(obj.weekday, 1, 7);\n\n    if (!validYear) {\n      return unitOutOfRange(\"weekYear\", obj.weekYear);\n    } else if (!validWeek) {\n      return unitOutOfRange(\"week\", obj.weekNumber);\n    } else if (!validWeekday) {\n      return unitOutOfRange(\"weekday\", obj.weekday);\n    } else return false;\n  }\n\n  function hasInvalidOrdinalData(obj) {\n    const validYear = isInteger(obj.year),\n      validOrdinal = integerBetween(obj.ordinal, 1, daysInYear(obj.year));\n\n    if (!validYear) {\n      return unitOutOfRange(\"year\", obj.year);\n    } else if (!validOrdinal) {\n      return unitOutOfRange(\"ordinal\", obj.ordinal);\n    } else return false;\n  }\n\n  function hasInvalidGregorianData(obj) {\n    const validYear = isInteger(obj.year),\n      validMonth = integerBetween(obj.month, 1, 12),\n      validDay = integerBetween(obj.day, 1, daysInMonth(obj.year, obj.month));\n\n    if (!validYear) {\n      return unitOutOfRange(\"year\", obj.year);\n    } else if (!validMonth) {\n      return unitOutOfRange(\"month\", obj.month);\n    } else if (!validDay) {\n      return unitOutOfRange(\"day\", obj.day);\n    } else return false;\n  }\n\n  function hasInvalidTimeData(obj) {\n    const { hour, minute, second, millisecond } = obj;\n    const validHour =\n        integerBetween(hour, 0, 23) ||\n        (hour === 24 && minute === 0 && second === 0 && millisecond === 0),\n      validMinute = integerBetween(minute, 0, 59),\n      validSecond = integerBetween(second, 0, 59),\n      validMillisecond = integerBetween(millisecond, 0, 999);\n\n    if (!validHour) {\n      return unitOutOfRange(\"hour\", hour);\n    } else if (!validMinute) {\n      return unitOutOfRange(\"minute\", minute);\n    } else if (!validSecond) {\n      return unitOutOfRange(\"second\", second);\n    } else if (!validMillisecond) {\n      return unitOutOfRange(\"millisecond\", millisecond);\n    } else return false;\n  }\n\n  /*\n    This is just a junk drawer, containing anything used across multiple classes.\n    Because Luxon is small(ish), this should stay small and we won't worry about splitting\n    it up into, say, parsingUtil.js and basicUtil.js and so on. But they are divided up by feature area.\n  */\n\n  /**\n   * @private\n   */\n\n  // TYPES\n\n  function isUndefined(o) {\n    return typeof o === \"undefined\";\n  }\n\n  function isNumber(o) {\n    return typeof o === \"number\";\n  }\n\n  function isInteger(o) {\n    return typeof o === \"number\" && o % 1 === 0;\n  }\n\n  function isString(o) {\n    return typeof o === \"string\";\n  }\n\n  function isDate(o) {\n    return Object.prototype.toString.call(o) === \"[object Date]\";\n  }\n\n  // CAPABILITIES\n\n  function hasRelative() {\n    try {\n      return typeof Intl !== \"undefined\" && !!Intl.RelativeTimeFormat;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  function hasLocaleWeekInfo() {\n    try {\n      return (\n        typeof Intl !== \"undefined\" &&\n        !!Intl.Locale &&\n        (\"weekInfo\" in Intl.Locale.prototype || \"getWeekInfo\" in Intl.Locale.prototype)\n      );\n    } catch (e) {\n      return false;\n    }\n  }\n\n  // OBJECTS AND ARRAYS\n\n  function maybeArray(thing) {\n    return Array.isArray(thing) ? thing : [thing];\n  }\n\n  function bestBy(arr, by, compare) {\n    if (arr.length === 0) {\n      return undefined;\n    }\n    return arr.reduce((best, next) => {\n      const pair = [by(next), next];\n      if (!best) {\n        return pair;\n      } else if (compare(best[0], pair[0]) === best[0]) {\n        return best;\n      } else {\n        return pair;\n      }\n    }, null)[1];\n  }\n\n  function pick(obj, keys) {\n    return keys.reduce((a, k) => {\n      a[k] = obj[k];\n      return a;\n    }, {});\n  }\n\n  function hasOwnProperty(obj, prop) {\n    return Object.prototype.hasOwnProperty.call(obj, prop);\n  }\n\n  function validateWeekSettings(settings) {\n    if (settings == null) {\n      return null;\n    } else if (typeof settings !== \"object\") {\n      throw new InvalidArgumentError(\"Week settings must be an object\");\n    } else {\n      if (\n        !integerBetween(settings.firstDay, 1, 7) ||\n        !integerBetween(settings.minimalDays, 1, 7) ||\n        !Array.isArray(settings.weekend) ||\n        settings.weekend.some((v) => !integerBetween(v, 1, 7))\n      ) {\n        throw new InvalidArgumentError(\"Invalid week settings\");\n      }\n      return {\n        firstDay: settings.firstDay,\n        minimalDays: settings.minimalDays,\n        weekend: Array.from(settings.weekend),\n      };\n    }\n  }\n\n  // NUMBERS AND STRINGS\n\n  function integerBetween(thing, bottom, top) {\n    return isInteger(thing) && thing >= bottom && thing <= top;\n  }\n\n  // x % n but takes the sign of n instead of x\n  function floorMod(x, n) {\n    return x - n * Math.floor(x / n);\n  }\n\n  function padStart(input, n = 2) {\n    const isNeg = input < 0;\n    let padded;\n    if (isNeg) {\n      padded = \"-\" + (\"\" + -input).padStart(n, \"0\");\n    } else {\n      padded = (\"\" + input).padStart(n, \"0\");\n    }\n    return padded;\n  }\n\n  function parseInteger(string) {\n    if (isUndefined(string) || string === null || string === \"\") {\n      return undefined;\n    } else {\n      return parseInt(string, 10);\n    }\n  }\n\n  function parseFloating(string) {\n    if (isUndefined(string) || string === null || string === \"\") {\n      return undefined;\n    } else {\n      return parseFloat(string);\n    }\n  }\n\n  function parseMillis(fraction) {\n    // Return undefined (instead of 0) in these cases, where fraction is not set\n    if (isUndefined(fraction) || fraction === null || fraction === \"\") {\n      return undefined;\n    } else {\n      const f = parseFloat(\"0.\" + fraction) * 1000;\n      return Math.floor(f);\n    }\n  }\n\n  function roundTo(number, digits, towardZero = false) {\n    const factor = 10 ** digits,\n      rounder = towardZero ? Math.trunc : Math.round;\n    return rounder(number * factor) / factor;\n  }\n\n  // DATE BASICS\n\n  function isLeapYear(year) {\n    return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n  }\n\n  function daysInYear(year) {\n    return isLeapYear(year) ? 366 : 365;\n  }\n\n  function daysInMonth(year, month) {\n    const modMonth = floorMod(month - 1, 12) + 1,\n      modYear = year + (month - modMonth) / 12;\n\n    if (modMonth === 2) {\n      return isLeapYear(modYear) ? 29 : 28;\n    } else {\n      return [31, null, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][modMonth - 1];\n    }\n  }\n\n  // convert a calendar object to a local timestamp (epoch, but with the offset baked in)\n  function objToLocalTS(obj) {\n    let d = Date.UTC(\n      obj.year,\n      obj.month - 1,\n      obj.day,\n      obj.hour,\n      obj.minute,\n      obj.second,\n      obj.millisecond\n    );\n\n    // for legacy reasons, years between 0 and 99 are interpreted as 19XX; revert that\n    if (obj.year < 100 && obj.year >= 0) {\n      d = new Date(d);\n      // set the month and day again, this is necessary because year 2000 is a leap year, but year 100 is not\n      // so if obj.year is in 99, but obj.day makes it roll over into year 100,\n      // the calculations done by Date.UTC are using year 2000 - which is incorrect\n      d.setUTCFullYear(obj.year, obj.month - 1, obj.day);\n    }\n    return +d;\n  }\n\n  // adapted from moment.js: https://github.com/moment/moment/blob/000ac1800e620f770f4eb31b5ae908f6167b0ab2/src/lib/units/week-calendar-utils.js\n  function firstWeekOffset(year, minDaysInFirstWeek, startOfWeek) {\n    const fwdlw = isoWeekdayToLocal(dayOfWeek(year, 1, minDaysInFirstWeek), startOfWeek);\n    return -fwdlw + minDaysInFirstWeek - 1;\n  }\n\n  function weeksInWeekYear(weekYear, minDaysInFirstWeek = 4, startOfWeek = 1) {\n    const weekOffset = firstWeekOffset(weekYear, minDaysInFirstWeek, startOfWeek);\n    const weekOffsetNext = firstWeekOffset(weekYear + 1, minDaysInFirstWeek, startOfWeek);\n    return (daysInYear(weekYear) - weekOffset + weekOffsetNext) / 7;\n  }\n\n  function untruncateYear(year) {\n    if (year > 99) {\n      return year;\n    } else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2000 + year;\n  }\n\n  // PARSING\n\n  function parseZoneInfo(ts, offsetFormat, locale, timeZone = null) {\n    const date = new Date(ts),\n      intlOpts = {\n        hourCycle: \"h23\",\n        year: \"numeric\",\n        month: \"2-digit\",\n        day: \"2-digit\",\n        hour: \"2-digit\",\n        minute: \"2-digit\",\n      };\n\n    if (timeZone) {\n      intlOpts.timeZone = timeZone;\n    }\n\n    const modified = { timeZoneName: offsetFormat, ...intlOpts };\n\n    const parsed = new Intl.DateTimeFormat(locale, modified)\n      .formatToParts(date)\n      .find((m) => m.type.toLowerCase() === \"timezonename\");\n    return parsed ? parsed.value : null;\n  }\n\n  // signedOffset('-5', '30') -> -330\n  function signedOffset(offHourStr, offMinuteStr) {\n    let offHour = parseInt(offHourStr, 10);\n\n    // don't || this because we want to preserve -0\n    if (Number.isNaN(offHour)) {\n      offHour = 0;\n    }\n\n    const offMin = parseInt(offMinuteStr, 10) || 0,\n      offMinSigned = offHour < 0 || Object.is(offHour, -0) ? -offMin : offMin;\n    return offHour * 60 + offMinSigned;\n  }\n\n  // COERCION\n\n  function asNumber(value) {\n    const numericValue = Number(value);\n    if (typeof value === \"boolean\" || value === \"\" || Number.isNaN(numericValue))\n      throw new InvalidArgumentError(`Invalid unit value ${value}`);\n    return numericValue;\n  }\n\n  function normalizeObject(obj, normalizer) {\n    const normalized = {};\n    for (const u in obj) {\n      if (hasOwnProperty(obj, u)) {\n        const v = obj[u];\n        if (v === undefined || v === null) continue;\n        normalized[normalizer(u)] = asNumber(v);\n      }\n    }\n    return normalized;\n  }\n\n  function formatOffset(offset, format) {\n    const hours = Math.trunc(Math.abs(offset / 60)),\n      minutes = Math.trunc(Math.abs(offset % 60)),\n      sign = offset >= 0 ? \"+\" : \"-\";\n\n    switch (format) {\n      case \"short\":\n        return `${sign}${padStart(hours, 2)}:${padStart(minutes, 2)}`;\n      case \"narrow\":\n        return `${sign}${hours}${minutes > 0 ? `:${minutes}` : \"\"}`;\n      case \"techie\":\n        return `${sign}${padStart(hours, 2)}${padStart(minutes, 2)}`;\n      default:\n        throw new RangeError(`Value format ${format} is out of range for property format`);\n    }\n  }\n\n  function timeObject(obj) {\n    return pick(obj, [\"hour\", \"minute\", \"second\", \"millisecond\"]);\n  }\n\n  /**\n   * @private\n   */\n\n  const monthsLong = [\n    \"January\",\n    \"February\",\n    \"March\",\n    \"April\",\n    \"May\",\n    \"June\",\n    \"July\",\n    \"August\",\n    \"September\",\n    \"October\",\n    \"November\",\n    \"December\",\n  ];\n\n  const monthsShort = [\n    \"Jan\",\n    \"Feb\",\n    \"Mar\",\n    \"Apr\",\n    \"May\",\n    \"Jun\",\n    \"Jul\",\n    \"Aug\",\n    \"Sep\",\n    \"Oct\",\n    \"Nov\",\n    \"Dec\",\n  ];\n\n  const monthsNarrow = [\"J\", \"F\", \"M\", \"A\", \"M\", \"J\", \"J\", \"A\", \"S\", \"O\", \"N\", \"D\"];\n\n  function months(length) {\n    switch (length) {\n      case \"narrow\":\n        return [...monthsNarrow];\n      case \"short\":\n        return [...monthsShort];\n      case \"long\":\n        return [...monthsLong];\n      case \"numeric\":\n        return [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\"];\n      case \"2-digit\":\n        return [\"01\", \"02\", \"03\", \"04\", \"05\", \"06\", \"07\", \"08\", \"09\", \"10\", \"11\", \"12\"];\n      default:\n        return null;\n    }\n  }\n\n  const weekdaysLong = [\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n    \"Sunday\",\n  ];\n\n  const weekdaysShort = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"];\n\n  const weekdaysNarrow = [\"M\", \"T\", \"W\", \"T\", \"F\", \"S\", \"S\"];\n\n  function weekdays(length) {\n    switch (length) {\n      case \"narrow\":\n        return [...weekdaysNarrow];\n      case \"short\":\n        return [...weekdaysShort];\n      case \"long\":\n        return [...weekdaysLong];\n      case \"numeric\":\n        return [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\"];\n      default:\n        return null;\n    }\n  }\n\n  const meridiems = [\"AM\", \"PM\"];\n\n  const erasLong = [\"Before Christ\", \"Anno Domini\"];\n\n  const erasShort = [\"BC\", \"AD\"];\n\n  const erasNarrow = [\"B\", \"A\"];\n\n  function eras(length) {\n    switch (length) {\n      case \"narrow\":\n        return [...erasNarrow];\n      case \"short\":\n        return [...erasShort];\n      case \"long\":\n        return [...erasLong];\n      default:\n        return null;\n    }\n  }\n\n  function meridiemForDateTime(dt) {\n    return meridiems[dt.hour < 12 ? 0 : 1];\n  }\n\n  function weekdayForDateTime(dt, length) {\n    return weekdays(length)[dt.weekday - 1];\n  }\n\n  function monthForDateTime(dt, length) {\n    return months(length)[dt.month - 1];\n  }\n\n  function eraForDateTime(dt, length) {\n    return eras(length)[dt.year < 0 ? 0 : 1];\n  }\n\n  function formatRelativeTime(unit, count, numeric = \"always\", narrow = false) {\n    const units = {\n      years: [\"year\", \"yr.\"],\n      quarters: [\"quarter\", \"qtr.\"],\n      months: [\"month\", \"mo.\"],\n      weeks: [\"week\", \"wk.\"],\n      days: [\"day\", \"day\", \"days\"],\n      hours: [\"hour\", \"hr.\"],\n      minutes: [\"minute\", \"min.\"],\n      seconds: [\"second\", \"sec.\"],\n    };\n\n    const lastable = [\"hours\", \"minutes\", \"seconds\"].indexOf(unit) === -1;\n\n    if (numeric === \"auto\" && lastable) {\n      const isDay = unit === \"days\";\n      switch (count) {\n        case 1:\n          return isDay ? \"tomorrow\" : `next ${units[unit][0]}`;\n        case -1:\n          return isDay ? \"yesterday\" : `last ${units[unit][0]}`;\n        case 0:\n          return isDay ? \"today\" : `this ${units[unit][0]}`;\n      }\n    }\n\n    const isInPast = Object.is(count, -0) || count < 0,\n      fmtValue = Math.abs(count),\n      singular = fmtValue === 1,\n      lilUnits = units[unit],\n      fmtUnit = narrow\n        ? singular\n          ? lilUnits[1]\n          : lilUnits[2] || lilUnits[1]\n        : singular\n        ? units[unit][0]\n        : unit;\n    return isInPast ? `${fmtValue} ${fmtUnit} ago` : `in ${fmtValue} ${fmtUnit}`;\n  }\n\n  function stringifyTokens(splits, tokenToString) {\n    let s = \"\";\n    for (const token of splits) {\n      if (token.literal) {\n        s += token.val;\n      } else {\n        s += tokenToString(token.val);\n      }\n    }\n    return s;\n  }\n\n  const macroTokenToFormatOpts = {\n    D: DATE_SHORT,\n    DD: DATE_MED,\n    DDD: DATE_FULL,\n    DDDD: DATE_HUGE,\n    t: TIME_SIMPLE,\n    tt: TIME_WITH_SECONDS,\n    ttt: TIME_WITH_SHORT_OFFSET,\n    tttt: TIME_WITH_LONG_OFFSET,\n    T: TIME_24_SIMPLE,\n    TT: TIME_24_WITH_SECONDS,\n    TTT: TIME_24_WITH_SHORT_OFFSET,\n    TTTT: TIME_24_WITH_LONG_OFFSET,\n    f: DATETIME_SHORT,\n    ff: DATETIME_MED,\n    fff: DATETIME_FULL,\n    ffff: DATETIME_HUGE,\n    F: DATETIME_SHORT_WITH_SECONDS,\n    FF: DATETIME_MED_WITH_SECONDS,\n    FFF: DATETIME_FULL_WITH_SECONDS,\n    FFFF: DATETIME_HUGE_WITH_SECONDS,\n  };\n\n  /**\n   * @private\n   */\n\n  class Formatter {\n    static create(locale, opts = {}) {\n      return new Formatter(locale, opts);\n    }\n\n    static parseFormat(fmt) {\n      // white-space is always considered a literal in user-provided formats\n      // the \" \" token has a special meaning (see unitForToken)\n\n      let current = null,\n        currentFull = \"\",\n        bracketed = false;\n      const splits = [];\n      for (let i = 0; i < fmt.length; i++) {\n        const c = fmt.charAt(i);\n        if (c === \"'\") {\n          if (currentFull.length > 0) {\n            splits.push({ literal: bracketed || /^\\s+$/.test(currentFull), val: currentFull });\n          }\n          current = null;\n          currentFull = \"\";\n          bracketed = !bracketed;\n        } else if (bracketed) {\n          currentFull += c;\n        } else if (c === current) {\n          currentFull += c;\n        } else {\n          if (currentFull.length > 0) {\n            splits.push({ literal: /^\\s+$/.test(currentFull), val: currentFull });\n          }\n          currentFull = c;\n          current = c;\n        }\n      }\n\n      if (currentFull.length > 0) {\n        splits.push({ literal: bracketed || /^\\s+$/.test(currentFull), val: currentFull });\n      }\n\n      return splits;\n    }\n\n    static macroTokenToFormatOpts(token) {\n      return macroTokenToFormatOpts[token];\n    }\n\n    constructor(locale, formatOpts) {\n      this.opts = formatOpts;\n      this.loc = locale;\n      this.systemLoc = null;\n    }\n\n    formatWithSystemDefault(dt, opts) {\n      if (this.systemLoc === null) {\n        this.systemLoc = this.loc.redefaultToSystem();\n      }\n      const df = this.systemLoc.dtFormatter(dt, { ...this.opts, ...opts });\n      return df.format();\n    }\n\n    dtFormatter(dt, opts = {}) {\n      return this.loc.dtFormatter(dt, { ...this.opts, ...opts });\n    }\n\n    formatDateTime(dt, opts) {\n      return this.dtFormatter(dt, opts).format();\n    }\n\n    formatDateTimeParts(dt, opts) {\n      return this.dtFormatter(dt, opts).formatToParts();\n    }\n\n    formatInterval(interval, opts) {\n      const df = this.dtFormatter(interval.start, opts);\n      return df.dtf.formatRange(interval.start.toJSDate(), interval.end.toJSDate());\n    }\n\n    resolvedOptions(dt, opts) {\n      return this.dtFormatter(dt, opts).resolvedOptions();\n    }\n\n    num(n, p = 0) {\n      // we get some perf out of doing this here, annoyingly\n      if (this.opts.forceSimple) {\n        return padStart(n, p);\n      }\n\n      const opts = { ...this.opts };\n\n      if (p > 0) {\n        opts.padTo = p;\n      }\n\n      return this.loc.numberFormatter(opts).format(n);\n    }\n\n    formatDateTimeFromString(dt, fmt) {\n      const knownEnglish = this.loc.listingMode() === \"en\",\n        useDateTimeFormatter = this.loc.outputCalendar && this.loc.outputCalendar !== \"gregory\",\n        string = (opts, extract) => this.loc.extract(dt, opts, extract),\n        formatOffset = (opts) => {\n          if (dt.isOffsetFixed && dt.offset === 0 && opts.allowZ) {\n            return \"Z\";\n          }\n\n          return dt.isValid ? dt.zone.formatOffset(dt.ts, opts.format) : \"\";\n        },\n        meridiem = () =>\n          knownEnglish\n            ? meridiemForDateTime(dt)\n            : string({ hour: \"numeric\", hourCycle: \"h12\" }, \"dayperiod\"),\n        month = (length, standalone) =>\n          knownEnglish\n            ? monthForDateTime(dt, length)\n            : string(standalone ? { month: length } : { month: length, day: \"numeric\" }, \"month\"),\n        weekday = (length, standalone) =>\n          knownEnglish\n            ? weekdayForDateTime(dt, length)\n            : string(\n                standalone ? { weekday: length } : { weekday: length, month: \"long\", day: \"numeric\" },\n                \"weekday\"\n              ),\n        maybeMacro = (token) => {\n          const formatOpts = Formatter.macroTokenToFormatOpts(token);\n          if (formatOpts) {\n            return this.formatWithSystemDefault(dt, formatOpts);\n          } else {\n            return token;\n          }\n        },\n        era = (length) =>\n          knownEnglish ? eraForDateTime(dt, length) : string({ era: length }, \"era\"),\n        tokenToString = (token) => {\n          // Where possible: https://cldr.unicode.org/translation/date-time/date-time-symbols\n          switch (token) {\n            // ms\n            case \"S\":\n              return this.num(dt.millisecond);\n            case \"u\":\n            // falls through\n            case \"SSS\":\n              return this.num(dt.millisecond, 3);\n            // seconds\n            case \"s\":\n              return this.num(dt.second);\n            case \"ss\":\n              return this.num(dt.second, 2);\n            // fractional seconds\n            case \"uu\":\n              return this.num(Math.floor(dt.millisecond / 10), 2);\n            case \"uuu\":\n              return this.num(Math.floor(dt.millisecond / 100));\n            // minutes\n            case \"m\":\n              return this.num(dt.minute);\n            case \"mm\":\n              return this.num(dt.minute, 2);\n            // hours\n            case \"h\":\n              return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12);\n            case \"hh\":\n              return this.num(dt.hour % 12 === 0 ? 12 : dt.hour % 12, 2);\n            case \"H\":\n              return this.num(dt.hour);\n            case \"HH\":\n              return this.num(dt.hour, 2);\n            // offset\n            case \"Z\":\n              // like +6\n              return formatOffset({ format: \"narrow\", allowZ: this.opts.allowZ });\n            case \"ZZ\":\n              // like +06:00\n              return formatOffset({ format: \"short\", allowZ: this.opts.allowZ });\n            case \"ZZZ\":\n              // like +0600\n              return formatOffset({ format: \"techie\", allowZ: this.opts.allowZ });\n            case \"ZZZZ\":\n              // like EST\n              return dt.zone.offsetName(dt.ts, { format: \"short\", locale: this.loc.locale });\n            case \"ZZZZZ\":\n              // like Eastern Standard Time\n              return dt.zone.offsetName(dt.ts, { format: \"long\", locale: this.loc.locale });\n            // zone\n            case \"z\":\n              // like America/New_York\n              return dt.zoneName;\n            // meridiems\n            case \"a\":\n              return meridiem();\n            // dates\n            case \"d\":\n              return useDateTimeFormatter ? string({ day: \"numeric\" }, \"day\") : this.num(dt.day);\n            case \"dd\":\n              return useDateTimeFormatter ? string({ day: \"2-digit\" }, \"day\") : this.num(dt.day, 2);\n            // weekdays - standalone\n            case \"c\":\n              // like 1\n              return this.num(dt.weekday);\n            case \"ccc\":\n              // like 'Tues'\n              return weekday(\"short\", true);\n            case \"cccc\":\n              // like 'Tuesday'\n              return weekday(\"long\", true);\n            case \"ccccc\":\n              // like 'T'\n              return weekday(\"narrow\", true);\n            // weekdays - format\n            case \"E\":\n              // like 1\n              return this.num(dt.weekday);\n            case \"EEE\":\n              // like 'Tues'\n              return weekday(\"short\", false);\n            case \"EEEE\":\n              // like 'Tuesday'\n              return weekday(\"long\", false);\n            case \"EEEEE\":\n              // like 'T'\n              return weekday(\"narrow\", false);\n            // months - standalone\n            case \"L\":\n              // like 1\n              return useDateTimeFormatter\n                ? string({ month: \"numeric\", day: \"numeric\" }, \"month\")\n                : this.num(dt.month);\n            case \"LL\":\n              // like 01, doesn't seem to work\n              return useDateTimeFormatter\n                ? string({ month: \"2-digit\", day: \"numeric\" }, \"month\")\n                : this.num(dt.month, 2);\n            case \"LLL\":\n              // like Jan\n              return month(\"short\", true);\n            case \"LLLL\":\n              // like January\n              return month(\"long\", true);\n            case \"LLLLL\":\n              // like J\n              return month(\"narrow\", true);\n            // months - format\n            case \"M\":\n              // like 1\n              return useDateTimeFormatter\n                ? string({ month: \"numeric\" }, \"month\")\n                : this.num(dt.month);\n            case \"MM\":\n              // like 01\n              return useDateTimeFormatter\n                ? string({ month: \"2-digit\" }, \"month\")\n                : this.num(dt.month, 2);\n            case \"MMM\":\n              // like Jan\n              return month(\"short\", false);\n            case \"MMMM\":\n              // like January\n              return month(\"long\", false);\n            case \"MMMMM\":\n              // like J\n              return month(\"narrow\", false);\n            // years\n            case \"y\":\n              // like 2014\n              return useDateTimeFormatter ? string({ year: \"numeric\" }, \"year\") : this.num(dt.year);\n            case \"yy\":\n              // like 14\n              return useDateTimeFormatter\n                ? string({ year: \"2-digit\" }, \"year\")\n                : this.num(dt.year.toString().slice(-2), 2);\n            case \"yyyy\":\n              // like 0012\n              return useDateTimeFormatter\n                ? string({ year: \"numeric\" }, \"year\")\n                : this.num(dt.year, 4);\n            case \"yyyyyy\":\n              // like 000012\n              return useDateTimeFormatter\n                ? string({ year: \"numeric\" }, \"year\")\n                : this.num(dt.year, 6);\n            // eras\n            case \"G\":\n              // like AD\n              return era(\"short\");\n            case \"GG\":\n              // like Anno Domini\n              return era(\"long\");\n            case \"GGGGG\":\n              return era(\"narrow\");\n            case \"kk\":\n              return this.num(dt.weekYear.toString().slice(-2), 2);\n            case \"kkkk\":\n              return this.num(dt.weekYear, 4);\n            case \"W\":\n              return this.num(dt.weekNumber);\n            case \"WW\":\n              return this.num(dt.weekNumber, 2);\n            case \"n\":\n              return this.num(dt.localWeekNumber);\n            case \"nn\":\n              return this.num(dt.localWeekNumber, 2);\n            case \"ii\":\n              return this.num(dt.localWeekYear.toString().slice(-2), 2);\n            case \"iiii\":\n              return this.num(dt.localWeekYear, 4);\n            case \"o\":\n              return this.num(dt.ordinal);\n            case \"ooo\":\n              return this.num(dt.ordinal, 3);\n            case \"q\":\n              // like 1\n              return this.num(dt.quarter);\n            case \"qq\":\n              // like 01\n              return this.num(dt.quarter, 2);\n            case \"X\":\n              return this.num(Math.floor(dt.ts / 1000));\n            case \"x\":\n              return this.num(dt.ts);\n            default:\n              return maybeMacro(token);\n          }\n        };\n\n      return stringifyTokens(Formatter.parseFormat(fmt), tokenToString);\n    }\n\n    formatDurationFromString(dur, fmt) {\n      const tokenToField = (token) => {\n          switch (token[0]) {\n            case \"S\":\n              return \"millisecond\";\n            case \"s\":\n              return \"second\";\n            case \"m\":\n              return \"minute\";\n            case \"h\":\n              return \"hour\";\n            case \"d\":\n              return \"day\";\n            case \"w\":\n              return \"week\";\n            case \"M\":\n              return \"month\";\n            case \"y\":\n              return \"year\";\n            default:\n              return null;\n          }\n        },\n        tokenToString = (lildur) => (token) => {\n          const mapped = tokenToField(token);\n          if (mapped) {\n            return this.num(lildur.get(mapped), token.length);\n          } else {\n            return token;\n          }\n        },\n        tokens = Formatter.parseFormat(fmt),\n        realTokens = tokens.reduce(\n          (found, { literal, val }) => (literal ? found : found.concat(val)),\n          []\n        ),\n        collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t));\n      return stringifyTokens(tokens, tokenToString(collapsed));\n    }\n  }\n\n  /*\n   * This file handles parsing for well-specified formats. Here's how it works:\n   * Two things go into parsing: a regex to match with and an extractor to take apart the groups in the match.\n   * An extractor is just a function that takes a regex match array and returns a { year: ..., month: ... } object\n   * parse() does the work of executing the regex and applying the extractor. It takes multiple regex/extractor pairs to try in sequence.\n   * Extractors can take a \"cursor\" representing the offset in the match to look at. This makes it easy to combine extractors.\n   * combineExtractors() does the work of combining them, keeping track of the cursor through multiple extractions.\n   * Some extractions are super dumb and simpleParse and fromStrings help DRY them.\n   */\n\n  const ianaRegex = /[A-Za-z_+-]{1,256}(?::?\\/[A-Za-z0-9_+-]{1,256}(?:\\/[A-Za-z0-9_+-]{1,256})?)?/;\n\n  function combineRegexes(...regexes) {\n    const full = regexes.reduce((f, r) => f + r.source, \"\");\n    return RegExp(`^${full}$`);\n  }\n\n  function combineExtractors(...extractors) {\n    return (m) =>\n      extractors\n        .reduce(\n          ([mergedVals, mergedZone, cursor], ex) => {\n            const [val, zone, next] = ex(m, cursor);\n            return [{ ...mergedVals, ...val }, zone || mergedZone, next];\n          },\n          [{}, null, 1]\n        )\n        .slice(0, 2);\n  }\n\n  function parse(s, ...patterns) {\n    if (s == null) {\n      return [null, null];\n    }\n\n    for (const [regex, extractor] of patterns) {\n      const m = regex.exec(s);\n      if (m) {\n        return extractor(m);\n      }\n    }\n    return [null, null];\n  }\n\n  function simpleParse(...keys) {\n    return (match, cursor) => {\n      const ret = {};\n      let i;\n\n      for (i = 0; i < keys.length; i++) {\n        ret[keys[i]] = parseInteger(match[cursor + i]);\n      }\n      return [ret, null, cursor + i];\n    };\n  }\n\n  // ISO and SQL parsing\n  const offsetRegex = /(?:(Z)|([+-]\\d\\d)(?::?(\\d\\d))?)/;\n  const isoExtendedZone = `(?:${offsetRegex.source}?(?:\\\\[(${ianaRegex.source})\\\\])?)?`;\n  const isoTimeBaseRegex = /(\\d\\d)(?::?(\\d\\d)(?::?(\\d\\d)(?:[.,](\\d{1,30}))?)?)?/;\n  const isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`);\n  const isoTimeExtensionRegex = RegExp(`(?:T${isoTimeRegex.source})?`);\n  const isoYmdRegex = /([+-]\\d{6}|\\d{4})(?:-?(\\d\\d)(?:-?(\\d\\d))?)?/;\n  const isoWeekRegex = /(\\d{4})-?W(\\d\\d)(?:-?(\\d))?/;\n  const isoOrdinalRegex = /(\\d{4})-?(\\d{3})/;\n  const extractISOWeekData = simpleParse(\"weekYear\", \"weekNumber\", \"weekDay\");\n  const extractISOOrdinalData = simpleParse(\"year\", \"ordinal\");\n  const sqlYmdRegex = /(\\d{4})-(\\d\\d)-(\\d\\d)/; // dumbed-down version of the ISO one\n  const sqlTimeRegex = RegExp(\n    `${isoTimeBaseRegex.source} ?(?:${offsetRegex.source}|(${ianaRegex.source}))?`\n  );\n  const sqlTimeExtensionRegex = RegExp(`(?: ${sqlTimeRegex.source})?`);\n\n  function int(match, pos, fallback) {\n    const m = match[pos];\n    return isUndefined(m) ? fallback : parseInteger(m);\n  }\n\n  function extractISOYmd(match, cursor) {\n    const item = {\n      year: int(match, cursor),\n      month: int(match, cursor + 1, 1),\n      day: int(match, cursor + 2, 1),\n    };\n\n    return [item, null, cursor + 3];\n  }\n\n  function extractISOTime(match, cursor) {\n    const item = {\n      hours: int(match, cursor, 0),\n      minutes: int(match, cursor + 1, 0),\n      seconds: int(match, cursor + 2, 0),\n      milliseconds: parseMillis(match[cursor + 3]),\n    };\n\n    return [item, null, cursor + 4];\n  }\n\n  function extractISOOffset(match, cursor) {\n    const local = !match[cursor] && !match[cursor + 1],\n      fullOffset = signedOffset(match[cursor + 1], match[cursor + 2]),\n      zone = local ? null : FixedOffsetZone.instance(fullOffset);\n    return [{}, zone, cursor + 3];\n  }\n\n  function extractIANAZone(match, cursor) {\n    const zone = match[cursor] ? IANAZone.create(match[cursor]) : null;\n    return [{}, zone, cursor + 1];\n  }\n\n  // ISO time parsing\n\n  const isoTimeOnly = RegExp(`^T?${isoTimeBaseRegex.source}$`);\n\n  // ISO duration parsing\n\n  const isoDuration =\n    /^-?P(?:(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)Y)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)M)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)W)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)D)?(?:T(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)H)?(?:(-?\\d{1,20}(?:\\.\\d{1,20})?)M)?(?:(-?\\d{1,20})(?:[.,](-?\\d{1,20}))?S)?)?)$/;\n\n  function extractISODuration(match) {\n    const [s, yearStr, monthStr, weekStr, dayStr, hourStr, minuteStr, secondStr, millisecondsStr] =\n      match;\n\n    const hasNegativePrefix = s[0] === \"-\";\n    const negativeSeconds = secondStr && secondStr[0] === \"-\";\n\n    const maybeNegate = (num, force = false) =>\n      num !== undefined && (force || (num && hasNegativePrefix)) ? -num : num;\n\n    return [\n      {\n        years: maybeNegate(parseFloating(yearStr)),\n        months: maybeNegate(parseFloating(monthStr)),\n        weeks: maybeNegate(parseFloating(weekStr)),\n        days: maybeNegate(parseFloating(dayStr)),\n        hours: maybeNegate(parseFloating(hourStr)),\n        minutes: maybeNegate(parseFloating(minuteStr)),\n        seconds: maybeNegate(parseFloating(secondStr), secondStr === \"-0\"),\n        milliseconds: maybeNegate(parseMillis(millisecondsStr), negativeSeconds),\n      },\n    ];\n  }\n\n  // These are a little braindead. EDT *should* tell us that we're in, say, America/New_York\n  // and not just that we're in -240 *right now*. But since I don't think these are used that often\n  // I'm just going to ignore that\n  const obsOffsets = {\n    GMT: 0,\n    EDT: -4 * 60,\n    EST: -5 * 60,\n    CDT: -5 * 60,\n    CST: -6 * 60,\n    MDT: -6 * 60,\n    MST: -7 * 60,\n    PDT: -7 * 60,\n    PST: -8 * 60,\n  };\n\n  function fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {\n    const result = {\n      year: yearStr.length === 2 ? untruncateYear(parseInteger(yearStr)) : parseInteger(yearStr),\n      month: monthsShort.indexOf(monthStr) + 1,\n      day: parseInteger(dayStr),\n      hour: parseInteger(hourStr),\n      minute: parseInteger(minuteStr),\n    };\n\n    if (secondStr) result.second = parseInteger(secondStr);\n    if (weekdayStr) {\n      result.weekday =\n        weekdayStr.length > 3\n          ? weekdaysLong.indexOf(weekdayStr) + 1\n          : weekdaysShort.indexOf(weekdayStr) + 1;\n    }\n\n    return result;\n  }\n\n  // RFC 2822/5322\n  const rfc2822 =\n    /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s)?(\\d{1,2})\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(\\d{2,4})\\s(\\d\\d):(\\d\\d)(?::(\\d\\d))?\\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\\d\\d)(\\d\\d)))$/;\n\n  function extractRFC2822(match) {\n    const [\n        ,\n        weekdayStr,\n        dayStr,\n        monthStr,\n        yearStr,\n        hourStr,\n        minuteStr,\n        secondStr,\n        obsOffset,\n        milOffset,\n        offHourStr,\n        offMinuteStr,\n      ] = match,\n      result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n\n    let offset;\n    if (obsOffset) {\n      offset = obsOffsets[obsOffset];\n    } else if (milOffset) {\n      offset = 0;\n    } else {\n      offset = signedOffset(offHourStr, offMinuteStr);\n    }\n\n    return [result, new FixedOffsetZone(offset)];\n  }\n\n  function preprocessRFC2822(s) {\n    // Remove comments and folding whitespace and replace multiple-spaces with a single space\n    return s\n      .replace(/\\([^()]*\\)|[\\n\\t]/g, \" \")\n      .replace(/(\\s\\s+)/g, \" \")\n      .trim();\n  }\n\n  // http date\n\n  const rfc1123 =\n      /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\\d\\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\\d{4}) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,\n    rfc850 =\n      /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\\d\\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) GMT$/,\n    ascii =\n      /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \\d|\\d\\d) (\\d\\d):(\\d\\d):(\\d\\d) (\\d{4})$/;\n\n  function extractRFC1123Or850(match) {\n    const [, weekdayStr, dayStr, monthStr, yearStr, hourStr, minuteStr, secondStr] = match,\n      result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n    return [result, FixedOffsetZone.utcInstance];\n  }\n\n  function extractASCII(match) {\n    const [, weekdayStr, monthStr, dayStr, hourStr, minuteStr, secondStr, yearStr] = match,\n      result = fromStrings(weekdayStr, yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr);\n    return [result, FixedOffsetZone.utcInstance];\n  }\n\n  const isoYmdWithTimeExtensionRegex = combineRegexes(isoYmdRegex, isoTimeExtensionRegex);\n  const isoWeekWithTimeExtensionRegex = combineRegexes(isoWeekRegex, isoTimeExtensionRegex);\n  const isoOrdinalWithTimeExtensionRegex = combineRegexes(isoOrdinalRegex, isoTimeExtensionRegex);\n  const isoTimeCombinedRegex = combineRegexes(isoTimeRegex);\n\n  const extractISOYmdTimeAndOffset = combineExtractors(\n    extractISOYmd,\n    extractISOTime,\n    extractISOOffset,\n    extractIANAZone\n  );\n  const extractISOWeekTimeAndOffset = combineExtractors(\n    extractISOWeekData,\n    extractISOTime,\n    extractISOOffset,\n    extractIANAZone\n  );\n  const extractISOOrdinalDateAndTime = combineExtractors(\n    extractISOOrdinalData,\n    extractISOTime,\n    extractISOOffset,\n    extractIANAZone\n  );\n  const extractISOTimeAndOffset = combineExtractors(\n    extractISOTime,\n    extractISOOffset,\n    extractIANAZone\n  );\n\n  /*\n   * @private\n   */\n\n  function parseISODate(s) {\n    return parse(\n      s,\n      [isoYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],\n      [isoWeekWithTimeExtensionRegex, extractISOWeekTimeAndOffset],\n      [isoOrdinalWithTimeExtensionRegex, extractISOOrdinalDateAndTime],\n      [isoTimeCombinedRegex, extractISOTimeAndOffset]\n    );\n  }\n\n  function parseRFC2822Date(s) {\n    return parse(preprocessRFC2822(s), [rfc2822, extractRFC2822]);\n  }\n\n  function parseHTTPDate(s) {\n    return parse(\n      s,\n      [rfc1123, extractRFC1123Or850],\n      [rfc850, extractRFC1123Or850],\n      [ascii, extractASCII]\n    );\n  }\n\n  function parseISODuration(s) {\n    return parse(s, [isoDuration, extractISODuration]);\n  }\n\n  const extractISOTimeOnly = combineExtractors(extractISOTime);\n\n  function parseISOTimeOnly(s) {\n    return parse(s, [isoTimeOnly, extractISOTimeOnly]);\n  }\n\n  const sqlYmdWithTimeExtensionRegex = combineRegexes(sqlYmdRegex, sqlTimeExtensionRegex);\n  const sqlTimeCombinedRegex = combineRegexes(sqlTimeRegex);\n\n  const extractISOTimeOffsetAndIANAZone = combineExtractors(\n    extractISOTime,\n    extractISOOffset,\n    extractIANAZone\n  );\n\n  function parseSQL(s) {\n    return parse(\n      s,\n      [sqlYmdWithTimeExtensionRegex, extractISOYmdTimeAndOffset],\n      [sqlTimeCombinedRegex, extractISOTimeOffsetAndIANAZone]\n    );\n  }\n\n  const INVALID$2 = \"Invalid Duration\";\n\n  // unit conversion constants\n  const lowOrderMatrix = {\n      weeks: {\n        days: 7,\n        hours: 7 * 24,\n        minutes: 7 * 24 * 60,\n        seconds: 7 * 24 * 60 * 60,\n        milliseconds: 7 * 24 * 60 * 60 * 1000,\n      },\n      days: {\n        hours: 24,\n        minutes: 24 * 60,\n        seconds: 24 * 60 * 60,\n        milliseconds: 24 * 60 * 60 * 1000,\n      },\n      hours: { minutes: 60, seconds: 60 * 60, milliseconds: 60 * 60 * 1000 },\n      minutes: { seconds: 60, milliseconds: 60 * 1000 },\n      seconds: { milliseconds: 1000 },\n    },\n    casualMatrix = {\n      years: {\n        quarters: 4,\n        months: 12,\n        weeks: 52,\n        days: 365,\n        hours: 365 * 24,\n        minutes: 365 * 24 * 60,\n        seconds: 365 * 24 * 60 * 60,\n        milliseconds: 365 * 24 * 60 * 60 * 1000,\n      },\n      quarters: {\n        months: 3,\n        weeks: 13,\n        days: 91,\n        hours: 91 * 24,\n        minutes: 91 * 24 * 60,\n        seconds: 91 * 24 * 60 * 60,\n        milliseconds: 91 * 24 * 60 * 60 * 1000,\n      },\n      months: {\n        weeks: 4,\n        days: 30,\n        hours: 30 * 24,\n        minutes: 30 * 24 * 60,\n        seconds: 30 * 24 * 60 * 60,\n        milliseconds: 30 * 24 * 60 * 60 * 1000,\n      },\n\n      ...lowOrderMatrix,\n    },\n    daysInYearAccurate = 146097.0 / 400,\n    daysInMonthAccurate = 146097.0 / 4800,\n    accurateMatrix = {\n      years: {\n        quarters: 4,\n        months: 12,\n        weeks: daysInYearAccurate / 7,\n        days: daysInYearAccurate,\n        hours: daysInYearAccurate * 24,\n        minutes: daysInYearAccurate * 24 * 60,\n        seconds: daysInYearAccurate * 24 * 60 * 60,\n        milliseconds: daysInYearAccurate * 24 * 60 * 60 * 1000,\n      },\n      quarters: {\n        months: 3,\n        weeks: daysInYearAccurate / 28,\n        days: daysInYearAccurate / 4,\n        hours: (daysInYearAccurate * 24) / 4,\n        minutes: (daysInYearAccurate * 24 * 60) / 4,\n        seconds: (daysInYearAccurate * 24 * 60 * 60) / 4,\n        milliseconds: (daysInYearAccurate * 24 * 60 * 60 * 1000) / 4,\n      },\n      months: {\n        weeks: daysInMonthAccurate / 7,\n        days: daysInMonthAccurate,\n        hours: daysInMonthAccurate * 24,\n        minutes: daysInMonthAccurate * 24 * 60,\n        seconds: daysInMonthAccurate * 24 * 60 * 60,\n        milliseconds: daysInMonthAccurate * 24 * 60 * 60 * 1000,\n      },\n      ...lowOrderMatrix,\n    };\n\n  // units ordered by size\n  const orderedUnits$1 = [\n    \"years\",\n    \"quarters\",\n    \"months\",\n    \"weeks\",\n    \"days\",\n    \"hours\",\n    \"minutes\",\n    \"seconds\",\n    \"milliseconds\",\n  ];\n\n  const reverseUnits = orderedUnits$1.slice(0).reverse();\n\n  // clone really means \"create another instance just like this one, but with these changes\"\n  function clone$1(dur, alts, clear = false) {\n    // deep merge for vals\n    const conf = {\n      values: clear ? alts.values : { ...dur.values, ...(alts.values || {}) },\n      loc: dur.loc.clone(alts.loc),\n      conversionAccuracy: alts.conversionAccuracy || dur.conversionAccuracy,\n      matrix: alts.matrix || dur.matrix,\n    };\n    return new Duration(conf);\n  }\n\n  function durationToMillis(matrix, vals) {\n    let sum = vals.milliseconds ?? 0;\n    for (const unit of reverseUnits.slice(1)) {\n      if (vals[unit]) {\n        sum += vals[unit] * matrix[unit][\"milliseconds\"];\n      }\n    }\n    return sum;\n  }\n\n  // NB: mutates parameters\n  function normalizeValues(matrix, vals) {\n    // the logic below assumes the overall value of the duration is positive\n    // if this is not the case, factor is used to make it so\n    const factor = durationToMillis(matrix, vals) < 0 ? -1 : 1;\n\n    orderedUnits$1.reduceRight((previous, current) => {\n      if (!isUndefined(vals[current])) {\n        if (previous) {\n          const previousVal = vals[previous] * factor;\n          const conv = matrix[current][previous];\n\n          // if (previousVal < 0):\n          // lower order unit is negative (e.g. { years: 2, days: -2 })\n          // normalize this by reducing the higher order unit by the appropriate amount\n          // and increasing the lower order unit\n          // this can never make the higher order unit negative, because this function only operates\n          // on positive durations, so the amount of time represented by the lower order unit cannot\n          // be larger than the higher order unit\n          // else:\n          // lower order unit is positive (e.g. { years: 2, days: 450 } or { years: -2, days: 450 })\n          // in this case we attempt to convert as much as possible from the lower order unit into\n          // the higher order one\n          //\n          // Math.floor takes care of both of these cases, rounding away from 0\n          // if previousVal < 0 it makes the absolute value larger\n          // if previousVal >= it makes the absolute value smaller\n          const rollUp = Math.floor(previousVal / conv);\n          vals[current] += rollUp * factor;\n          vals[previous] -= rollUp * conv * factor;\n        }\n        return current;\n      } else {\n        return previous;\n      }\n    }, null);\n\n    // try to convert any decimals into smaller units if possible\n    // for example for { years: 2.5, days: 0, seconds: 0 } we want to get { years: 2, days: 182, hours: 12 }\n    orderedUnits$1.reduce((previous, current) => {\n      if (!isUndefined(vals[current])) {\n        if (previous) {\n          const fraction = vals[previous] % 1;\n          vals[previous] -= fraction;\n          vals[current] += fraction * matrix[previous][current];\n        }\n        return current;\n      } else {\n        return previous;\n      }\n    }, null);\n  }\n\n  // Remove all properties with a value of 0 from an object\n  function removeZeroes(vals) {\n    const newVals = {};\n    for (const [key, value] of Object.entries(vals)) {\n      if (value !== 0) {\n        newVals[key] = value;\n      }\n    }\n    return newVals;\n  }\n\n  /**\n   * A Duration object represents a period of time, like \"2 months\" or \"1 day, 1 hour\". Conceptually, it's just a map of units to their quantities, accompanied by some additional configuration and methods for creating, parsing, interrogating, transforming, and formatting them. They can be used on their own or in conjunction with other Luxon types; for example, you can use {@link DateTime#plus} to add a Duration object to a DateTime, producing another DateTime.\n   *\n   * Here is a brief overview of commonly used methods and getters in Duration:\n   *\n   * * **Creation** To create a Duration, use {@link Duration.fromMillis}, {@link Duration.fromObject}, or {@link Duration.fromISO}.\n   * * **Unit values** See the {@link Duration#years}, {@link Duration#months}, {@link Duration#weeks}, {@link Duration#days}, {@link Duration#hours}, {@link Duration#minutes}, {@link Duration#seconds}, {@link Duration#milliseconds} accessors.\n   * * **Configuration** See  {@link Duration#locale} and {@link Duration#numberingSystem} accessors.\n   * * **Transformation** To create new Durations out of old ones use {@link Duration#plus}, {@link Duration#minus}, {@link Duration#normalize}, {@link Duration#set}, {@link Duration#reconfigure}, {@link Duration#shiftTo}, and {@link Duration#negate}.\n   * * **Output** To convert the Duration into other representations, see {@link Duration#as}, {@link Duration#toISO}, {@link Duration#toFormat}, and {@link Duration#toJSON}\n   *\n   * There's are more methods documented below. In addition, for more information on subtler topics like internationalization and validity, see the external documentation.\n   */\n  class Duration {\n    /**\n     * @private\n     */\n    constructor(config) {\n      const accurate = config.conversionAccuracy === \"longterm\" || false;\n      let matrix = accurate ? accurateMatrix : casualMatrix;\n\n      if (config.matrix) {\n        matrix = config.matrix;\n      }\n\n      /**\n       * @access private\n       */\n      this.values = config.values;\n      /**\n       * @access private\n       */\n      this.loc = config.loc || Locale.create();\n      /**\n       * @access private\n       */\n      this.conversionAccuracy = accurate ? \"longterm\" : \"casual\";\n      /**\n       * @access private\n       */\n      this.invalid = config.invalid || null;\n      /**\n       * @access private\n       */\n      this.matrix = matrix;\n      /**\n       * @access private\n       */\n      this.isLuxonDuration = true;\n    }\n\n    /**\n     * Create Duration from a number of milliseconds.\n     * @param {number} count of milliseconds\n     * @param {Object} opts - options for parsing\n     * @param {string} [opts.locale='en-US'] - the locale to use\n     * @param {string} opts.numberingSystem - the numbering system to use\n     * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n     * @return {Duration}\n     */\n    static fromMillis(count, opts) {\n      return Duration.fromObject({ milliseconds: count }, opts);\n    }\n\n    /**\n     * Create a Duration from a JavaScript object with keys like 'years' and 'hours'.\n     * If this object is empty then a zero milliseconds duration is returned.\n     * @param {Object} obj - the object to create the DateTime from\n     * @param {number} obj.years\n     * @param {number} obj.quarters\n     * @param {number} obj.months\n     * @param {number} obj.weeks\n     * @param {number} obj.days\n     * @param {number} obj.hours\n     * @param {number} obj.minutes\n     * @param {number} obj.seconds\n     * @param {number} obj.milliseconds\n     * @param {Object} [opts=[]] - options for creating this Duration\n     * @param {string} [opts.locale='en-US'] - the locale to use\n     * @param {string} opts.numberingSystem - the numbering system to use\n     * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n     * @param {string} [opts.matrix=Object] - the custom conversion system to use\n     * @return {Duration}\n     */\n    static fromObject(obj, opts = {}) {\n      if (obj == null || typeof obj !== \"object\") {\n        throw new InvalidArgumentError(\n          `Duration.fromObject: argument expected to be an object, got ${\n          obj === null ? \"null\" : typeof obj\n        }`\n        );\n      }\n\n      return new Duration({\n        values: normalizeObject(obj, Duration.normalizeUnit),\n        loc: Locale.fromObject(opts),\n        conversionAccuracy: opts.conversionAccuracy,\n        matrix: opts.matrix,\n      });\n    }\n\n    /**\n     * Create a Duration from DurationLike.\n     *\n     * @param {Object | number | Duration} durationLike\n     * One of:\n     * - object with keys like 'years' and 'hours'.\n     * - number representing milliseconds\n     * - Duration instance\n     * @return {Duration}\n     */\n    static fromDurationLike(durationLike) {\n      if (isNumber(durationLike)) {\n        return Duration.fromMillis(durationLike);\n      } else if (Duration.isDuration(durationLike)) {\n        return durationLike;\n      } else if (typeof durationLike === \"object\") {\n        return Duration.fromObject(durationLike);\n      } else {\n        throw new InvalidArgumentError(\n          `Unknown duration argument ${durationLike} of type ${typeof durationLike}`\n        );\n      }\n    }\n\n    /**\n     * Create a Duration from an ISO 8601 duration string.\n     * @param {string} text - text to parse\n     * @param {Object} opts - options for parsing\n     * @param {string} [opts.locale='en-US'] - the locale to use\n     * @param {string} opts.numberingSystem - the numbering system to use\n     * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n     * @param {string} [opts.matrix=Object] - the preset conversion system to use\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Durations\n     * @example Duration.fromISO('P3Y6M1W4DT12H30M5S').toObject() //=> { years: 3, months: 6, weeks: 1, days: 4, hours: 12, minutes: 30, seconds: 5 }\n     * @example Duration.fromISO('PT23H').toObject() //=> { hours: 23 }\n     * @example Duration.fromISO('P5Y3M').toObject() //=> { years: 5, months: 3 }\n     * @return {Duration}\n     */\n    static fromISO(text, opts) {\n      const [parsed] = parseISODuration(text);\n      if (parsed) {\n        return Duration.fromObject(parsed, opts);\n      } else {\n        return Duration.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n      }\n    }\n\n    /**\n     * Create a Duration from an ISO 8601 time string.\n     * @param {string} text - text to parse\n     * @param {Object} opts - options for parsing\n     * @param {string} [opts.locale='en-US'] - the locale to use\n     * @param {string} opts.numberingSystem - the numbering system to use\n     * @param {string} [opts.conversionAccuracy='casual'] - the preset conversion system to use\n     * @param {string} [opts.matrix=Object] - the conversion system to use\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Times\n     * @example Duration.fromISOTime('11:22:33.444').toObject() //=> { hours: 11, minutes: 22, seconds: 33, milliseconds: 444 }\n     * @example Duration.fromISOTime('11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n     * @example Duration.fromISOTime('T11:00').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n     * @example Duration.fromISOTime('1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n     * @example Duration.fromISOTime('T1100').toObject() //=> { hours: 11, minutes: 0, seconds: 0 }\n     * @return {Duration}\n     */\n    static fromISOTime(text, opts) {\n      const [parsed] = parseISOTimeOnly(text);\n      if (parsed) {\n        return Duration.fromObject(parsed, opts);\n      } else {\n        return Duration.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n      }\n    }\n\n    /**\n     * Create an invalid Duration.\n     * @param {string} reason - simple string of why this datetime is invalid. Should not contain parameters or anything else data-dependent\n     * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n     * @return {Duration}\n     */\n    static invalid(reason, explanation = null) {\n      if (!reason) {\n        throw new InvalidArgumentError(\"need to specify a reason the Duration is invalid\");\n      }\n\n      const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n      if (Settings.throwOnInvalid) {\n        throw new InvalidDurationError(invalid);\n      } else {\n        return new Duration({ invalid });\n      }\n    }\n\n    /**\n     * @private\n     */\n    static normalizeUnit(unit) {\n      const normalized = {\n        year: \"years\",\n        years: \"years\",\n        quarter: \"quarters\",\n        quarters: \"quarters\",\n        month: \"months\",\n        months: \"months\",\n        week: \"weeks\",\n        weeks: \"weeks\",\n        day: \"days\",\n        days: \"days\",\n        hour: \"hours\",\n        hours: \"hours\",\n        minute: \"minutes\",\n        minutes: \"minutes\",\n        second: \"seconds\",\n        seconds: \"seconds\",\n        millisecond: \"milliseconds\",\n        milliseconds: \"milliseconds\",\n      }[unit ? unit.toLowerCase() : unit];\n\n      if (!normalized) throw new InvalidUnitError(unit);\n\n      return normalized;\n    }\n\n    /**\n     * Check if an object is a Duration. Works across context boundaries\n     * @param {object} o\n     * @return {boolean}\n     */\n    static isDuration(o) {\n      return (o && o.isLuxonDuration) || false;\n    }\n\n    /**\n     * Get  the locale of a Duration, such 'en-GB'\n     * @type {string}\n     */\n    get locale() {\n      return this.isValid ? this.loc.locale : null;\n    }\n\n    /**\n     * Get the numbering system of a Duration, such 'beng'. The numbering system is used when formatting the Duration\n     *\n     * @type {string}\n     */\n    get numberingSystem() {\n      return this.isValid ? this.loc.numberingSystem : null;\n    }\n\n    /**\n     * Returns a string representation of this Duration formatted according to the specified format string. You may use these tokens:\n     * * `S` for milliseconds\n     * * `s` for seconds\n     * * `m` for minutes\n     * * `h` for hours\n     * * `d` for days\n     * * `w` for weeks\n     * * `M` for months\n     * * `y` for years\n     * Notes:\n     * * Add padding by repeating the token, e.g. \"yy\" pads the years to two digits, \"hhhh\" pads the hours out to four digits\n     * * Tokens can be escaped by wrapping with single quotes.\n     * * The duration will be converted to the set of units in the format string using {@link Duration#shiftTo} and the Durations's conversion accuracy setting.\n     * @param {string} fmt - the format string\n     * @param {Object} opts - options\n     * @param {boolean} [opts.floor=true] - floor numerical values\n     * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"y d s\") //=> \"1 6 2\"\n     * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"yy dd sss\") //=> \"01 06 002\"\n     * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat(\"M S\") //=> \"12 518402000\"\n     * @return {string}\n     */\n    toFormat(fmt, opts = {}) {\n      // reverse-compat since 1.2; we always round down now, never up, and we do it by default\n      const fmtOpts = {\n        ...opts,\n        floor: opts.round !== false && opts.floor !== false,\n      };\n      return this.isValid\n        ? Formatter.create(this.loc, fmtOpts).formatDurationFromString(this, fmt)\n        : INVALID$2;\n    }\n\n    /**\n     * Returns a string representation of a Duration with all units included.\n     * To modify its behavior, use `listStyle` and any Intl.NumberFormat option, though `unitDisplay` is especially relevant.\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options\n     * @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`.\n     * @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor.\n     * @example\n     * ```js\n     * var dur = Duration.fromObject({ days: 1, hours: 5, minutes: 6 })\n     * dur.toHuman() //=> '1 day, 5 hours, 6 minutes'\n     * dur.toHuman({ listStyle: \"long\" }) //=> '1 day, 5 hours, and 6 minutes'\n     * dur.toHuman({ unitDisplay: \"short\" }) //=> '1 day, 5 hr, 6 min'\n     * ```\n     */\n    toHuman(opts = {}) {\n      if (!this.isValid) return INVALID$2;\n\n      const l = orderedUnits$1\n        .map((unit) => {\n          const val = this.values[unit];\n          if (isUndefined(val)) {\n            return null;\n          }\n          return this.loc\n            .numberFormatter({ style: \"unit\", unitDisplay: \"long\", ...opts, unit: unit.slice(0, -1) })\n            .format(val);\n        })\n        .filter((n) => n);\n\n      return this.loc\n        .listFormatter({ type: \"conjunction\", style: opts.listStyle || \"narrow\", ...opts })\n        .format(l);\n    }\n\n    /**\n     * Returns a JavaScript object with this Duration's values.\n     * @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toObject() //=> { years: 1, days: 6, seconds: 2 }\n     * @return {Object}\n     */\n    toObject() {\n      if (!this.isValid) return {};\n      return { ...this.values };\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this Duration.\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Durations\n     * @example Duration.fromObject({ years: 3, seconds: 45 }).toISO() //=> 'P3YT45S'\n     * @example Duration.fromObject({ months: 4, seconds: 45 }).toISO() //=> 'P4MT45S'\n     * @example Duration.fromObject({ months: 5 }).toISO() //=> 'P5M'\n     * @example Duration.fromObject({ minutes: 5 }).toISO() //=> 'PT5M'\n     * @example Duration.fromObject({ milliseconds: 6 }).toISO() //=> 'PT0.006S'\n     * @return {string}\n     */\n    toISO() {\n      // we could use the formatter, but this is an easier way to get the minimum string\n      if (!this.isValid) return null;\n\n      let s = \"P\";\n      if (this.years !== 0) s += this.years + \"Y\";\n      if (this.months !== 0 || this.quarters !== 0) s += this.months + this.quarters * 3 + \"M\";\n      if (this.weeks !== 0) s += this.weeks + \"W\";\n      if (this.days !== 0) s += this.days + \"D\";\n      if (this.hours !== 0 || this.minutes !== 0 || this.seconds !== 0 || this.milliseconds !== 0)\n        s += \"T\";\n      if (this.hours !== 0) s += this.hours + \"H\";\n      if (this.minutes !== 0) s += this.minutes + \"M\";\n      if (this.seconds !== 0 || this.milliseconds !== 0)\n        // this will handle \"floating point madness\" by removing extra decimal places\n        // https://stackoverflow.com/questions/588004/is-floating-point-math-broken\n        s += roundTo(this.seconds + this.milliseconds / 1000, 3) + \"S\";\n      if (s === \"P\") s += \"T0S\";\n      return s;\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this Duration, formatted as a time of day.\n     * Note that this will return null if the duration is invalid, negative, or equal to or greater than 24 hours.\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Times\n     * @param {Object} opts - options\n     * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n     * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n     * @param {boolean} [opts.includePrefix=false] - include the `T` prefix\n     * @param {string} [opts.format='extended'] - choose between the basic and extended format\n     * @example Duration.fromObject({ hours: 11 }).toISOTime() //=> '11:00:00.000'\n     * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressMilliseconds: true }) //=> '11:00:00'\n     * @example Duration.fromObject({ hours: 11 }).toISOTime({ suppressSeconds: true }) //=> '11:00'\n     * @example Duration.fromObject({ hours: 11 }).toISOTime({ includePrefix: true }) //=> 'T11:00:00.000'\n     * @example Duration.fromObject({ hours: 11 }).toISOTime({ format: 'basic' }) //=> '110000.000'\n     * @return {string}\n     */\n    toISOTime(opts = {}) {\n      if (!this.isValid) return null;\n\n      const millis = this.toMillis();\n      if (millis < 0 || millis >= 86400000) return null;\n\n      opts = {\n        suppressMilliseconds: false,\n        suppressSeconds: false,\n        includePrefix: false,\n        format: \"extended\",\n        ...opts,\n        includeOffset: false,\n      };\n\n      const dateTime = DateTime.fromMillis(millis, { zone: \"UTC\" });\n      return dateTime.toISOTime(opts);\n    }\n\n    /**\n     * Returns an ISO 8601 representation of this Duration appropriate for use in JSON.\n     * @return {string}\n     */\n    toJSON() {\n      return this.toISO();\n    }\n\n    /**\n     * Returns an ISO 8601 representation of this Duration appropriate for use in debugging.\n     * @return {string}\n     */\n    toString() {\n      return this.toISO();\n    }\n\n    /**\n     * Returns a string representation of this Duration appropriate for the REPL.\n     * @return {string}\n     */\n    [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n      if (this.isValid) {\n        return `Duration { values: ${JSON.stringify(this.values)} }`;\n      } else {\n        return `Duration { Invalid, reason: ${this.invalidReason} }`;\n      }\n    }\n\n    /**\n     * Returns an milliseconds value of this Duration.\n     * @return {number}\n     */\n    toMillis() {\n      if (!this.isValid) return NaN;\n\n      return durationToMillis(this.matrix, this.values);\n    }\n\n    /**\n     * Returns an milliseconds value of this Duration. Alias of {@link toMillis}\n     * @return {number}\n     */\n    valueOf() {\n      return this.toMillis();\n    }\n\n    /**\n     * Make this Duration longer by the specified amount. Return a newly-constructed Duration.\n     * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n     * @return {Duration}\n     */\n    plus(duration) {\n      if (!this.isValid) return this;\n\n      const dur = Duration.fromDurationLike(duration),\n        result = {};\n\n      for (const k of orderedUnits$1) {\n        if (hasOwnProperty(dur.values, k) || hasOwnProperty(this.values, k)) {\n          result[k] = dur.get(k) + this.get(k);\n        }\n      }\n\n      return clone$1(this, { values: result }, true);\n    }\n\n    /**\n     * Make this Duration shorter by the specified amount. Return a newly-constructed Duration.\n     * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n     * @return {Duration}\n     */\n    minus(duration) {\n      if (!this.isValid) return this;\n\n      const dur = Duration.fromDurationLike(duration);\n      return this.plus(dur.negate());\n    }\n\n    /**\n     * Scale this Duration by the specified amount. Return a newly-constructed Duration.\n     * @param {function} fn - The function to apply to each unit. Arity is 1 or 2: the value of the unit and, optionally, the unit name. Must return a number.\n     * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits(x => x * 2) //=> { hours: 2, minutes: 60 }\n     * @example Duration.fromObject({ hours: 1, minutes: 30 }).mapUnits((x, u) => u === \"hours\" ? x * 2 : x) //=> { hours: 2, minutes: 30 }\n     * @return {Duration}\n     */\n    mapUnits(fn) {\n      if (!this.isValid) return this;\n      const result = {};\n      for (const k of Object.keys(this.values)) {\n        result[k] = asNumber(fn(this.values[k], k));\n      }\n      return clone$1(this, { values: result }, true);\n    }\n\n    /**\n     * Get the value of unit.\n     * @param {string} unit - a unit such as 'minute' or 'day'\n     * @example Duration.fromObject({years: 2, days: 3}).get('years') //=> 2\n     * @example Duration.fromObject({years: 2, days: 3}).get('months') //=> 0\n     * @example Duration.fromObject({years: 2, days: 3}).get('days') //=> 3\n     * @return {number}\n     */\n    get(unit) {\n      return this[Duration.normalizeUnit(unit)];\n    }\n\n    /**\n     * \"Set\" the values of specified units. Return a newly-constructed Duration.\n     * @param {Object} values - a mapping of units to numbers\n     * @example dur.set({ years: 2017 })\n     * @example dur.set({ hours: 8, minutes: 30 })\n     * @return {Duration}\n     */\n    set(values) {\n      if (!this.isValid) return this;\n\n      const mixed = { ...this.values, ...normalizeObject(values, Duration.normalizeUnit) };\n      return clone$1(this, { values: mixed });\n    }\n\n    /**\n     * \"Set\" the locale and/or numberingSystem.  Returns a newly-constructed Duration.\n     * @example dur.reconfigure({ locale: 'en-GB' })\n     * @return {Duration}\n     */\n    reconfigure({ locale, numberingSystem, conversionAccuracy, matrix } = {}) {\n      const loc = this.loc.clone({ locale, numberingSystem });\n      const opts = { loc, matrix, conversionAccuracy };\n      return clone$1(this, opts);\n    }\n\n    /**\n     * Return the length of the duration in the specified unit.\n     * @param {string} unit - a unit such as 'minutes' or 'days'\n     * @example Duration.fromObject({years: 1}).as('days') //=> 365\n     * @example Duration.fromObject({years: 1}).as('months') //=> 12\n     * @example Duration.fromObject({hours: 60}).as('days') //=> 2.5\n     * @return {number}\n     */\n    as(unit) {\n      return this.isValid ? this.shiftTo(unit).get(unit) : NaN;\n    }\n\n    /**\n     * Reduce this Duration to its canonical representation in its current units.\n     * Assuming the overall value of the Duration is positive, this means:\n     * - excessive values for lower-order units are converted to higher-order units (if possible, see first and second example)\n     * - negative lower-order units are converted to higher order units (there must be such a higher order unit, otherwise\n     *   the overall value would be negative, see third example)\n     * - fractional values for higher-order units are converted to lower-order units (if possible, see fourth example)\n     *\n     * If the overall value is negative, the result of this method is equivalent to `this.negate().normalize().negate()`.\n     * @example Duration.fromObject({ years: 2, days: 5000 }).normalize().toObject() //=> { years: 15, days: 255 }\n     * @example Duration.fromObject({ days: 5000 }).normalize().toObject() //=> { days: 5000 }\n     * @example Duration.fromObject({ hours: 12, minutes: -45 }).normalize().toObject() //=> { hours: 11, minutes: 15 }\n     * @example Duration.fromObject({ years: 2.5, days: 0, hours: 0 }).normalize().toObject() //=> { years: 2, days: 182, hours: 12 }\n     * @return {Duration}\n     */\n    normalize() {\n      if (!this.isValid) return this;\n      const vals = this.toObject();\n      normalizeValues(this.matrix, vals);\n      return clone$1(this, { values: vals }, true);\n    }\n\n    /**\n     * Rescale units to its largest representation\n     * @example Duration.fromObject({ milliseconds: 90000 }).rescale().toObject() //=> { minutes: 1, seconds: 30 }\n     * @return {Duration}\n     */\n    rescale() {\n      if (!this.isValid) return this;\n      const vals = removeZeroes(this.normalize().shiftToAll().toObject());\n      return clone$1(this, { values: vals }, true);\n    }\n\n    /**\n     * Convert this Duration into its representation in a different set of units.\n     * @example Duration.fromObject({ hours: 1, seconds: 30 }).shiftTo('minutes', 'milliseconds').toObject() //=> { minutes: 60, milliseconds: 30000 }\n     * @return {Duration}\n     */\n    shiftTo(...units) {\n      if (!this.isValid) return this;\n\n      if (units.length === 0) {\n        return this;\n      }\n\n      units = units.map((u) => Duration.normalizeUnit(u));\n\n      const built = {},\n        accumulated = {},\n        vals = this.toObject();\n      let lastUnit;\n\n      for (const k of orderedUnits$1) {\n        if (units.indexOf(k) >= 0) {\n          lastUnit = k;\n\n          let own = 0;\n\n          // anything we haven't boiled down yet should get boiled to this unit\n          for (const ak in accumulated) {\n            own += this.matrix[ak][k] * accumulated[ak];\n            accumulated[ak] = 0;\n          }\n\n          // plus anything that's already in this unit\n          if (isNumber(vals[k])) {\n            own += vals[k];\n          }\n\n          // only keep the integer part for now in the hopes of putting any decimal part\n          // into a smaller unit later\n          const i = Math.trunc(own);\n          built[k] = i;\n          accumulated[k] = (own * 1000 - i * 1000) / 1000;\n\n          // otherwise, keep it in the wings to boil it later\n        } else if (isNumber(vals[k])) {\n          accumulated[k] = vals[k];\n        }\n      }\n\n      // anything leftover becomes the decimal for the last unit\n      // lastUnit must be defined since units is not empty\n      for (const key in accumulated) {\n        if (accumulated[key] !== 0) {\n          built[lastUnit] +=\n            key === lastUnit ? accumulated[key] : accumulated[key] / this.matrix[lastUnit][key];\n        }\n      }\n\n      normalizeValues(this.matrix, built);\n      return clone$1(this, { values: built }, true);\n    }\n\n    /**\n     * Shift this Duration to all available units.\n     * Same as shiftTo(\"years\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", \"seconds\", \"milliseconds\")\n     * @return {Duration}\n     */\n    shiftToAll() {\n      if (!this.isValid) return this;\n      return this.shiftTo(\n        \"years\",\n        \"months\",\n        \"weeks\",\n        \"days\",\n        \"hours\",\n        \"minutes\",\n        \"seconds\",\n        \"milliseconds\"\n      );\n    }\n\n    /**\n     * Return the negative of this Duration.\n     * @example Duration.fromObject({ hours: 1, seconds: 30 }).negate().toObject() //=> { hours: -1, seconds: -30 }\n     * @return {Duration}\n     */\n    negate() {\n      if (!this.isValid) return this;\n      const negated = {};\n      for (const k of Object.keys(this.values)) {\n        negated[k] = this.values[k] === 0 ? 0 : -this.values[k];\n      }\n      return clone$1(this, { values: negated }, true);\n    }\n\n    /**\n     * Get the years.\n     * @type {number}\n     */\n    get years() {\n      return this.isValid ? this.values.years || 0 : NaN;\n    }\n\n    /**\n     * Get the quarters.\n     * @type {number}\n     */\n    get quarters() {\n      return this.isValid ? this.values.quarters || 0 : NaN;\n    }\n\n    /**\n     * Get the months.\n     * @type {number}\n     */\n    get months() {\n      return this.isValid ? this.values.months || 0 : NaN;\n    }\n\n    /**\n     * Get the weeks\n     * @type {number}\n     */\n    get weeks() {\n      return this.isValid ? this.values.weeks || 0 : NaN;\n    }\n\n    /**\n     * Get the days.\n     * @type {number}\n     */\n    get days() {\n      return this.isValid ? this.values.days || 0 : NaN;\n    }\n\n    /**\n     * Get the hours.\n     * @type {number}\n     */\n    get hours() {\n      return this.isValid ? this.values.hours || 0 : NaN;\n    }\n\n    /**\n     * Get the minutes.\n     * @type {number}\n     */\n    get minutes() {\n      return this.isValid ? this.values.minutes || 0 : NaN;\n    }\n\n    /**\n     * Get the seconds.\n     * @return {number}\n     */\n    get seconds() {\n      return this.isValid ? this.values.seconds || 0 : NaN;\n    }\n\n    /**\n     * Get the milliseconds.\n     * @return {number}\n     */\n    get milliseconds() {\n      return this.isValid ? this.values.milliseconds || 0 : NaN;\n    }\n\n    /**\n     * Returns whether the Duration is invalid. Invalid durations are returned by diff operations\n     * on invalid DateTimes or Intervals.\n     * @return {boolean}\n     */\n    get isValid() {\n      return this.invalid === null;\n    }\n\n    /**\n     * Returns an error code if this Duration became invalid, or null if the Duration is valid\n     * @return {string}\n     */\n    get invalidReason() {\n      return this.invalid ? this.invalid.reason : null;\n    }\n\n    /**\n     * Returns an explanation of why this Duration became invalid, or null if the Duration is valid\n     * @type {string}\n     */\n    get invalidExplanation() {\n      return this.invalid ? this.invalid.explanation : null;\n    }\n\n    /**\n     * Equality check\n     * Two Durations are equal iff they have the same units and the same values for each unit.\n     * @param {Duration} other\n     * @return {boolean}\n     */\n    equals(other) {\n      if (!this.isValid || !other.isValid) {\n        return false;\n      }\n\n      if (!this.loc.equals(other.loc)) {\n        return false;\n      }\n\n      function eq(v1, v2) {\n        // Consider 0 and undefined as equal\n        if (v1 === undefined || v1 === 0) return v2 === undefined || v2 === 0;\n        return v1 === v2;\n      }\n\n      for (const u of orderedUnits$1) {\n        if (!eq(this.values[u], other.values[u])) {\n          return false;\n        }\n      }\n      return true;\n    }\n  }\n\n  const INVALID$1 = \"Invalid Interval\";\n\n  // checks if the start is equal to or before the end\n  function validateStartEnd(start, end) {\n    if (!start || !start.isValid) {\n      return Interval.invalid(\"missing or invalid start\");\n    } else if (!end || !end.isValid) {\n      return Interval.invalid(\"missing or invalid end\");\n    } else if (end < start) {\n      return Interval.invalid(\n        \"end before start\",\n        `The end of an interval must be after its start, but you had start=${start.toISO()} and end=${end.toISO()}`\n      );\n    } else {\n      return null;\n    }\n  }\n\n  /**\n   * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}. Conceptually, it's a container for those two endpoints, accompanied by methods for creating, parsing, interrogating, comparing, transforming, and formatting them.\n   *\n   * Here is a brief overview of the most commonly used methods and getters in Interval:\n   *\n   * * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.\n   * * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.\n   * * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame}, {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.\n   * * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually}, {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.\n   * * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}\n   * * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate}, {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.\n   */\n  class Interval {\n    /**\n     * @private\n     */\n    constructor(config) {\n      /**\n       * @access private\n       */\n      this.s = config.start;\n      /**\n       * @access private\n       */\n      this.e = config.end;\n      /**\n       * @access private\n       */\n      this.invalid = config.invalid || null;\n      /**\n       * @access private\n       */\n      this.isLuxonInterval = true;\n    }\n\n    /**\n     * Create an invalid Interval.\n     * @param {string} reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent\n     * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n     * @return {Interval}\n     */\n    static invalid(reason, explanation = null) {\n      if (!reason) {\n        throw new InvalidArgumentError(\"need to specify a reason the Interval is invalid\");\n      }\n\n      const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n      if (Settings.throwOnInvalid) {\n        throw new InvalidIntervalError(invalid);\n      } else {\n        return new Interval({ invalid });\n      }\n    }\n\n    /**\n     * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.\n     * @param {DateTime|Date|Object} start\n     * @param {DateTime|Date|Object} end\n     * @return {Interval}\n     */\n    static fromDateTimes(start, end) {\n      const builtStart = friendlyDateTime(start),\n        builtEnd = friendlyDateTime(end);\n\n      const validateError = validateStartEnd(builtStart, builtEnd);\n\n      if (validateError == null) {\n        return new Interval({\n          start: builtStart,\n          end: builtEnd,\n        });\n      } else {\n        return validateError;\n      }\n    }\n\n    /**\n     * Create an Interval from a start DateTime and a Duration to extend to.\n     * @param {DateTime|Date|Object} start\n     * @param {Duration|Object|number} duration - the length of the Interval.\n     * @return {Interval}\n     */\n    static after(start, duration) {\n      const dur = Duration.fromDurationLike(duration),\n        dt = friendlyDateTime(start);\n      return Interval.fromDateTimes(dt, dt.plus(dur));\n    }\n\n    /**\n     * Create an Interval from an end DateTime and a Duration to extend backwards to.\n     * @param {DateTime|Date|Object} end\n     * @param {Duration|Object|number} duration - the length of the Interval.\n     * @return {Interval}\n     */\n    static before(end, duration) {\n      const dur = Duration.fromDurationLike(duration),\n        dt = friendlyDateTime(end);\n      return Interval.fromDateTimes(dt.minus(dur), dt);\n    }\n\n    /**\n     * Create an Interval from an ISO 8601 string.\n     * Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.\n     * @param {string} text - the ISO string to parse\n     * @param {Object} [opts] - options to pass {@link DateTime#fromISO} and optionally {@link Duration#fromISO}\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n     * @return {Interval}\n     */\n    static fromISO(text, opts) {\n      const [s, e] = (text || \"\").split(\"/\", 2);\n      if (s && e) {\n        let start, startIsValid;\n        try {\n          start = DateTime.fromISO(s, opts);\n          startIsValid = start.isValid;\n        } catch (e) {\n          startIsValid = false;\n        }\n\n        let end, endIsValid;\n        try {\n          end = DateTime.fromISO(e, opts);\n          endIsValid = end.isValid;\n        } catch (e) {\n          endIsValid = false;\n        }\n\n        if (startIsValid && endIsValid) {\n          return Interval.fromDateTimes(start, end);\n        }\n\n        if (startIsValid) {\n          const dur = Duration.fromISO(e, opts);\n          if (dur.isValid) {\n            return Interval.after(start, dur);\n          }\n        } else if (endIsValid) {\n          const dur = Duration.fromISO(s, opts);\n          if (dur.isValid) {\n            return Interval.before(end, dur);\n          }\n        }\n      }\n      return Interval.invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ISO 8601`);\n    }\n\n    /**\n     * Check if an object is an Interval. Works across context boundaries\n     * @param {object} o\n     * @return {boolean}\n     */\n    static isInterval(o) {\n      return (o && o.isLuxonInterval) || false;\n    }\n\n    /**\n     * Returns the start of the Interval\n     * @type {DateTime}\n     */\n    get start() {\n      return this.isValid ? this.s : null;\n    }\n\n    /**\n     * Returns the end of the Interval\n     * @type {DateTime}\n     */\n    get end() {\n      return this.isValid ? this.e : null;\n    }\n\n    /**\n     * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.\n     * @type {boolean}\n     */\n    get isValid() {\n      return this.invalidReason === null;\n    }\n\n    /**\n     * Returns an error code if this Interval is invalid, or null if the Interval is valid\n     * @type {string}\n     */\n    get invalidReason() {\n      return this.invalid ? this.invalid.reason : null;\n    }\n\n    /**\n     * Returns an explanation of why this Interval became invalid, or null if the Interval is valid\n     * @type {string}\n     */\n    get invalidExplanation() {\n      return this.invalid ? this.invalid.explanation : null;\n    }\n\n    /**\n     * Returns the length of the Interval in the specified unit.\n     * @param {string} unit - the unit (such as 'hours' or 'days') to return the length in.\n     * @return {number}\n     */\n    length(unit = \"milliseconds\") {\n      return this.isValid ? this.toDuration(...[unit]).get(unit) : NaN;\n    }\n\n    /**\n     * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.\n     * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'\n     * asks 'what dates are included in this interval?', not 'how many days long is this interval?'\n     * @param {string} [unit='milliseconds'] - the unit of time to count.\n     * @param {Object} opts - options\n     * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; this operation will always use the locale of the start DateTime\n     * @return {number}\n     */\n    count(unit = \"milliseconds\", opts) {\n      if (!this.isValid) return NaN;\n      const start = this.start.startOf(unit, opts);\n      let end;\n      if (opts?.useLocaleWeeks) {\n        end = this.end.reconfigure({ locale: start.locale });\n      } else {\n        end = this.end;\n      }\n      end = end.startOf(unit, opts);\n      return Math.floor(end.diff(start, unit).get(unit)) + (end.valueOf() !== this.end.valueOf());\n    }\n\n    /**\n     * Returns whether this Interval's start and end are both in the same unit of time\n     * @param {string} unit - the unit of time to check sameness on\n     * @return {boolean}\n     */\n    hasSame(unit) {\n      return this.isValid ? this.isEmpty() || this.e.minus(1).hasSame(this.s, unit) : false;\n    }\n\n    /**\n     * Return whether this Interval has the same start and end DateTimes.\n     * @return {boolean}\n     */\n    isEmpty() {\n      return this.s.valueOf() === this.e.valueOf();\n    }\n\n    /**\n     * Return whether this Interval's start is after the specified DateTime.\n     * @param {DateTime} dateTime\n     * @return {boolean}\n     */\n    isAfter(dateTime) {\n      if (!this.isValid) return false;\n      return this.s > dateTime;\n    }\n\n    /**\n     * Return whether this Interval's end is before the specified DateTime.\n     * @param {DateTime} dateTime\n     * @return {boolean}\n     */\n    isBefore(dateTime) {\n      if (!this.isValid) return false;\n      return this.e <= dateTime;\n    }\n\n    /**\n     * Return whether this Interval contains the specified DateTime.\n     * @param {DateTime} dateTime\n     * @return {boolean}\n     */\n    contains(dateTime) {\n      if (!this.isValid) return false;\n      return this.s <= dateTime && this.e > dateTime;\n    }\n\n    /**\n     * \"Sets\" the start and/or end dates. Returns a newly-constructed Interval.\n     * @param {Object} values - the values to set\n     * @param {DateTime} values.start - the starting DateTime\n     * @param {DateTime} values.end - the ending DateTime\n     * @return {Interval}\n     */\n    set({ start, end } = {}) {\n      if (!this.isValid) return this;\n      return Interval.fromDateTimes(start || this.s, end || this.e);\n    }\n\n    /**\n     * Split this Interval at each of the specified DateTimes\n     * @param {...DateTime} dateTimes - the unit of time to count.\n     * @return {Array}\n     */\n    splitAt(...dateTimes) {\n      if (!this.isValid) return [];\n      const sorted = dateTimes\n          .map(friendlyDateTime)\n          .filter((d) => this.contains(d))\n          .sort((a, b) => a.toMillis() - b.toMillis()),\n        results = [];\n      let { s } = this,\n        i = 0;\n\n      while (s < this.e) {\n        const added = sorted[i] || this.e,\n          next = +added > +this.e ? this.e : added;\n        results.push(Interval.fromDateTimes(s, next));\n        s = next;\n        i += 1;\n      }\n\n      return results;\n    }\n\n    /**\n     * Split this Interval into smaller Intervals, each of the specified length.\n     * Left over time is grouped into a smaller interval\n     * @param {Duration|Object|number} duration - The length of each resulting interval.\n     * @return {Array}\n     */\n    splitBy(duration) {\n      const dur = Duration.fromDurationLike(duration);\n\n      if (!this.isValid || !dur.isValid || dur.as(\"milliseconds\") === 0) {\n        return [];\n      }\n\n      let { s } = this,\n        idx = 1,\n        next;\n\n      const results = [];\n      while (s < this.e) {\n        const added = this.start.plus(dur.mapUnits((x) => x * idx));\n        next = +added > +this.e ? this.e : added;\n        results.push(Interval.fromDateTimes(s, next));\n        s = next;\n        idx += 1;\n      }\n\n      return results;\n    }\n\n    /**\n     * Split this Interval into the specified number of smaller intervals.\n     * @param {number} numberOfParts - The number of Intervals to divide the Interval into.\n     * @return {Array}\n     */\n    divideEqually(numberOfParts) {\n      if (!this.isValid) return [];\n      return this.splitBy(this.length() / numberOfParts).slice(0, numberOfParts);\n    }\n\n    /**\n     * Return whether this Interval overlaps with the specified Interval\n     * @param {Interval} other\n     * @return {boolean}\n     */\n    overlaps(other) {\n      return this.e > other.s && this.s < other.e;\n    }\n\n    /**\n     * Return whether this Interval's end is adjacent to the specified Interval's start.\n     * @param {Interval} other\n     * @return {boolean}\n     */\n    abutsStart(other) {\n      if (!this.isValid) return false;\n      return +this.e === +other.s;\n    }\n\n    /**\n     * Return whether this Interval's start is adjacent to the specified Interval's end.\n     * @param {Interval} other\n     * @return {boolean}\n     */\n    abutsEnd(other) {\n      if (!this.isValid) return false;\n      return +other.e === +this.s;\n    }\n\n    /**\n     * Return whether this Interval engulfs the start and end of the specified Interval.\n     * @param {Interval} other\n     * @return {boolean}\n     */\n    engulfs(other) {\n      if (!this.isValid) return false;\n      return this.s <= other.s && this.e >= other.e;\n    }\n\n    /**\n     * Return whether this Interval has the same start and end as the specified Interval.\n     * @param {Interval} other\n     * @return {boolean}\n     */\n    equals(other) {\n      if (!this.isValid || !other.isValid) {\n        return false;\n      }\n\n      return this.s.equals(other.s) && this.e.equals(other.e);\n    }\n\n    /**\n     * Return an Interval representing the intersection of this Interval and the specified Interval.\n     * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.\n     * Returns null if the intersection is empty, meaning, the intervals don't intersect.\n     * @param {Interval} other\n     * @return {Interval}\n     */\n    intersection(other) {\n      if (!this.isValid) return this;\n      const s = this.s > other.s ? this.s : other.s,\n        e = this.e < other.e ? this.e : other.e;\n\n      if (s >= e) {\n        return null;\n      } else {\n        return Interval.fromDateTimes(s, e);\n      }\n    }\n\n    /**\n     * Return an Interval representing the union of this Interval and the specified Interval.\n     * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.\n     * @param {Interval} other\n     * @return {Interval}\n     */\n    union(other) {\n      if (!this.isValid) return this;\n      const s = this.s < other.s ? this.s : other.s,\n        e = this.e > other.e ? this.e : other.e;\n      return Interval.fromDateTimes(s, e);\n    }\n\n    /**\n     * Merge an array of Intervals into a equivalent minimal set of Intervals.\n     * Combines overlapping and adjacent Intervals.\n     * @param {Array} intervals\n     * @return {Array}\n     */\n    static merge(intervals) {\n      const [found, final] = intervals\n        .sort((a, b) => a.s - b.s)\n        .reduce(\n          ([sofar, current], item) => {\n            if (!current) {\n              return [sofar, item];\n            } else if (current.overlaps(item) || current.abutsStart(item)) {\n              return [sofar, current.union(item)];\n            } else {\n              return [sofar.concat([current]), item];\n            }\n          },\n          [[], null]\n        );\n      if (final) {\n        found.push(final);\n      }\n      return found;\n    }\n\n    /**\n     * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.\n     * @param {Array} intervals\n     * @return {Array}\n     */\n    static xor(intervals) {\n      let start = null,\n        currentCount = 0;\n      const results = [],\n        ends = intervals.map((i) => [\n          { time: i.s, type: \"s\" },\n          { time: i.e, type: \"e\" },\n        ]),\n        flattened = Array.prototype.concat(...ends),\n        arr = flattened.sort((a, b) => a.time - b.time);\n\n      for (const i of arr) {\n        currentCount += i.type === \"s\" ? 1 : -1;\n\n        if (currentCount === 1) {\n          start = i.time;\n        } else {\n          if (start && +start !== +i.time) {\n            results.push(Interval.fromDateTimes(start, i.time));\n          }\n\n          start = null;\n        }\n      }\n\n      return Interval.merge(results);\n    }\n\n    /**\n     * Return an Interval representing the span of time in this Interval that doesn't overlap with any of the specified Intervals.\n     * @param {...Interval} intervals\n     * @return {Array}\n     */\n    difference(...intervals) {\n      return Interval.xor([this].concat(intervals))\n        .map((i) => this.intersection(i))\n        .filter((i) => i && !i.isEmpty());\n    }\n\n    /**\n     * Returns a string representation of this Interval appropriate for debugging.\n     * @return {string}\n     */\n    toString() {\n      if (!this.isValid) return INVALID$1;\n      return `[${this.s.toISO()} \u2013 ${this.e.toISO()})`;\n    }\n\n    /**\n     * Returns a string representation of this Interval appropriate for the REPL.\n     * @return {string}\n     */\n    [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n      if (this.isValid) {\n        return `Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`;\n      } else {\n        return `Interval { Invalid, reason: ${this.invalidReason} }`;\n      }\n    }\n\n    /**\n     * Returns a localized string representing this Interval. Accepts the same options as the\n     * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as\n     * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method\n     * is browser-specific, but in general it will return an appropriate representation of the\n     * Interval in the assigned locale. Defaults to the system's locale if no locale has been\n     * specified.\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n     * @param {Object} [formatOpts=DateTime.DATE_SHORT] - Either a DateTime preset or\n     * Intl.DateTimeFormat constructor options.\n     * @param {Object} opts - Options to override the configuration of the start DateTime.\n     * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 \u2013 11/8/2022\n     * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 \u2013 8, 2022\n     * @example Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7\u20138 novembre 2022\n     * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 \u2013 8:00 PM\n     * @example Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> Mon, Nov 07, 6:00 \u2013 8:00 p\n     * @return {string}\n     */\n    toLocaleString(formatOpts = DATE_SHORT, opts = {}) {\n      return this.isValid\n        ? Formatter.create(this.s.loc.clone(opts), formatOpts).formatInterval(this)\n        : INVALID$1;\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this Interval.\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n     * @param {Object} opts - The same options as {@link DateTime#toISO}\n     * @return {string}\n     */\n    toISO(opts) {\n      if (!this.isValid) return INVALID$1;\n      return `${this.s.toISO(opts)}/${this.e.toISO(opts)}`;\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of date of this Interval.\n     * The time components are ignored.\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n     * @return {string}\n     */\n    toISODate() {\n      if (!this.isValid) return INVALID$1;\n      return `${this.s.toISODate()}/${this.e.toISODate()}`;\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of time of this Interval.\n     * The date components are ignored.\n     * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals\n     * @param {Object} opts - The same options as {@link DateTime#toISO}\n     * @return {string}\n     */\n    toISOTime(opts) {\n      if (!this.isValid) return INVALID$1;\n      return `${this.s.toISOTime(opts)}/${this.e.toISOTime(opts)}`;\n    }\n\n    /**\n     * Returns a string representation of this Interval formatted according to the specified format\n     * string. **You may not want this.** See {@link Interval#toLocaleString} for a more flexible\n     * formatting tool.\n     * @param {string} dateFormat - The format string. This string formats the start and end time.\n     * See {@link DateTime#toFormat} for details.\n     * @param {Object} opts - Options.\n     * @param {string} [opts.separator =  ' \u2013 '] - A separator to place between the start and end\n     * representations.\n     * @return {string}\n     */\n    toFormat(dateFormat, { separator = \" \u2013 \" } = {}) {\n      if (!this.isValid) return INVALID$1;\n      return `${this.s.toFormat(dateFormat)}${separator}${this.e.toFormat(dateFormat)}`;\n    }\n\n    /**\n     * Return a Duration representing the time spanned by this interval.\n     * @param {string|string[]} [unit=['milliseconds']] - the unit or units (such as 'hours' or 'days') to include in the duration.\n     * @param {Object} opts - options that affect the creation of the Duration\n     * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n     * @example Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }\n     * @example Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }\n     * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }\n     * @example Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }\n     * @example Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }\n     * @return {Duration}\n     */\n    toDuration(unit, opts) {\n      if (!this.isValid) {\n        return Duration.invalid(this.invalidReason);\n      }\n      return this.e.diff(this.s, unit, opts);\n    }\n\n    /**\n     * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes\n     * @param {function} mapFn\n     * @return {Interval}\n     * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())\n     * @example Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))\n     */\n    mapEndpoints(mapFn) {\n      return Interval.fromDateTimes(mapFn(this.s), mapFn(this.e));\n    }\n  }\n\n  /**\n   * The Info class contains static methods for retrieving general time and date related data. For example, it has methods for finding out if a time zone has a DST, for listing the months in any supported locale, and for discovering which of Luxon features are available in the current environment.\n   */\n  class Info {\n    /**\n     * Return whether the specified zone contains a DST.\n     * @param {string|Zone} [zone='local'] - Zone to check. Defaults to the environment's local zone.\n     * @return {boolean}\n     */\n    static hasDST(zone = Settings.defaultZone) {\n      const proto = DateTime.now().setZone(zone).set({ month: 12 });\n\n      return !zone.isUniversal && proto.offset !== proto.set({ month: 6 }).offset;\n    }\n\n    /**\n     * Return whether the specified zone is a valid IANA specifier.\n     * @param {string} zone - Zone to check\n     * @return {boolean}\n     */\n    static isValidIANAZone(zone) {\n      return IANAZone.isValidZone(zone);\n    }\n\n    /**\n     * Converts the input into a {@link Zone} instance.\n     *\n     * * If `input` is already a Zone instance, it is returned unchanged.\n     * * If `input` is a string containing a valid time zone name, a Zone instance\n     *   with that name is returned.\n     * * If `input` is a string that doesn't refer to a known time zone, a Zone\n     *   instance with {@link Zone#isValid} == false is returned.\n     * * If `input is a number, a Zone instance with the specified fixed offset\n     *   in minutes is returned.\n     * * If `input` is `null` or `undefined`, the default zone is returned.\n     * @param {string|Zone|number} [input] - the value to be converted\n     * @return {Zone}\n     */\n    static normalizeZone(input) {\n      return normalizeZone(input, Settings.defaultZone);\n    }\n\n    /**\n     * Get the weekday on which the week starts according to the given locale.\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @returns {number} the start of the week, 1 for Monday through 7 for Sunday\n     */\n    static getStartOfWeek({ locale = null, locObj = null } = {}) {\n      return (locObj || Locale.create(locale)).getStartOfWeek();\n    }\n\n    /**\n     * Get the minimum number of days necessary in a week before it is considered part of the next year according\n     * to the given locale.\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @returns {number}\n     */\n    static getMinimumDaysInFirstWeek({ locale = null, locObj = null } = {}) {\n      return (locObj || Locale.create(locale)).getMinDaysInFirstWeek();\n    }\n\n    /**\n     * Get the weekdays, which are considered the weekend according to the given locale\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @returns {number[]} an array of weekdays, 1 for Monday through 7 for Sunday\n     */\n    static getWeekendWeekdays({ locale = null, locObj = null } = {}) {\n      // copy the array, because we cache it internally\n      return (locObj || Locale.create(locale)).getWeekendDays().slice();\n    }\n\n    /**\n     * Return an array of standalone month names.\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n     * @param {string} [length='long'] - the length of the month representation, such as \"numeric\", \"2-digit\", \"narrow\", \"short\", \"long\"\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.numberingSystem=null] - the numbering system\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @param {string} [opts.outputCalendar='gregory'] - the calendar\n     * @example Info.months()[0] //=> 'January'\n     * @example Info.months('short')[0] //=> 'Jan'\n     * @example Info.months('numeric')[0] //=> '1'\n     * @example Info.months('short', { locale: 'fr-CA' } )[0] //=> 'janv.'\n     * @example Info.months('numeric', { locale: 'ar' })[0] //=> '\u0661'\n     * @example Info.months('long', { outputCalendar: 'islamic' })[0] //=> 'Rabi\u02bb I'\n     * @return {Array}\n     */\n    static months(\n      length = \"long\",\n      { locale = null, numberingSystem = null, locObj = null, outputCalendar = \"gregory\" } = {}\n    ) {\n      return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length);\n    }\n\n    /**\n     * Return an array of format month names.\n     * Format months differ from standalone months in that they're meant to appear next to the day of the month. In some languages, that\n     * changes the string.\n     * See {@link Info#months}\n     * @param {string} [length='long'] - the length of the month representation, such as \"numeric\", \"2-digit\", \"narrow\", \"short\", \"long\"\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.numberingSystem=null] - the numbering system\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @param {string} [opts.outputCalendar='gregory'] - the calendar\n     * @return {Array}\n     */\n    static monthsFormat(\n      length = \"long\",\n      { locale = null, numberingSystem = null, locObj = null, outputCalendar = \"gregory\" } = {}\n    ) {\n      return (locObj || Locale.create(locale, numberingSystem, outputCalendar)).months(length, true);\n    }\n\n    /**\n     * Return an array of standalone week names.\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n     * @param {string} [length='long'] - the length of the weekday representation, such as \"narrow\", \"short\", \"long\".\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @param {string} [opts.numberingSystem=null] - the numbering system\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @example Info.weekdays()[0] //=> 'Monday'\n     * @example Info.weekdays('short')[0] //=> 'Mon'\n     * @example Info.weekdays('short', { locale: 'fr-CA' })[0] //=> 'lun.'\n     * @example Info.weekdays('short', { locale: 'ar' })[0] //=> '\u0627\u0644\u0627\u062b\u0646\u064a\u0646'\n     * @return {Array}\n     */\n    static weekdays(length = \"long\", { locale = null, numberingSystem = null, locObj = null } = {}) {\n      return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length);\n    }\n\n    /**\n     * Return an array of format week names.\n     * Format weekdays differ from standalone weekdays in that they're meant to appear next to more date information. In some languages, that\n     * changes the string.\n     * See {@link Info#weekdays}\n     * @param {string} [length='long'] - the length of the month representation, such as \"narrow\", \"short\", \"long\".\n     * @param {Object} opts - options\n     * @param {string} [opts.locale=null] - the locale code\n     * @param {string} [opts.numberingSystem=null] - the numbering system\n     * @param {string} [opts.locObj=null] - an existing locale object to use\n     * @return {Array}\n     */\n    static weekdaysFormat(\n      length = \"long\",\n      { locale = null, numberingSystem = null, locObj = null } = {}\n    ) {\n      return (locObj || Locale.create(locale, numberingSystem, null)).weekdays(length, true);\n    }\n\n    /**\n     * Return an array of meridiems.\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @example Info.meridiems() //=> [ 'AM', 'PM' ]\n     * @example Info.meridiems({ locale: 'my' }) //=> [ '\u1014\u1036\u1014\u1000\u103a', '\u100a\u1014\u1031' ]\n     * @return {Array}\n     */\n    static meridiems({ locale = null } = {}) {\n      return Locale.create(locale).meridiems();\n    }\n\n    /**\n     * Return an array of eras, such as ['BC', 'AD']. The locale can be specified, but the calendar system is always Gregorian.\n     * @param {string} [length='short'] - the length of the era representation, such as \"short\" or \"long\".\n     * @param {Object} opts - options\n     * @param {string} [opts.locale] - the locale code\n     * @example Info.eras() //=> [ 'BC', 'AD' ]\n     * @example Info.eras('long') //=> [ 'Before Christ', 'Anno Domini' ]\n     * @example Info.eras('long', { locale: 'fr' }) //=> [ 'avant J\u00e9sus-Christ', 'apr\u00e8s J\u00e9sus-Christ' ]\n     * @return {Array}\n     */\n    static eras(length = \"short\", { locale = null } = {}) {\n      return Locale.create(locale, null, \"gregory\").eras(length);\n    }\n\n    /**\n     * Return the set of available features in this environment.\n     * Some features of Luxon are not available in all environments. For example, on older browsers, relative time formatting support is not available. Use this function to figure out if that's the case.\n     * Keys:\n     * * `relative`: whether this environment supports relative time formatting\n     * * `localeWeek`: whether this environment supports different weekdays for the start of the week based on the locale\n     * @example Info.features() //=> { relative: false, localeWeek: true }\n     * @return {Object}\n     */\n    static features() {\n      return { relative: hasRelative(), localeWeek: hasLocaleWeekInfo() };\n    }\n  }\n\n  function dayDiff(earlier, later) {\n    const utcDayStart = (dt) => dt.toUTC(0, { keepLocalTime: true }).startOf(\"day\").valueOf(),\n      ms = utcDayStart(later) - utcDayStart(earlier);\n    return Math.floor(Duration.fromMillis(ms).as(\"days\"));\n  }\n\n  function highOrderDiffs(cursor, later, units) {\n    const differs = [\n      [\"years\", (a, b) => b.year - a.year],\n      [\"quarters\", (a, b) => b.quarter - a.quarter + (b.year - a.year) * 4],\n      [\"months\", (a, b) => b.month - a.month + (b.year - a.year) * 12],\n      [\n        \"weeks\",\n        (a, b) => {\n          const days = dayDiff(a, b);\n          return (days - (days % 7)) / 7;\n        },\n      ],\n      [\"days\", dayDiff],\n    ];\n\n    const results = {};\n    const earlier = cursor;\n    let lowestOrder, highWater;\n\n    /* This loop tries to diff using larger units first.\n       If we overshoot, we backtrack and try the next smaller unit.\n       \"cursor\" starts out at the earlier timestamp and moves closer and closer to \"later\"\n       as we use smaller and smaller units.\n       highWater keeps track of where we would be if we added one more of the smallest unit,\n       this is used later to potentially convert any difference smaller than the smallest higher order unit\n       into a fraction of that smallest higher order unit\n    */\n    for (const [unit, differ] of differs) {\n      if (units.indexOf(unit) >= 0) {\n        lowestOrder = unit;\n\n        results[unit] = differ(cursor, later);\n        highWater = earlier.plus(results);\n\n        if (highWater > later) {\n          // we overshot the end point, backtrack cursor by 1\n          results[unit]--;\n          cursor = earlier.plus(results);\n\n          // if we are still overshooting now, we need to backtrack again\n          // this happens in certain situations when diffing times in different zones,\n          // because this calculation ignores time zones\n          if (cursor > later) {\n            // keep the \"overshot by 1\" around as highWater\n            highWater = cursor;\n            // backtrack cursor by 1\n            results[unit]--;\n            cursor = earlier.plus(results);\n          }\n        } else {\n          cursor = highWater;\n        }\n      }\n    }\n\n    return [cursor, results, highWater, lowestOrder];\n  }\n\n  function diff (earlier, later, units, opts) {\n    let [cursor, results, highWater, lowestOrder] = highOrderDiffs(earlier, later, units);\n\n    const remainingMillis = later - cursor;\n\n    const lowerOrderUnits = units.filter(\n      (u) => [\"hours\", \"minutes\", \"seconds\", \"milliseconds\"].indexOf(u) >= 0\n    );\n\n    if (lowerOrderUnits.length === 0) {\n      if (highWater < later) {\n        highWater = cursor.plus({ [lowestOrder]: 1 });\n      }\n\n      if (highWater !== cursor) {\n        results[lowestOrder] = (results[lowestOrder] || 0) + remainingMillis / (highWater - cursor);\n      }\n    }\n\n    const duration = Duration.fromObject(results, opts);\n\n    if (lowerOrderUnits.length > 0) {\n      return Duration.fromMillis(remainingMillis, opts)\n        .shiftTo(...lowerOrderUnits)\n        .plus(duration);\n    } else {\n      return duration;\n    }\n  }\n\n  const numberingSystems = {\n    arab: \"[\\u0660-\\u0669]\",\n    arabext: \"[\\u06F0-\\u06F9]\",\n    bali: \"[\\u1B50-\\u1B59]\",\n    beng: \"[\\u09E6-\\u09EF]\",\n    deva: \"[\\u0966-\\u096F]\",\n    fullwide: \"[\\uFF10-\\uFF19]\",\n    gujr: \"[\\u0AE6-\\u0AEF]\",\n    hanidec: \"[\u3007|\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u4e03|\u516b|\u4e5d]\",\n    khmr: \"[\\u17E0-\\u17E9]\",\n    knda: \"[\\u0CE6-\\u0CEF]\",\n    laoo: \"[\\u0ED0-\\u0ED9]\",\n    limb: \"[\\u1946-\\u194F]\",\n    mlym: \"[\\u0D66-\\u0D6F]\",\n    mong: \"[\\u1810-\\u1819]\",\n    mymr: \"[\\u1040-\\u1049]\",\n    orya: \"[\\u0B66-\\u0B6F]\",\n    tamldec: \"[\\u0BE6-\\u0BEF]\",\n    telu: \"[\\u0C66-\\u0C6F]\",\n    thai: \"[\\u0E50-\\u0E59]\",\n    tibt: \"[\\u0F20-\\u0F29]\",\n    latn: \"\\\\d\",\n  };\n\n  const numberingSystemsUTF16 = {\n    arab: [1632, 1641],\n    arabext: [1776, 1785],\n    bali: [6992, 7001],\n    beng: [2534, 2543],\n    deva: [2406, 2415],\n    fullwide: [65296, 65303],\n    gujr: [2790, 2799],\n    khmr: [6112, 6121],\n    knda: [3302, 3311],\n    laoo: [3792, 3801],\n    limb: [6470, 6479],\n    mlym: [3430, 3439],\n    mong: [6160, 6169],\n    mymr: [4160, 4169],\n    orya: [2918, 2927],\n    tamldec: [3046, 3055],\n    telu: [3174, 3183],\n    thai: [3664, 3673],\n    tibt: [3872, 3881],\n  };\n\n  const hanidecChars = numberingSystems.hanidec.replace(/[\\[|\\]]/g, \"\").split(\"\");\n\n  function parseDigits(str) {\n    let value = parseInt(str, 10);\n    if (isNaN(value)) {\n      value = \"\";\n      for (let i = 0; i < str.length; i++) {\n        const code = str.charCodeAt(i);\n\n        if (str[i].search(numberingSystems.hanidec) !== -1) {\n          value += hanidecChars.indexOf(str[i]);\n        } else {\n          for (const key in numberingSystemsUTF16) {\n            const [min, max] = numberingSystemsUTF16[key];\n            if (code >= min && code <= max) {\n              value += code - min;\n            }\n          }\n        }\n      }\n      return parseInt(value, 10);\n    } else {\n      return value;\n    }\n  }\n\n  function digitRegex({ numberingSystem }, append = \"\") {\n    return new RegExp(`${numberingSystems[numberingSystem || \"latn\"]}${append}`);\n  }\n\n  const MISSING_FTP = \"missing Intl.DateTimeFormat.formatToParts support\";\n\n  function intUnit(regex, post = (i) => i) {\n    return { regex, deser: ([s]) => post(parseDigits(s)) };\n  }\n\n  const NBSP = String.fromCharCode(160);\n  const spaceOrNBSP = `[ ${NBSP}]`;\n  const spaceOrNBSPRegExp = new RegExp(spaceOrNBSP, \"g\");\n\n  function fixListRegex(s) {\n    // make dots optional and also make them literal\n    // make space and non breakable space characters interchangeable\n    return s.replace(/\\./g, \"\\\\.?\").replace(spaceOrNBSPRegExp, spaceOrNBSP);\n  }\n\n  function stripInsensitivities(s) {\n    return s\n      .replace(/\\./g, \"\") // ignore dots that were made optional\n      .replace(spaceOrNBSPRegExp, \" \") // interchange space and nbsp\n      .toLowerCase();\n  }\n\n  function oneOf(strings, startIndex) {\n    if (strings === null) {\n      return null;\n    } else {\n      return {\n        regex: RegExp(strings.map(fixListRegex).join(\"|\")),\n        deser: ([s]) =>\n          strings.findIndex((i) => stripInsensitivities(s) === stripInsensitivities(i)) + startIndex,\n      };\n    }\n  }\n\n  function offset(regex, groups) {\n    return { regex, deser: ([, h, m]) => signedOffset(h, m), groups };\n  }\n\n  function simple(regex) {\n    return { regex, deser: ([s]) => s };\n  }\n\n  function escapeToken(value) {\n    return value.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, \"\\\\$&\");\n  }\n\n  /**\n   * @param token\n   * @param {Locale} loc\n   */\n  function unitForToken(token, loc) {\n    const one = digitRegex(loc),\n      two = digitRegex(loc, \"{2}\"),\n      three = digitRegex(loc, \"{3}\"),\n      four = digitRegex(loc, \"{4}\"),\n      six = digitRegex(loc, \"{6}\"),\n      oneOrTwo = digitRegex(loc, \"{1,2}\"),\n      oneToThree = digitRegex(loc, \"{1,3}\"),\n      oneToSix = digitRegex(loc, \"{1,6}\"),\n      oneToNine = digitRegex(loc, \"{1,9}\"),\n      twoToFour = digitRegex(loc, \"{2,4}\"),\n      fourToSix = digitRegex(loc, \"{4,6}\"),\n      literal = (t) => ({ regex: RegExp(escapeToken(t.val)), deser: ([s]) => s, literal: true }),\n      unitate = (t) => {\n        if (token.literal) {\n          return literal(t);\n        }\n        switch (t.val) {\n          // era\n          case \"G\":\n            return oneOf(loc.eras(\"short\"), 0);\n          case \"GG\":\n            return oneOf(loc.eras(\"long\"), 0);\n          // years\n          case \"y\":\n            return intUnit(oneToSix);\n          case \"yy\":\n            return intUnit(twoToFour, untruncateYear);\n          case \"yyyy\":\n            return intUnit(four);\n          case \"yyyyy\":\n            return intUnit(fourToSix);\n          case \"yyyyyy\":\n            return intUnit(six);\n          // months\n          case \"M\":\n            return intUnit(oneOrTwo);\n          case \"MM\":\n            return intUnit(two);\n          case \"MMM\":\n            return oneOf(loc.months(\"short\", true), 1);\n          case \"MMMM\":\n            return oneOf(loc.months(\"long\", true), 1);\n          case \"L\":\n            return intUnit(oneOrTwo);\n          case \"LL\":\n            return intUnit(two);\n          case \"LLL\":\n            return oneOf(loc.months(\"short\", false), 1);\n          case \"LLLL\":\n            return oneOf(loc.months(\"long\", false), 1);\n          // dates\n          case \"d\":\n            return intUnit(oneOrTwo);\n          case \"dd\":\n            return intUnit(two);\n          // ordinals\n          case \"o\":\n            return intUnit(oneToThree);\n          case \"ooo\":\n            return intUnit(three);\n          // time\n          case \"HH\":\n            return intUnit(two);\n          case \"H\":\n            return intUnit(oneOrTwo);\n          case \"hh\":\n            return intUnit(two);\n          case \"h\":\n            return intUnit(oneOrTwo);\n          case \"mm\":\n            return intUnit(two);\n          case \"m\":\n            return intUnit(oneOrTwo);\n          case \"q\":\n            return intUnit(oneOrTwo);\n          case \"qq\":\n            return intUnit(two);\n          case \"s\":\n            return intUnit(oneOrTwo);\n          case \"ss\":\n            return intUnit(two);\n          case \"S\":\n            return intUnit(oneToThree);\n          case \"SSS\":\n            return intUnit(three);\n          case \"u\":\n            return simple(oneToNine);\n          case \"uu\":\n            return simple(oneOrTwo);\n          case \"uuu\":\n            return intUnit(one);\n          // meridiem\n          case \"a\":\n            return oneOf(loc.meridiems(), 0);\n          // weekYear (k)\n          case \"kkkk\":\n            return intUnit(four);\n          case \"kk\":\n            return intUnit(twoToFour, untruncateYear);\n          // weekNumber (W)\n          case \"W\":\n            return intUnit(oneOrTwo);\n          case \"WW\":\n            return intUnit(two);\n          // weekdays\n          case \"E\":\n          case \"c\":\n            return intUnit(one);\n          case \"EEE\":\n            return oneOf(loc.weekdays(\"short\", false), 1);\n          case \"EEEE\":\n            return oneOf(loc.weekdays(\"long\", false), 1);\n          case \"ccc\":\n            return oneOf(loc.weekdays(\"short\", true), 1);\n          case \"cccc\":\n            return oneOf(loc.weekdays(\"long\", true), 1);\n          // offset/zone\n          case \"Z\":\n          case \"ZZ\":\n            return offset(new RegExp(`([+-]${oneOrTwo.source})(?::(${two.source}))?`), 2);\n          case \"ZZZ\":\n            return offset(new RegExp(`([+-]${oneOrTwo.source})(${two.source})?`), 2);\n          // we don't support ZZZZ (PST) or ZZZZZ (Pacific Standard Time) in parsing\n          // because we don't have any way to figure out what they are\n          case \"z\":\n            return simple(/[a-z_+-/]{1,256}?/i);\n          // this special-case \"token\" represents a place where a macro-token expanded into a white-space literal\n          // in this case we accept any non-newline white-space\n          case \" \":\n            return simple(/[^\\S\\n\\r]/);\n          default:\n            return literal(t);\n        }\n      };\n\n    const unit = unitate(token) || {\n      invalidReason: MISSING_FTP,\n    };\n\n    unit.token = token;\n\n    return unit;\n  }\n\n  const partTypeStyleToTokenVal = {\n    year: {\n      \"2-digit\": \"yy\",\n      numeric: \"yyyyy\",\n    },\n    month: {\n      numeric: \"M\",\n      \"2-digit\": \"MM\",\n      short: \"MMM\",\n      long: \"MMMM\",\n    },\n    day: {\n      numeric: \"d\",\n      \"2-digit\": \"dd\",\n    },\n    weekday: {\n      short: \"EEE\",\n      long: \"EEEE\",\n    },\n    dayperiod: \"a\",\n    dayPeriod: \"a\",\n    hour12: {\n      numeric: \"h\",\n      \"2-digit\": \"hh\",\n    },\n    hour24: {\n      numeric: \"H\",\n      \"2-digit\": \"HH\",\n    },\n    minute: {\n      numeric: \"m\",\n      \"2-digit\": \"mm\",\n    },\n    second: {\n      numeric: \"s\",\n      \"2-digit\": \"ss\",\n    },\n    timeZoneName: {\n      long: \"ZZZZZ\",\n      short: \"ZZZ\",\n    },\n  };\n\n  function tokenForPart(part, formatOpts, resolvedOpts) {\n    const { type, value } = part;\n\n    if (type === \"literal\") {\n      const isSpace = /^\\s+$/.test(value);\n      return {\n        literal: !isSpace,\n        val: isSpace ? \" \" : value,\n      };\n    }\n\n    const style = formatOpts[type];\n\n    // The user might have explicitly specified hour12 or hourCycle\n    // if so, respect their decision\n    // if not, refer back to the resolvedOpts, which are based on the locale\n    let actualType = type;\n    if (type === \"hour\") {\n      if (formatOpts.hour12 != null) {\n        actualType = formatOpts.hour12 ? \"hour12\" : \"hour24\";\n      } else if (formatOpts.hourCycle != null) {\n        if (formatOpts.hourCycle === \"h11\" || formatOpts.hourCycle === \"h12\") {\n          actualType = \"hour12\";\n        } else {\n          actualType = \"hour24\";\n        }\n      } else {\n        // tokens only differentiate between 24 hours or not,\n        // so we do not need to check hourCycle here, which is less supported anyways\n        actualType = resolvedOpts.hour12 ? \"hour12\" : \"hour24\";\n      }\n    }\n    let val = partTypeStyleToTokenVal[actualType];\n    if (typeof val === \"object\") {\n      val = val[style];\n    }\n\n    if (val) {\n      return {\n        literal: false,\n        val,\n      };\n    }\n\n    return undefined;\n  }\n\n  function buildRegex(units) {\n    const re = units.map((u) => u.regex).reduce((f, r) => `${f}(${r.source})`, \"\");\n    return [`^${re}$`, units];\n  }\n\n  function match(input, regex, handlers) {\n    const matches = input.match(regex);\n\n    if (matches) {\n      const all = {};\n      let matchIndex = 1;\n      for (const i in handlers) {\n        if (hasOwnProperty(handlers, i)) {\n          const h = handlers[i],\n            groups = h.groups ? h.groups + 1 : 1;\n          if (!h.literal && h.token) {\n            all[h.token.val[0]] = h.deser(matches.slice(matchIndex, matchIndex + groups));\n          }\n          matchIndex += groups;\n        }\n      }\n      return [matches, all];\n    } else {\n      return [matches, {}];\n    }\n  }\n\n  function dateTimeFromMatches(matches) {\n    const toField = (token) => {\n      switch (token) {\n        case \"S\":\n          return \"millisecond\";\n        case \"s\":\n          return \"second\";\n        case \"m\":\n          return \"minute\";\n        case \"h\":\n        case \"H\":\n          return \"hour\";\n        case \"d\":\n          return \"day\";\n        case \"o\":\n          return \"ordinal\";\n        case \"L\":\n        case \"M\":\n          return \"month\";\n        case \"y\":\n          return \"year\";\n        case \"E\":\n        case \"c\":\n          return \"weekday\";\n        case \"W\":\n          return \"weekNumber\";\n        case \"k\":\n          return \"weekYear\";\n        case \"q\":\n          return \"quarter\";\n        default:\n          return null;\n      }\n    };\n\n    let zone = null;\n    let specificOffset;\n    if (!isUndefined(matches.z)) {\n      zone = IANAZone.create(matches.z);\n    }\n\n    if (!isUndefined(matches.Z)) {\n      if (!zone) {\n        zone = new FixedOffsetZone(matches.Z);\n      }\n      specificOffset = matches.Z;\n    }\n\n    if (!isUndefined(matches.q)) {\n      matches.M = (matches.q - 1) * 3 + 1;\n    }\n\n    if (!isUndefined(matches.h)) {\n      if (matches.h < 12 && matches.a === 1) {\n        matches.h += 12;\n      } else if (matches.h === 12 && matches.a === 0) {\n        matches.h = 0;\n      }\n    }\n\n    if (matches.G === 0 && matches.y) {\n      matches.y = -matches.y;\n    }\n\n    if (!isUndefined(matches.u)) {\n      matches.S = parseMillis(matches.u);\n    }\n\n    const vals = Object.keys(matches).reduce((r, k) => {\n      const f = toField(k);\n      if (f) {\n        r[f] = matches[k];\n      }\n\n      return r;\n    }, {});\n\n    return [vals, zone, specificOffset];\n  }\n\n  let dummyDateTimeCache = null;\n\n  function getDummyDateTime() {\n    if (!dummyDateTimeCache) {\n      dummyDateTimeCache = DateTime.fromMillis(1555555555555);\n    }\n\n    return dummyDateTimeCache;\n  }\n\n  function maybeExpandMacroToken(token, locale) {\n    if (token.literal) {\n      return token;\n    }\n\n    const formatOpts = Formatter.macroTokenToFormatOpts(token.val);\n    const tokens = formatOptsToTokens(formatOpts, locale);\n\n    if (tokens == null || tokens.includes(undefined)) {\n      return token;\n    }\n\n    return tokens;\n  }\n\n  function expandMacroTokens(tokens, locale) {\n    return Array.prototype.concat(...tokens.map((t) => maybeExpandMacroToken(t, locale)));\n  }\n\n  /**\n   * @private\n   */\n\n  function explainFromTokens(locale, input, format) {\n    const tokens = expandMacroTokens(Formatter.parseFormat(format), locale),\n      units = tokens.map((t) => unitForToken(t, locale)),\n      disqualifyingUnit = units.find((t) => t.invalidReason);\n\n    if (disqualifyingUnit) {\n      return { input, tokens, invalidReason: disqualifyingUnit.invalidReason };\n    } else {\n      const [regexString, handlers] = buildRegex(units),\n        regex = RegExp(regexString, \"i\"),\n        [rawMatches, matches] = match(input, regex, handlers),\n        [result, zone, specificOffset] = matches\n          ? dateTimeFromMatches(matches)\n          : [null, null, undefined];\n      if (hasOwnProperty(matches, \"a\") && hasOwnProperty(matches, \"H\")) {\n        throw new ConflictingSpecificationError(\n          \"Can't include meridiem when specifying 24-hour format\"\n        );\n      }\n      return { input, tokens, regex, rawMatches, matches, result, zone, specificOffset };\n    }\n  }\n\n  function parseFromTokens(locale, input, format) {\n    const { result, zone, specificOffset, invalidReason } = explainFromTokens(locale, input, format);\n    return [result, zone, specificOffset, invalidReason];\n  }\n\n  function formatOptsToTokens(formatOpts, locale) {\n    if (!formatOpts) {\n      return null;\n    }\n\n    const formatter = Formatter.create(locale, formatOpts);\n    const df = formatter.dtFormatter(getDummyDateTime());\n    const parts = df.formatToParts();\n    const resolvedOpts = df.resolvedOptions();\n    return parts.map((p) => tokenForPart(p, formatOpts, resolvedOpts));\n  }\n\n  const INVALID = \"Invalid DateTime\";\n  const MAX_DATE = 8.64e15;\n\n  function unsupportedZone(zone) {\n    return new Invalid(\"unsupported zone\", `the zone \"${zone.name}\" is not supported`);\n  }\n\n  // we cache week data on the DT object and this intermediates the cache\n  /**\n   * @param {DateTime} dt\n   */\n  function possiblyCachedWeekData(dt) {\n    if (dt.weekData === null) {\n      dt.weekData = gregorianToWeek(dt.c);\n    }\n    return dt.weekData;\n  }\n\n  /**\n   * @param {DateTime} dt\n   */\n  function possiblyCachedLocalWeekData(dt) {\n    if (dt.localWeekData === null) {\n      dt.localWeekData = gregorianToWeek(\n        dt.c,\n        dt.loc.getMinDaysInFirstWeek(),\n        dt.loc.getStartOfWeek()\n      );\n    }\n    return dt.localWeekData;\n  }\n\n  // clone really means, \"make a new object with these modifications\". all \"setters\" really use this\n  // to create a new object while only changing some of the properties\n  function clone(inst, alts) {\n    const current = {\n      ts: inst.ts,\n      zone: inst.zone,\n      c: inst.c,\n      o: inst.o,\n      loc: inst.loc,\n      invalid: inst.invalid,\n    };\n    return new DateTime({ ...current, ...alts, old: current });\n  }\n\n  // find the right offset a given local time. The o input is our guess, which determines which\n  // offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)\n  function fixOffset(localTS, o, tz) {\n    // Our UTC time is just a guess because our offset is just a guess\n    let utcGuess = localTS - o * 60 * 1000;\n\n    // Test whether the zone matches the offset for this ts\n    const o2 = tz.offset(utcGuess);\n\n    // If so, offset didn't change and we're done\n    if (o === o2) {\n      return [utcGuess, o];\n    }\n\n    // If not, change the ts by the difference in the offset\n    utcGuess -= (o2 - o) * 60 * 1000;\n\n    // If that gives us the local time we want, we're done\n    const o3 = tz.offset(utcGuess);\n    if (o2 === o3) {\n      return [utcGuess, o2];\n    }\n\n    // If it's different, we're in a hole time. The offset has changed, but the we don't adjust the time\n    return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];\n  }\n\n  // convert an epoch timestamp into a calendar object with the given offset\n  function tsToObj(ts, offset) {\n    ts += offset * 60 * 1000;\n\n    const d = new Date(ts);\n\n    return {\n      year: d.getUTCFullYear(),\n      month: d.getUTCMonth() + 1,\n      day: d.getUTCDate(),\n      hour: d.getUTCHours(),\n      minute: d.getUTCMinutes(),\n      second: d.getUTCSeconds(),\n      millisecond: d.getUTCMilliseconds(),\n    };\n  }\n\n  // convert a calendar object to a epoch timestamp\n  function objToTS(obj, offset, zone) {\n    return fixOffset(objToLocalTS(obj), offset, zone);\n  }\n\n  // create a new DT instance by adding a duration, adjusting for DSTs\n  function adjustTime(inst, dur) {\n    const oPre = inst.o,\n      year = inst.c.year + Math.trunc(dur.years),\n      month = inst.c.month + Math.trunc(dur.months) + Math.trunc(dur.quarters) * 3,\n      c = {\n        ...inst.c,\n        year,\n        month,\n        day:\n          Math.min(inst.c.day, daysInMonth(year, month)) +\n          Math.trunc(dur.days) +\n          Math.trunc(dur.weeks) * 7,\n      },\n      millisToAdd = Duration.fromObject({\n        years: dur.years - Math.trunc(dur.years),\n        quarters: dur.quarters - Math.trunc(dur.quarters),\n        months: dur.months - Math.trunc(dur.months),\n        weeks: dur.weeks - Math.trunc(dur.weeks),\n        days: dur.days - Math.trunc(dur.days),\n        hours: dur.hours,\n        minutes: dur.minutes,\n        seconds: dur.seconds,\n        milliseconds: dur.milliseconds,\n      }).as(\"milliseconds\"),\n      localTS = objToLocalTS(c);\n\n    let [ts, o] = fixOffset(localTS, oPre, inst.zone);\n\n    if (millisToAdd !== 0) {\n      ts += millisToAdd;\n      // that could have changed the offset by going over a DST, but we want to keep the ts the same\n      o = inst.zone.offset(ts);\n    }\n\n    return { ts, o };\n  }\n\n  // helper useful in turning the results of parsing into real dates\n  // by handling the zone options\n  function parseDataToDateTime(parsed, parsedZone, opts, format, text, specificOffset) {\n    const { setZone, zone } = opts;\n    if ((parsed && Object.keys(parsed).length !== 0) || parsedZone) {\n      const interpretationZone = parsedZone || zone,\n        inst = DateTime.fromObject(parsed, {\n          ...opts,\n          zone: interpretationZone,\n          specificOffset,\n        });\n      return setZone ? inst : inst.setZone(zone);\n    } else {\n      return DateTime.invalid(\n        new Invalid(\"unparsable\", `the input \"${text}\" can't be parsed as ${format}`)\n      );\n    }\n  }\n\n  // if you want to output a technical format (e.g. RFC 2822), this helper\n  // helps handle the details\n  function toTechFormat(dt, format, allowZ = true) {\n    return dt.isValid\n      ? Formatter.create(Locale.create(\"en-US\"), {\n          allowZ,\n          forceSimple: true,\n        }).formatDateTimeFromString(dt, format)\n      : null;\n  }\n\n  function toISODate(o, extended) {\n    const longFormat = o.c.year > 9999 || o.c.year < 0;\n    let c = \"\";\n    if (longFormat && o.c.year >= 0) c += \"+\";\n    c += padStart(o.c.year, longFormat ? 6 : 4);\n\n    if (extended) {\n      c += \"-\";\n      c += padStart(o.c.month);\n      c += \"-\";\n      c += padStart(o.c.day);\n    } else {\n      c += padStart(o.c.month);\n      c += padStart(o.c.day);\n    }\n    return c;\n  }\n\n  function toISOTime(\n    o,\n    extended,\n    suppressSeconds,\n    suppressMilliseconds,\n    includeOffset,\n    extendedZone\n  ) {\n    let c = padStart(o.c.hour);\n    if (extended) {\n      c += \":\";\n      c += padStart(o.c.minute);\n      if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {\n        c += \":\";\n      }\n    } else {\n      c += padStart(o.c.minute);\n    }\n\n    if (o.c.millisecond !== 0 || o.c.second !== 0 || !suppressSeconds) {\n      c += padStart(o.c.second);\n\n      if (o.c.millisecond !== 0 || !suppressMilliseconds) {\n        c += \".\";\n        c += padStart(o.c.millisecond, 3);\n      }\n    }\n\n    if (includeOffset) {\n      if (o.isOffsetFixed && o.offset === 0 && !extendedZone) {\n        c += \"Z\";\n      } else if (o.o < 0) {\n        c += \"-\";\n        c += padStart(Math.trunc(-o.o / 60));\n        c += \":\";\n        c += padStart(Math.trunc(-o.o % 60));\n      } else {\n        c += \"+\";\n        c += padStart(Math.trunc(o.o / 60));\n        c += \":\";\n        c += padStart(Math.trunc(o.o % 60));\n      }\n    }\n\n    if (extendedZone) {\n      c += \"[\" + o.zone.ianaName + \"]\";\n    }\n    return c;\n  }\n\n  // defaults for unspecified units in the supported calendars\n  const defaultUnitValues = {\n      month: 1,\n      day: 1,\n      hour: 0,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    defaultWeekUnitValues = {\n      weekNumber: 1,\n      weekday: 1,\n      hour: 0,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    },\n    defaultOrdinalUnitValues = {\n      ordinal: 1,\n      hour: 0,\n      minute: 0,\n      second: 0,\n      millisecond: 0,\n    };\n\n  // Units in the supported calendars, sorted by bigness\n  const orderedUnits = [\"year\", \"month\", \"day\", \"hour\", \"minute\", \"second\", \"millisecond\"],\n    orderedWeekUnits = [\n      \"weekYear\",\n      \"weekNumber\",\n      \"weekday\",\n      \"hour\",\n      \"minute\",\n      \"second\",\n      \"millisecond\",\n    ],\n    orderedOrdinalUnits = [\"year\", \"ordinal\", \"hour\", \"minute\", \"second\", \"millisecond\"];\n\n  // standardize case and plurality in units\n  function normalizeUnit(unit) {\n    const normalized = {\n      year: \"year\",\n      years: \"year\",\n      month: \"month\",\n      months: \"month\",\n      day: \"day\",\n      days: \"day\",\n      hour: \"hour\",\n      hours: \"hour\",\n      minute: \"minute\",\n      minutes: \"minute\",\n      quarter: \"quarter\",\n      quarters: \"quarter\",\n      second: \"second\",\n      seconds: \"second\",\n      millisecond: \"millisecond\",\n      milliseconds: \"millisecond\",\n      weekday: \"weekday\",\n      weekdays: \"weekday\",\n      weeknumber: \"weekNumber\",\n      weeksnumber: \"weekNumber\",\n      weeknumbers: \"weekNumber\",\n      weekyear: \"weekYear\",\n      weekyears: \"weekYear\",\n      ordinal: \"ordinal\",\n    }[unit.toLowerCase()];\n\n    if (!normalized) throw new InvalidUnitError(unit);\n\n    return normalized;\n  }\n\n  function normalizeUnitWithLocalWeeks(unit) {\n    switch (unit.toLowerCase()) {\n      case \"localweekday\":\n      case \"localweekdays\":\n        return \"localWeekday\";\n      case \"localweeknumber\":\n      case \"localweeknumbers\":\n        return \"localWeekNumber\";\n      case \"localweekyear\":\n      case \"localweekyears\":\n        return \"localWeekYear\";\n      default:\n        return normalizeUnit(unit);\n    }\n  }\n\n  // this is a dumbed down version of fromObject() that runs about 60% faster\n  // but doesn't do any validation, makes a bunch of assumptions about what units\n  // are present, and so on.\n  function quickDT(obj, opts) {\n    const zone = normalizeZone(opts.zone, Settings.defaultZone),\n      loc = Locale.fromObject(opts),\n      tsNow = Settings.now();\n\n    let ts, o;\n\n    // assume we have the higher-order units\n    if (!isUndefined(obj.year)) {\n      for (const u of orderedUnits) {\n        if (isUndefined(obj[u])) {\n          obj[u] = defaultUnitValues[u];\n        }\n      }\n\n      const invalid = hasInvalidGregorianData(obj) || hasInvalidTimeData(obj);\n      if (invalid) {\n        return DateTime.invalid(invalid);\n      }\n\n      const offsetProvis = zone.offset(tsNow);\n      [ts, o] = objToTS(obj, offsetProvis, zone);\n    } else {\n      ts = tsNow;\n    }\n\n    return new DateTime({ ts, zone, loc, o });\n  }\n\n  function diffRelative(start, end, opts) {\n    const round = isUndefined(opts.round) ? true : opts.round,\n      format = (c, unit) => {\n        c = roundTo(c, round || opts.calendary ? 0 : 2, true);\n        const formatter = end.loc.clone(opts).relFormatter(opts);\n        return formatter.format(c, unit);\n      },\n      differ = (unit) => {\n        if (opts.calendary) {\n          if (!end.hasSame(start, unit)) {\n            return end.startOf(unit).diff(start.startOf(unit), unit).get(unit);\n          } else return 0;\n        } else {\n          return end.diff(start, unit).get(unit);\n        }\n      };\n\n    if (opts.unit) {\n      return format(differ(opts.unit), opts.unit);\n    }\n\n    for (const unit of opts.units) {\n      const count = differ(unit);\n      if (Math.abs(count) >= 1) {\n        return format(count, unit);\n      }\n    }\n    return format(start > end ? -0 : 0, opts.units[opts.units.length - 1]);\n  }\n\n  function lastOpts(argList) {\n    let opts = {},\n      args;\n    if (argList.length > 0 && typeof argList[argList.length - 1] === \"object\") {\n      opts = argList[argList.length - 1];\n      args = Array.from(argList).slice(0, argList.length - 1);\n    } else {\n      args = Array.from(argList);\n    }\n    return [opts, args];\n  }\n\n  /**\n   * A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.\n   *\n   * A DateTime comprises of:\n   * * A timestamp. Each DateTime instance refers to a specific millisecond of the Unix epoch.\n   * * A time zone. Each instance is considered in the context of a specific zone (by default the local system's zone).\n   * * Configuration properties that effect how output strings are formatted, such as `locale`, `numberingSystem`, and `outputCalendar`.\n   *\n   * Here is a brief overview of the most commonly used functionality it provides:\n   *\n   * * **Creation**: To create a DateTime from its components, use one of its factory class methods: {@link DateTime.local}, {@link DateTime.utc}, and (most flexibly) {@link DateTime.fromObject}. To create one from a standard string format, use {@link DateTime.fromISO}, {@link DateTime.fromHTTP}, and {@link DateTime.fromRFC2822}. To create one from a custom string format, use {@link DateTime.fromFormat}. To create one from a native JS date, use {@link DateTime.fromJSDate}.\n   * * **Gregorian calendar and time**: To examine the Gregorian properties of a DateTime individually (i.e as opposed to collectively through {@link DateTime#toObject}), use the {@link DateTime#year}, {@link DateTime#month},\n   * {@link DateTime#day}, {@link DateTime#hour}, {@link DateTime#minute}, {@link DateTime#second}, {@link DateTime#millisecond} accessors.\n   * * **Week calendar**: For ISO week calendar attributes, see the {@link DateTime#weekYear}, {@link DateTime#weekNumber}, and {@link DateTime#weekday} accessors.\n   * * **Configuration** See the {@link DateTime#locale} and {@link DateTime#numberingSystem} accessors.\n   * * **Transformation**: To transform the DateTime into other DateTimes, use {@link DateTime#set}, {@link DateTime#reconfigure}, {@link DateTime#setZone}, {@link DateTime#setLocale}, {@link DateTime.plus}, {@link DateTime#minus}, {@link DateTime#endOf}, {@link DateTime#startOf}, {@link DateTime#toUTC}, and {@link DateTime#toLocal}.\n   * * **Output**: To convert the DateTime to other representations, use the {@link DateTime#toRelative}, {@link DateTime#toRelativeCalendar}, {@link DateTime#toJSON}, {@link DateTime#toISO}, {@link DateTime#toHTTP}, {@link DateTime#toObject}, {@link DateTime#toRFC2822}, {@link DateTime#toString}, {@link DateTime#toLocaleString}, {@link DateTime#toFormat}, {@link DateTime#toMillis} and {@link DateTime#toJSDate}.\n   *\n   * There's plenty others documented below. In addition, for more information on subtler topics like internationalization, time zones, alternative calendars, validity, and so on, see the external documentation.\n   */\n  class DateTime {\n    /**\n     * @access private\n     */\n    constructor(config) {\n      const zone = config.zone || Settings.defaultZone;\n\n      let invalid =\n        config.invalid ||\n        (Number.isNaN(config.ts) ? new Invalid(\"invalid input\") : null) ||\n        (!zone.isValid ? unsupportedZone(zone) : null);\n      /**\n       * @access private\n       */\n      this.ts = isUndefined(config.ts) ? Settings.now() : config.ts;\n\n      let c = null,\n        o = null;\n      if (!invalid) {\n        const unchanged = config.old && config.old.ts === this.ts && config.old.zone.equals(zone);\n\n        if (unchanged) {\n          [c, o] = [config.old.c, config.old.o];\n        } else {\n          const ot = zone.offset(this.ts);\n          c = tsToObj(this.ts, ot);\n          invalid = Number.isNaN(c.year) ? new Invalid(\"invalid input\") : null;\n          c = invalid ? null : c;\n          o = invalid ? null : ot;\n        }\n      }\n\n      /**\n       * @access private\n       */\n      this._zone = zone;\n      /**\n       * @access private\n       */\n      this.loc = config.loc || Locale.create();\n      /**\n       * @access private\n       */\n      this.invalid = invalid;\n      /**\n       * @access private\n       */\n      this.weekData = null;\n      /**\n       * @access private\n       */\n      this.localWeekData = null;\n      /**\n       * @access private\n       */\n      this.c = c;\n      /**\n       * @access private\n       */\n      this.o = o;\n      /**\n       * @access private\n       */\n      this.isLuxonDateTime = true;\n    }\n\n    // CONSTRUCT\n\n    /**\n     * Create a DateTime for the current instant, in the system's time zone.\n     *\n     * Use Settings to override these default values if needed.\n     * @example DateTime.now().toISO() //~> now in the ISO format\n     * @return {DateTime}\n     */\n    static now() {\n      return new DateTime({});\n    }\n\n    /**\n     * Create a local DateTime\n     * @param {number} [year] - The calendar year. If omitted (as in, call `local()` with no arguments), the current time will be used\n     * @param {number} [month=1] - The month, 1-indexed\n     * @param {number} [day=1] - The day of the month, 1-indexed\n     * @param {number} [hour=0] - The hour of the day, in 24-hour time\n     * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59\n     * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59\n     * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999\n     * @example DateTime.local()                                  //~> now\n     * @example DateTime.local({ zone: \"America/New_York\" })      //~> now, in US east coast time\n     * @example DateTime.local(2017)                              //~> 2017-01-01T00:00:00\n     * @example DateTime.local(2017, 3)                           //~> 2017-03-01T00:00:00\n     * @example DateTime.local(2017, 3, 12, { locale: \"fr\" })     //~> 2017-03-12T00:00:00, with a French locale\n     * @example DateTime.local(2017, 3, 12, 5)                    //~> 2017-03-12T05:00:00\n     * @example DateTime.local(2017, 3, 12, 5, { zone: \"utc\" })   //~> 2017-03-12T05:00:00, in UTC\n     * @example DateTime.local(2017, 3, 12, 5, 45)                //~> 2017-03-12T05:45:00\n     * @example DateTime.local(2017, 3, 12, 5, 45, 10)            //~> 2017-03-12T05:45:10\n     * @example DateTime.local(2017, 3, 12, 5, 45, 10, 765)       //~> 2017-03-12T05:45:10.765\n     * @return {DateTime}\n     */\n    static local() {\n      const [opts, args] = lastOpts(arguments),\n        [year, month, day, hour, minute, second, millisecond] = args;\n      return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);\n    }\n\n    /**\n     * Create a DateTime in UTC\n     * @param {number} [year] - The calendar year. If omitted (as in, call `utc()` with no arguments), the current time will be used\n     * @param {number} [month=1] - The month, 1-indexed\n     * @param {number} [day=1] - The day of the month\n     * @param {number} [hour=0] - The hour of the day, in 24-hour time\n     * @param {number} [minute=0] - The minute of the hour, meaning a number between 0 and 59\n     * @param {number} [second=0] - The second of the minute, meaning a number between 0 and 59\n     * @param {number} [millisecond=0] - The millisecond of the second, meaning a number between 0 and 999\n     * @param {Object} options - configuration options for the DateTime\n     * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n     * @param {string} [options.outputCalendar] - the output calendar to set on the resulting DateTime instance\n     * @param {string} [options.numberingSystem] - the numbering system to set on the resulting DateTime instance\n     * @example DateTime.utc()                                              //~> now\n     * @example DateTime.utc(2017)                                          //~> 2017-01-01T00:00:00Z\n     * @example DateTime.utc(2017, 3)                                       //~> 2017-03-01T00:00:00Z\n     * @example DateTime.utc(2017, 3, 12)                                   //~> 2017-03-12T00:00:00Z\n     * @example DateTime.utc(2017, 3, 12, 5)                                //~> 2017-03-12T05:00:00Z\n     * @example DateTime.utc(2017, 3, 12, 5, 45)                            //~> 2017-03-12T05:45:00Z\n     * @example DateTime.utc(2017, 3, 12, 5, 45, { locale: \"fr\" })          //~> 2017-03-12T05:45:00Z with a French locale\n     * @example DateTime.utc(2017, 3, 12, 5, 45, 10)                        //~> 2017-03-12T05:45:10Z\n     * @example DateTime.utc(2017, 3, 12, 5, 45, 10, 765, { locale: \"fr\" }) //~> 2017-03-12T05:45:10.765Z with a French locale\n     * @return {DateTime}\n     */\n    static utc() {\n      const [opts, args] = lastOpts(arguments),\n        [year, month, day, hour, minute, second, millisecond] = args;\n\n      opts.zone = FixedOffsetZone.utcInstance;\n      return quickDT({ year, month, day, hour, minute, second, millisecond }, opts);\n    }\n\n    /**\n     * Create a DateTime from a JavaScript Date object. Uses the default zone.\n     * @param {Date} date - a JavaScript Date object\n     * @param {Object} options - configuration options for the DateTime\n     * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n     * @return {DateTime}\n     */\n    static fromJSDate(date, options = {}) {\n      const ts = isDate(date) ? date.valueOf() : NaN;\n      if (Number.isNaN(ts)) {\n        return DateTime.invalid(\"invalid input\");\n      }\n\n      const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);\n      if (!zoneToUse.isValid) {\n        return DateTime.invalid(unsupportedZone(zoneToUse));\n      }\n\n      return new DateTime({\n        ts: ts,\n        zone: zoneToUse,\n        loc: Locale.fromObject(options),\n      });\n    }\n\n    /**\n     * Create a DateTime from a number of milliseconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.\n     * @param {number} milliseconds - a number of milliseconds since 1970 UTC\n     * @param {Object} options - configuration options for the DateTime\n     * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n     * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n     * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance\n     * @return {DateTime}\n     */\n    static fromMillis(milliseconds, options = {}) {\n      if (!isNumber(milliseconds)) {\n        throw new InvalidArgumentError(\n          `fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`\n        );\n      } else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {\n        // this isn't perfect because because we can still end up out of range because of additional shifting, but it's a start\n        return DateTime.invalid(\"Timestamp out of range\");\n      } else {\n        return new DateTime({\n          ts: milliseconds,\n          zone: normalizeZone(options.zone, Settings.defaultZone),\n          loc: Locale.fromObject(options),\n        });\n      }\n    }\n\n    /**\n     * Create a DateTime from a number of seconds since the epoch (meaning since 1 January 1970 00:00:00 UTC). Uses the default zone.\n     * @param {number} seconds - a number of seconds since 1970 UTC\n     * @param {Object} options - configuration options for the DateTime\n     * @param {string|Zone} [options.zone='local'] - the zone to place the DateTime into\n     * @param {string} [options.locale] - a locale to set on the resulting DateTime instance\n     * @param {string} options.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @param {string} options.numberingSystem - the numbering system to set on the resulting DateTime instance\n     * @return {DateTime}\n     */\n    static fromSeconds(seconds, options = {}) {\n      if (!isNumber(seconds)) {\n        throw new InvalidArgumentError(\"fromSeconds requires a numerical input\");\n      } else {\n        return new DateTime({\n          ts: seconds * 1000,\n          zone: normalizeZone(options.zone, Settings.defaultZone),\n          loc: Locale.fromObject(options),\n        });\n      }\n    }\n\n    /**\n     * Create a DateTime from a JavaScript object with keys like 'year' and 'hour' with reasonable defaults.\n     * @param {Object} obj - the object to create the DateTime from\n     * @param {number} obj.year - a year, such as 1987\n     * @param {number} obj.month - a month, 1-12\n     * @param {number} obj.day - a day of the month, 1-31, depending on the month\n     * @param {number} obj.ordinal - day of the year, 1-365 or 366\n     * @param {number} obj.weekYear - an ISO week year\n     * @param {number} obj.weekNumber - an ISO week number, between 1 and 52 or 53, depending on the year\n     * @param {number} obj.weekday - an ISO weekday, 1-7, where 1 is Monday and 7 is Sunday\n     * @param {number} obj.localWeekYear - a week year, according to the locale\n     * @param {number} obj.localWeekNumber - a week number, between 1 and 52 or 53, depending on the year, according to the locale\n     * @param {number} obj.localWeekday - a weekday, 1-7, where 1 is the first and 7 is the last day of the week, according to the locale\n     * @param {number} obj.hour - hour of the day, 0-23\n     * @param {number} obj.minute - minute of the hour, 0-59\n     * @param {number} obj.second - second of the minute, 0-59\n     * @param {number} obj.millisecond - millisecond of the second, 0-999\n     * @param {Object} opts - options for creating this DateTime\n     * @param {string|Zone} [opts.zone='local'] - interpret the numbers in the context of a particular zone. Can take any value taken as the first argument to setZone()\n     * @param {string} [opts.locale='system\\'s locale'] - a locale to set on the resulting DateTime instance\n     * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n     * @example DateTime.fromObject({ year: 1982, month: 5, day: 25}).toISODate() //=> '1982-05-25'\n     * @example DateTime.fromObject({ year: 1982 }).toISODate() //=> '1982-01-01'\n     * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }) //~> today at 10:26:06\n     * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'utc' }),\n     * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'local' })\n     * @example DateTime.fromObject({ hour: 10, minute: 26, second: 6 }, { zone: 'America/New_York' })\n     * @example DateTime.fromObject({ weekYear: 2016, weekNumber: 2, weekday: 3 }).toISODate() //=> '2016-01-13'\n     * @example DateTime.fromObject({ localWeekYear: 2022, localWeekNumber: 1, localWeekday: 1 }, { locale: \"en-US\" }).toISODate() //=> '2021-12-26'\n     * @return {DateTime}\n     */\n    static fromObject(obj, opts = {}) {\n      obj = obj || {};\n      const zoneToUse = normalizeZone(opts.zone, Settings.defaultZone);\n      if (!zoneToUse.isValid) {\n        return DateTime.invalid(unsupportedZone(zoneToUse));\n      }\n\n      const loc = Locale.fromObject(opts);\n      const normalized = normalizeObject(obj, normalizeUnitWithLocalWeeks);\n      const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, loc);\n\n      const tsNow = Settings.now(),\n        offsetProvis = !isUndefined(opts.specificOffset)\n          ? opts.specificOffset\n          : zoneToUse.offset(tsNow),\n        containsOrdinal = !isUndefined(normalized.ordinal),\n        containsGregorYear = !isUndefined(normalized.year),\n        containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),\n        containsGregor = containsGregorYear || containsGregorMD,\n        definiteWeekDef = normalized.weekYear || normalized.weekNumber;\n\n      // cases:\n      // just a weekday -> this week's instance of that weekday, no worries\n      // (gregorian data or ordinal) + (weekYear or weekNumber) -> error\n      // (gregorian month or day) + ordinal -> error\n      // otherwise just use weeks or ordinals or gregorian, depending on what's specified\n\n      if ((containsGregor || containsOrdinal) && definiteWeekDef) {\n        throw new ConflictingSpecificationError(\n          \"Can't mix weekYear/weekNumber units with year/month/day or ordinals\"\n        );\n      }\n\n      if (containsGregorMD && containsOrdinal) {\n        throw new ConflictingSpecificationError(\"Can't mix ordinal dates with month/day\");\n      }\n\n      const useWeekData = definiteWeekDef || (normalized.weekday && !containsGregor);\n\n      // configure ourselves to deal with gregorian dates or week stuff\n      let units,\n        defaultValues,\n        objNow = tsToObj(tsNow, offsetProvis);\n      if (useWeekData) {\n        units = orderedWeekUnits;\n        defaultValues = defaultWeekUnitValues;\n        objNow = gregorianToWeek(objNow, minDaysInFirstWeek, startOfWeek);\n      } else if (containsOrdinal) {\n        units = orderedOrdinalUnits;\n        defaultValues = defaultOrdinalUnitValues;\n        objNow = gregorianToOrdinal(objNow);\n      } else {\n        units = orderedUnits;\n        defaultValues = defaultUnitValues;\n      }\n\n      // set default values for missing stuff\n      let foundFirst = false;\n      for (const u of units) {\n        const v = normalized[u];\n        if (!isUndefined(v)) {\n          foundFirst = true;\n        } else if (foundFirst) {\n          normalized[u] = defaultValues[u];\n        } else {\n          normalized[u] = objNow[u];\n        }\n      }\n\n      // make sure the values we have are in range\n      const higherOrderInvalid = useWeekData\n          ? hasInvalidWeekData(normalized, minDaysInFirstWeek, startOfWeek)\n          : containsOrdinal\n          ? hasInvalidOrdinalData(normalized)\n          : hasInvalidGregorianData(normalized),\n        invalid = higherOrderInvalid || hasInvalidTimeData(normalized);\n\n      if (invalid) {\n        return DateTime.invalid(invalid);\n      }\n\n      // compute the actual time\n      const gregorian = useWeekData\n          ? weekToGregorian(normalized, minDaysInFirstWeek, startOfWeek)\n          : containsOrdinal\n          ? ordinalToGregorian(normalized)\n          : normalized,\n        [tsFinal, offsetFinal] = objToTS(gregorian, offsetProvis, zoneToUse),\n        inst = new DateTime({\n          ts: tsFinal,\n          zone: zoneToUse,\n          o: offsetFinal,\n          loc,\n        });\n\n      // gregorian data + weekday serves only to validate\n      if (normalized.weekday && containsGregor && obj.weekday !== inst.weekday) {\n        return DateTime.invalid(\n          \"mismatched weekday\",\n          `you can't specify both a weekday of ${normalized.weekday} and a date of ${inst.toISO()}`\n        );\n      }\n\n      if (!inst.isValid) {\n        return DateTime.invalid(inst.invalid);\n      }\n\n      return inst;\n    }\n\n    /**\n     * Create a DateTime from an ISO 8601 string\n     * @param {string} text - the ISO string\n     * @param {Object} opts - options to affect the creation\n     * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the time to this zone\n     * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one\n     * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n     * @param {string} [opts.outputCalendar] - the output calendar to set on the resulting DateTime instance\n     * @param {string} [opts.numberingSystem] - the numbering system to set on the resulting DateTime instance\n     * @example DateTime.fromISO('2016-05-25T09:08:34.123')\n     * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00')\n     * @example DateTime.fromISO('2016-05-25T09:08:34.123+06:00', {setZone: true})\n     * @example DateTime.fromISO('2016-05-25T09:08:34.123', {zone: 'utc'})\n     * @example DateTime.fromISO('2016-W05-4')\n     * @return {DateTime}\n     */\n    static fromISO(text, opts = {}) {\n      const [vals, parsedZone] = parseISODate(text);\n      return parseDataToDateTime(vals, parsedZone, opts, \"ISO 8601\", text);\n    }\n\n    /**\n     * Create a DateTime from an RFC 2822 string\n     * @param {string} text - the RFC 2822 string\n     * @param {Object} opts - options to affect the creation\n     * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since the offset is always specified in the string itself, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.\n     * @param {boolean} [opts.setZone=false] - override the zone with a fixed-offset zone specified in the string itself, if it specifies one\n     * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n     * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n     * @example DateTime.fromRFC2822('25 Nov 2016 13:23:12 GMT')\n     * @example DateTime.fromRFC2822('Fri, 25 Nov 2016 13:23:12 +0600')\n     * @example DateTime.fromRFC2822('25 Nov 2016 13:23 Z')\n     * @return {DateTime}\n     */\n    static fromRFC2822(text, opts = {}) {\n      const [vals, parsedZone] = parseRFC2822Date(text);\n      return parseDataToDateTime(vals, parsedZone, opts, \"RFC 2822\", text);\n    }\n\n    /**\n     * Create a DateTime from an HTTP header date\n     * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1\n     * @param {string} text - the HTTP header date\n     * @param {Object} opts - options to affect the creation\n     * @param {string|Zone} [opts.zone='local'] - convert the time to this zone. Since HTTP dates are always in UTC, this has no effect on the interpretation of string, merely the zone the resulting DateTime is expressed in.\n     * @param {boolean} [opts.setZone=false] - override the zone with the fixed-offset zone specified in the string. For HTTP dates, this is always UTC, so this option is equivalent to setting the `zone` option to 'utc', but this option is included for consistency with similar methods.\n     * @param {string} [opts.locale='system's locale'] - a locale to set on the resulting DateTime instance\n     * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @param {string} opts.numberingSystem - the numbering system to set on the resulting DateTime instance\n     * @example DateTime.fromHTTP('Sun, 06 Nov 1994 08:49:37 GMT')\n     * @example DateTime.fromHTTP('Sunday, 06-Nov-94 08:49:37 GMT')\n     * @example DateTime.fromHTTP('Sun Nov  6 08:49:37 1994')\n     * @return {DateTime}\n     */\n    static fromHTTP(text, opts = {}) {\n      const [vals, parsedZone] = parseHTTPDate(text);\n      return parseDataToDateTime(vals, parsedZone, opts, \"HTTP\", opts);\n    }\n\n    /**\n     * Create a DateTime from an input string and format string.\n     * Defaults to en-US if no locale has been specified, regardless of the system's locale. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/parsing?id=table-of-tokens).\n     * @param {string} text - the string to parse\n     * @param {string} fmt - the format the string is expected to be in (see the link below for the formats)\n     * @param {Object} opts - options to affect the creation\n     * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone\n     * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one\n     * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale\n     * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system\n     * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @return {DateTime}\n     */\n    static fromFormat(text, fmt, opts = {}) {\n      if (isUndefined(text) || isUndefined(fmt)) {\n        throw new InvalidArgumentError(\"fromFormat requires an input string and a format\");\n      }\n\n      const { locale = null, numberingSystem = null } = opts,\n        localeToUse = Locale.fromOpts({\n          locale,\n          numberingSystem,\n          defaultToEN: true,\n        }),\n        [vals, parsedZone, specificOffset, invalid] = parseFromTokens(localeToUse, text, fmt);\n      if (invalid) {\n        return DateTime.invalid(invalid);\n      } else {\n        return parseDataToDateTime(vals, parsedZone, opts, `format ${fmt}`, text, specificOffset);\n      }\n    }\n\n    /**\n     * @deprecated use fromFormat instead\n     */\n    static fromString(text, fmt, opts = {}) {\n      return DateTime.fromFormat(text, fmt, opts);\n    }\n\n    /**\n     * Create a DateTime from a SQL date, time, or datetime\n     * Defaults to en-US if no locale has been specified, regardless of the system's locale\n     * @param {string} text - the string to parse\n     * @param {Object} opts - options to affect the creation\n     * @param {string|Zone} [opts.zone='local'] - use this zone if no offset is specified in the input string itself. Will also convert the DateTime to this zone\n     * @param {boolean} [opts.setZone=false] - override the zone with a zone specified in the string itself, if it specifies one\n     * @param {string} [opts.locale='en-US'] - a locale string to use when parsing. Will also set the DateTime to this locale\n     * @param {string} opts.numberingSystem - the numbering system to use when parsing. Will also set the resulting DateTime to this numbering system\n     * @param {string} opts.outputCalendar - the output calendar to set on the resulting DateTime instance\n     * @example DateTime.fromSQL('2017-05-15')\n     * @example DateTime.fromSQL('2017-05-15 09:12:34')\n     * @example DateTime.fromSQL('2017-05-15 09:12:34.342')\n     * @example DateTime.fromSQL('2017-05-15 09:12:34.342+06:00')\n     * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles')\n     * @example DateTime.fromSQL('2017-05-15 09:12:34.342 America/Los_Angeles', { setZone: true })\n     * @example DateTime.fromSQL('2017-05-15 09:12:34.342', { zone: 'America/Los_Angeles' })\n     * @example DateTime.fromSQL('09:12:34.342')\n     * @return {DateTime}\n     */\n    static fromSQL(text, opts = {}) {\n      const [vals, parsedZone] = parseSQL(text);\n      return parseDataToDateTime(vals, parsedZone, opts, \"SQL\", text);\n    }\n\n    /**\n     * Create an invalid DateTime.\n     * @param {string} reason - simple string of why this DateTime is invalid. Should not contain parameters or anything else data-dependent.\n     * @param {string} [explanation=null] - longer explanation, may include parameters and other useful debugging information\n     * @return {DateTime}\n     */\n    static invalid(reason, explanation = null) {\n      if (!reason) {\n        throw new InvalidArgumentError(\"need to specify a reason the DateTime is invalid\");\n      }\n\n      const invalid = reason instanceof Invalid ? reason : new Invalid(reason, explanation);\n\n      if (Settings.throwOnInvalid) {\n        throw new InvalidDateTimeError(invalid);\n      } else {\n        return new DateTime({ invalid });\n      }\n    }\n\n    /**\n     * Check if an object is an instance of DateTime. Works across context boundaries\n     * @param {object} o\n     * @return {boolean}\n     */\n    static isDateTime(o) {\n      return (o && o.isLuxonDateTime) || false;\n    }\n\n    /**\n     * Produce the format string for a set of options\n     * @param formatOpts\n     * @param localeOpts\n     * @returns {string}\n     */\n    static parseFormatForOpts(formatOpts, localeOpts = {}) {\n      const tokenList = formatOptsToTokens(formatOpts, Locale.fromObject(localeOpts));\n      return !tokenList ? null : tokenList.map((t) => (t ? t.val : null)).join(\"\");\n    }\n\n    /**\n     * Produce the the fully expanded format token for the locale\n     * Does NOT quote characters, so quoted tokens will not round trip correctly\n     * @param fmt\n     * @param localeOpts\n     * @returns {string}\n     */\n    static expandFormat(fmt, localeOpts = {}) {\n      const expanded = expandMacroTokens(Formatter.parseFormat(fmt), Locale.fromObject(localeOpts));\n      return expanded.map((t) => t.val).join(\"\");\n    }\n\n    // INFO\n\n    /**\n     * Get the value of unit.\n     * @param {string} unit - a unit such as 'minute' or 'day'\n     * @example DateTime.local(2017, 7, 4).get('month'); //=> 7\n     * @example DateTime.local(2017, 7, 4).get('day'); //=> 4\n     * @return {number}\n     */\n    get(unit) {\n      return this[unit];\n    }\n\n    /**\n     * Returns whether the DateTime is valid. Invalid DateTimes occur when:\n     * * The DateTime was created from invalid calendar information, such as the 13th month or February 30\n     * * The DateTime was created by an operation on another invalid date\n     * @type {boolean}\n     */\n    get isValid() {\n      return this.invalid === null;\n    }\n\n    /**\n     * Returns an error code if this DateTime is invalid, or null if the DateTime is valid\n     * @type {string}\n     */\n    get invalidReason() {\n      return this.invalid ? this.invalid.reason : null;\n    }\n\n    /**\n     * Returns an explanation of why this DateTime became invalid, or null if the DateTime is valid\n     * @type {string}\n     */\n    get invalidExplanation() {\n      return this.invalid ? this.invalid.explanation : null;\n    }\n\n    /**\n     * Get the locale of a DateTime, such 'en-GB'. The locale is used when formatting the DateTime\n     *\n     * @type {string}\n     */\n    get locale() {\n      return this.isValid ? this.loc.locale : null;\n    }\n\n    /**\n     * Get the numbering system of a DateTime, such 'beng'. The numbering system is used when formatting the DateTime\n     *\n     * @type {string}\n     */\n    get numberingSystem() {\n      return this.isValid ? this.loc.numberingSystem : null;\n    }\n\n    /**\n     * Get the output calendar of a DateTime, such 'islamic'. The output calendar is used when formatting the DateTime\n     *\n     * @type {string}\n     */\n    get outputCalendar() {\n      return this.isValid ? this.loc.outputCalendar : null;\n    }\n\n    /**\n     * Get the time zone associated with this DateTime.\n     * @type {Zone}\n     */\n    get zone() {\n      return this._zone;\n    }\n\n    /**\n     * Get the name of the time zone.\n     * @type {string}\n     */\n    get zoneName() {\n      return this.isValid ? this.zone.name : null;\n    }\n\n    /**\n     * Get the year\n     * @example DateTime.local(2017, 5, 25).year //=> 2017\n     * @type {number}\n     */\n    get year() {\n      return this.isValid ? this.c.year : NaN;\n    }\n\n    /**\n     * Get the quarter\n     * @example DateTime.local(2017, 5, 25).quarter //=> 2\n     * @type {number}\n     */\n    get quarter() {\n      return this.isValid ? Math.ceil(this.c.month / 3) : NaN;\n    }\n\n    /**\n     * Get the month (1-12).\n     * @example DateTime.local(2017, 5, 25).month //=> 5\n     * @type {number}\n     */\n    get month() {\n      return this.isValid ? this.c.month : NaN;\n    }\n\n    /**\n     * Get the day of the month (1-30ish).\n     * @example DateTime.local(2017, 5, 25).day //=> 25\n     * @type {number}\n     */\n    get day() {\n      return this.isValid ? this.c.day : NaN;\n    }\n\n    /**\n     * Get the hour of the day (0-23).\n     * @example DateTime.local(2017, 5, 25, 9).hour //=> 9\n     * @type {number}\n     */\n    get hour() {\n      return this.isValid ? this.c.hour : NaN;\n    }\n\n    /**\n     * Get the minute of the hour (0-59).\n     * @example DateTime.local(2017, 5, 25, 9, 30).minute //=> 30\n     * @type {number}\n     */\n    get minute() {\n      return this.isValid ? this.c.minute : NaN;\n    }\n\n    /**\n     * Get the second of the minute (0-59).\n     * @example DateTime.local(2017, 5, 25, 9, 30, 52).second //=> 52\n     * @type {number}\n     */\n    get second() {\n      return this.isValid ? this.c.second : NaN;\n    }\n\n    /**\n     * Get the millisecond of the second (0-999).\n     * @example DateTime.local(2017, 5, 25, 9, 30, 52, 654).millisecond //=> 654\n     * @type {number}\n     */\n    get millisecond() {\n      return this.isValid ? this.c.millisecond : NaN;\n    }\n\n    /**\n     * Get the week year\n     * @see https://en.wikipedia.org/wiki/ISO_week_date\n     * @example DateTime.local(2014, 12, 31).weekYear //=> 2015\n     * @type {number}\n     */\n    get weekYear() {\n      return this.isValid ? possiblyCachedWeekData(this).weekYear : NaN;\n    }\n\n    /**\n     * Get the week number of the week year (1-52ish).\n     * @see https://en.wikipedia.org/wiki/ISO_week_date\n     * @example DateTime.local(2017, 5, 25).weekNumber //=> 21\n     * @type {number}\n     */\n    get weekNumber() {\n      return this.isValid ? possiblyCachedWeekData(this).weekNumber : NaN;\n    }\n\n    /**\n     * Get the day of the week.\n     * 1 is Monday and 7 is Sunday\n     * @see https://en.wikipedia.org/wiki/ISO_week_date\n     * @example DateTime.local(2014, 11, 31).weekday //=> 4\n     * @type {number}\n     */\n    get weekday() {\n      return this.isValid ? possiblyCachedWeekData(this).weekday : NaN;\n    }\n\n    /**\n     * Returns true if this date is on a weekend according to the locale, false otherwise\n     * @returns {boolean}\n     */\n    get isWeekend() {\n      return this.isValid && this.loc.getWeekendDays().includes(this.weekday);\n    }\n\n    /**\n     * Get the day of the week according to the locale.\n     * 1 is the first day of the week and 7 is the last day of the week.\n     * If the locale assigns Sunday as the first day of the week, then a date which is a Sunday will return 1,\n     * @returns {number}\n     */\n    get localWeekday() {\n      return this.isValid ? possiblyCachedLocalWeekData(this).weekday : NaN;\n    }\n\n    /**\n     * Get the week number of the week year according to the locale. Different locales assign week numbers differently,\n     * because the week can start on different days of the week (see localWeekday) and because a different number of days\n     * is required for a week to count as the first week of a year.\n     * @returns {number}\n     */\n    get localWeekNumber() {\n      return this.isValid ? possiblyCachedLocalWeekData(this).weekNumber : NaN;\n    }\n\n    /**\n     * Get the week year according to the locale. Different locales assign week numbers (and therefor week years)\n     * differently, see localWeekNumber.\n     * @returns {number}\n     */\n    get localWeekYear() {\n      return this.isValid ? possiblyCachedLocalWeekData(this).weekYear : NaN;\n    }\n\n    /**\n     * Get the ordinal (meaning the day of the year)\n     * @example DateTime.local(2017, 5, 25).ordinal //=> 145\n     * @type {number|DateTime}\n     */\n    get ordinal() {\n      return this.isValid ? gregorianToOrdinal(this.c).ordinal : NaN;\n    }\n\n    /**\n     * Get the human readable short month name, such as 'Oct'.\n     * Defaults to the system's locale if no locale has been specified\n     * @example DateTime.local(2017, 10, 30).monthShort //=> Oct\n     * @type {string}\n     */\n    get monthShort() {\n      return this.isValid ? Info.months(\"short\", { locObj: this.loc })[this.month - 1] : null;\n    }\n\n    /**\n     * Get the human readable long month name, such as 'October'.\n     * Defaults to the system's locale if no locale has been specified\n     * @example DateTime.local(2017, 10, 30).monthLong //=> October\n     * @type {string}\n     */\n    get monthLong() {\n      return this.isValid ? Info.months(\"long\", { locObj: this.loc })[this.month - 1] : null;\n    }\n\n    /**\n     * Get the human readable short weekday, such as 'Mon'.\n     * Defaults to the system's locale if no locale has been specified\n     * @example DateTime.local(2017, 10, 30).weekdayShort //=> Mon\n     * @type {string}\n     */\n    get weekdayShort() {\n      return this.isValid ? Info.weekdays(\"short\", { locObj: this.loc })[this.weekday - 1] : null;\n    }\n\n    /**\n     * Get the human readable long weekday, such as 'Monday'.\n     * Defaults to the system's locale if no locale has been specified\n     * @example DateTime.local(2017, 10, 30).weekdayLong //=> Monday\n     * @type {string}\n     */\n    get weekdayLong() {\n      return this.isValid ? Info.weekdays(\"long\", { locObj: this.loc })[this.weekday - 1] : null;\n    }\n\n    /**\n     * Get the UTC offset of this DateTime in minutes\n     * @example DateTime.now().offset //=> -240\n     * @example DateTime.utc().offset //=> 0\n     * @type {number}\n     */\n    get offset() {\n      return this.isValid ? +this.o : NaN;\n    }\n\n    /**\n     * Get the short human name for the zone's current offset, for example \"EST\" or \"EDT\".\n     * Defaults to the system's locale if no locale has been specified\n     * @type {string}\n     */\n    get offsetNameShort() {\n      if (this.isValid) {\n        return this.zone.offsetName(this.ts, {\n          format: \"short\",\n          locale: this.locale,\n        });\n      } else {\n        return null;\n      }\n    }\n\n    /**\n     * Get the long human name for the zone's current offset, for example \"Eastern Standard Time\" or \"Eastern Daylight Time\".\n     * Defaults to the system's locale if no locale has been specified\n     * @type {string}\n     */\n    get offsetNameLong() {\n      if (this.isValid) {\n        return this.zone.offsetName(this.ts, {\n          format: \"long\",\n          locale: this.locale,\n        });\n      } else {\n        return null;\n      }\n    }\n\n    /**\n     * Get whether this zone's offset ever changes, as in a DST.\n     * @type {boolean}\n     */\n    get isOffsetFixed() {\n      return this.isValid ? this.zone.isUniversal : null;\n    }\n\n    /**\n     * Get whether the DateTime is in a DST.\n     * @type {boolean}\n     */\n    get isInDST() {\n      if (this.isOffsetFixed) {\n        return false;\n      } else {\n        return (\n          this.offset > this.set({ month: 1, day: 1 }).offset ||\n          this.offset > this.set({ month: 5 }).offset\n        );\n      }\n    }\n\n    /**\n     * Get those DateTimes which have the same local time as this DateTime, but a different offset from UTC\n     * in this DateTime's zone. During DST changes local time can be ambiguous, for example\n     * `2023-10-29T02:30:00` in `Europe/Berlin` can have offset `+01:00` or `+02:00`.\n     * This method will return both possible DateTimes if this DateTime's local time is ambiguous.\n     * @returns {DateTime[]}\n     */\n    getPossibleOffsets() {\n      if (!this.isValid || this.isOffsetFixed) {\n        return [this];\n      }\n      const dayMs = 86400000;\n      const minuteMs = 60000;\n      const localTS = objToLocalTS(this.c);\n      const oEarlier = this.zone.offset(localTS - dayMs);\n      const oLater = this.zone.offset(localTS + dayMs);\n\n      const o1 = this.zone.offset(localTS - oEarlier * minuteMs);\n      const o2 = this.zone.offset(localTS - oLater * minuteMs);\n      if (o1 === o2) {\n        return [this];\n      }\n      const ts1 = localTS - o1 * minuteMs;\n      const ts2 = localTS - o2 * minuteMs;\n      const c1 = tsToObj(ts1, o1);\n      const c2 = tsToObj(ts2, o2);\n      if (\n        c1.hour === c2.hour &&\n        c1.minute === c2.minute &&\n        c1.second === c2.second &&\n        c1.millisecond === c2.millisecond\n      ) {\n        return [clone(this, { ts: ts1 }), clone(this, { ts: ts2 })];\n      }\n      return [this];\n    }\n\n    /**\n     * Returns true if this DateTime is in a leap year, false otherwise\n     * @example DateTime.local(2016).isInLeapYear //=> true\n     * @example DateTime.local(2013).isInLeapYear //=> false\n     * @type {boolean}\n     */\n    get isInLeapYear() {\n      return isLeapYear(this.year);\n    }\n\n    /**\n     * Returns the number of days in this DateTime's month\n     * @example DateTime.local(2016, 2).daysInMonth //=> 29\n     * @example DateTime.local(2016, 3).daysInMonth //=> 31\n     * @type {number}\n     */\n    get daysInMonth() {\n      return daysInMonth(this.year, this.month);\n    }\n\n    /**\n     * Returns the number of days in this DateTime's year\n     * @example DateTime.local(2016).daysInYear //=> 366\n     * @example DateTime.local(2013).daysInYear //=> 365\n     * @type {number}\n     */\n    get daysInYear() {\n      return this.isValid ? daysInYear(this.year) : NaN;\n    }\n\n    /**\n     * Returns the number of weeks in this DateTime's year\n     * @see https://en.wikipedia.org/wiki/ISO_week_date\n     * @example DateTime.local(2004).weeksInWeekYear //=> 53\n     * @example DateTime.local(2013).weeksInWeekYear //=> 52\n     * @type {number}\n     */\n    get weeksInWeekYear() {\n      return this.isValid ? weeksInWeekYear(this.weekYear) : NaN;\n    }\n\n    /**\n     * Returns the number of weeks in this DateTime's local week year\n     * @example DateTime.local(2020, 6, {locale: 'en-US'}).weeksInLocalWeekYear //=> 52\n     * @example DateTime.local(2020, 6, {locale: 'de-DE'}).weeksInLocalWeekYear //=> 53\n     * @type {number}\n     */\n    get weeksInLocalWeekYear() {\n      return this.isValid\n        ? weeksInWeekYear(\n            this.localWeekYear,\n            this.loc.getMinDaysInFirstWeek(),\n            this.loc.getStartOfWeek()\n          )\n        : NaN;\n    }\n\n    /**\n     * Returns the resolved Intl options for this DateTime.\n     * This is useful in understanding the behavior of formatting methods\n     * @param {Object} opts - the same options as toLocaleString\n     * @return {Object}\n     */\n    resolvedLocaleOptions(opts = {}) {\n      const { locale, numberingSystem, calendar } = Formatter.create(\n        this.loc.clone(opts),\n        opts\n      ).resolvedOptions(this);\n      return { locale, numberingSystem, outputCalendar: calendar };\n    }\n\n    // TRANSFORM\n\n    /**\n     * \"Set\" the DateTime's zone to UTC. Returns a newly-constructed DateTime.\n     *\n     * Equivalent to {@link DateTime#setZone}('utc')\n     * @param {number} [offset=0] - optionally, an offset from UTC in minutes\n     * @param {Object} [opts={}] - options to pass to `setZone()`\n     * @return {DateTime}\n     */\n    toUTC(offset = 0, opts = {}) {\n      return this.setZone(FixedOffsetZone.instance(offset), opts);\n    }\n\n    /**\n     * \"Set\" the DateTime's zone to the host's local zone. Returns a newly-constructed DateTime.\n     *\n     * Equivalent to `setZone('local')`\n     * @return {DateTime}\n     */\n    toLocal() {\n      return this.setZone(Settings.defaultZone);\n    }\n\n    /**\n     * \"Set\" the DateTime's zone to specified zone. Returns a newly-constructed DateTime.\n     *\n     * By default, the setter keeps the underlying time the same (as in, the same timestamp), but the new instance will report different local times and consider DSTs when making computations, as with {@link DateTime#plus}. You may wish to use {@link DateTime#toLocal} and {@link DateTime#toUTC} which provide simple convenience wrappers for commonly used zones.\n     * @param {string|Zone} [zone='local'] - a zone identifier. As a string, that can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3', or the strings 'local' or 'utc'. You may also supply an instance of a {@link DateTime#Zone} class.\n     * @param {Object} opts - options\n     * @param {boolean} [opts.keepLocalTime=false] - If true, adjust the underlying time so that the local time stays the same, but in the target zone. You should rarely need this.\n     * @return {DateTime}\n     */\n    setZone(zone, { keepLocalTime = false, keepCalendarTime = false } = {}) {\n      zone = normalizeZone(zone, Settings.defaultZone);\n      if (zone.equals(this.zone)) {\n        return this;\n      } else if (!zone.isValid) {\n        return DateTime.invalid(unsupportedZone(zone));\n      } else {\n        let newTS = this.ts;\n        if (keepLocalTime || keepCalendarTime) {\n          const offsetGuess = zone.offset(this.ts);\n          const asObj = this.toObject();\n          [newTS] = objToTS(asObj, offsetGuess, zone);\n        }\n        return clone(this, { ts: newTS, zone });\n      }\n    }\n\n    /**\n     * \"Set\" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime.\n     * @param {Object} properties - the properties to set\n     * @example DateTime.local(2017, 5, 25).reconfigure({ locale: 'en-GB' })\n     * @return {DateTime}\n     */\n    reconfigure({ locale, numberingSystem, outputCalendar } = {}) {\n      const loc = this.loc.clone({ locale, numberingSystem, outputCalendar });\n      return clone(this, { loc });\n    }\n\n    /**\n     * \"Set\" the locale. Returns a newly-constructed DateTime.\n     * Just a convenient alias for reconfigure({ locale })\n     * @example DateTime.local(2017, 5, 25).setLocale('en-GB')\n     * @return {DateTime}\n     */\n    setLocale(locale) {\n      return this.reconfigure({ locale });\n    }\n\n    /**\n     * \"Set\" the values of specified units. Returns a newly-constructed DateTime.\n     * You can only set units with this method; for \"setting\" metadata, see {@link DateTime#reconfigure} and {@link DateTime#setZone}.\n     *\n     * This method also supports setting locale-based week units, i.e. `localWeekday`, `localWeekNumber` and `localWeekYear`.\n     * They cannot be mixed with ISO-week units like `weekday`.\n     * @param {Object} values - a mapping of units to numbers\n     * @example dt.set({ year: 2017 })\n     * @example dt.set({ hour: 8, minute: 30 })\n     * @example dt.set({ weekday: 5 })\n     * @example dt.set({ year: 2005, ordinal: 234 })\n     * @return {DateTime}\n     */\n    set(values) {\n      if (!this.isValid) return this;\n\n      const normalized = normalizeObject(values, normalizeUnitWithLocalWeeks);\n      const { minDaysInFirstWeek, startOfWeek } = usesLocalWeekValues(normalized, this.loc);\n\n      const settingWeekStuff =\n          !isUndefined(normalized.weekYear) ||\n          !isUndefined(normalized.weekNumber) ||\n          !isUndefined(normalized.weekday),\n        containsOrdinal = !isUndefined(normalized.ordinal),\n        containsGregorYear = !isUndefined(normalized.year),\n        containsGregorMD = !isUndefined(normalized.month) || !isUndefined(normalized.day),\n        containsGregor = containsGregorYear || containsGregorMD,\n        definiteWeekDef = normalized.weekYear || normalized.weekNumber;\n\n      if ((containsGregor || containsOrdinal) && definiteWeekDef) {\n        throw new ConflictingSpecificationError(\n          \"Can't mix weekYear/weekNumber units with year/month/day or ordinals\"\n        );\n      }\n\n      if (containsGregorMD && containsOrdinal) {\n        throw new ConflictingSpecificationError(\"Can't mix ordinal dates with month/day\");\n      }\n\n      let mixed;\n      if (settingWeekStuff) {\n        mixed = weekToGregorian(\n          { ...gregorianToWeek(this.c, minDaysInFirstWeek, startOfWeek), ...normalized },\n          minDaysInFirstWeek,\n          startOfWeek\n        );\n      } else if (!isUndefined(normalized.ordinal)) {\n        mixed = ordinalToGregorian({ ...gregorianToOrdinal(this.c), ...normalized });\n      } else {\n        mixed = { ...this.toObject(), ...normalized };\n\n        // if we didn't set the day but we ended up on an overflow date,\n        // use the last day of the right month\n        if (isUndefined(normalized.day)) {\n          mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);\n        }\n      }\n\n      const [ts, o] = objToTS(mixed, this.o, this.zone);\n      return clone(this, { ts, o });\n    }\n\n    /**\n     * Add a period of time to this DateTime and return the resulting DateTime\n     *\n     * Adding hours, minutes, seconds, or milliseconds increases the timestamp by the right number of milliseconds. Adding days, months, or years shifts the calendar, accounting for DSTs and leap years along the way. Thus, `dt.plus({ hours: 24 })` may result in a different time than `dt.plus({ days: 1 })` if there's a DST shift in between.\n     * @param {Duration|Object|number} duration - The amount to add. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n     * @example DateTime.now().plus(123) //~> in 123 milliseconds\n     * @example DateTime.now().plus({ minutes: 15 }) //~> in 15 minutes\n     * @example DateTime.now().plus({ days: 1 }) //~> this time tomorrow\n     * @example DateTime.now().plus({ days: -1 }) //~> this time yesterday\n     * @example DateTime.now().plus({ hours: 3, minutes: 13 }) //~> in 3 hr, 13 min\n     * @example DateTime.now().plus(Duration.fromObject({ hours: 3, minutes: 13 })) //~> in 3 hr, 13 min\n     * @return {DateTime}\n     */\n    plus(duration) {\n      if (!this.isValid) return this;\n      const dur = Duration.fromDurationLike(duration);\n      return clone(this, adjustTime(this, dur));\n    }\n\n    /**\n     * Subtract a period of time to this DateTime and return the resulting DateTime\n     * See {@link DateTime#plus}\n     * @param {Duration|Object|number} duration - The amount to subtract. Either a Luxon Duration, a number of milliseconds, the object argument to Duration.fromObject()\n     @return {DateTime}\n     */\n    minus(duration) {\n      if (!this.isValid) return this;\n      const dur = Duration.fromDurationLike(duration).negate();\n      return clone(this, adjustTime(this, dur));\n    }\n\n    /**\n     * \"Set\" this DateTime to the beginning of a unit of time.\n     * @param {string} unit - The unit to go to the beginning of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.\n     * @param {Object} opts - options\n     * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week\n     * @example DateTime.local(2014, 3, 3).startOf('month').toISODate(); //=> '2014-03-01'\n     * @example DateTime.local(2014, 3, 3).startOf('year').toISODate(); //=> '2014-01-01'\n     * @example DateTime.local(2014, 3, 3).startOf('week').toISODate(); //=> '2014-03-03', weeks always start on Mondays\n     * @example DateTime.local(2014, 3, 3, 5, 30).startOf('day').toISOTime(); //=> '00:00.000-05:00'\n     * @example DateTime.local(2014, 3, 3, 5, 30).startOf('hour').toISOTime(); //=> '05:00:00.000-05:00'\n     * @return {DateTime}\n     */\n    startOf(unit, { useLocaleWeeks = false } = {}) {\n      if (!this.isValid) return this;\n\n      const o = {},\n        normalizedUnit = Duration.normalizeUnit(unit);\n      switch (normalizedUnit) {\n        case \"years\":\n          o.month = 1;\n        // falls through\n        case \"quarters\":\n        case \"months\":\n          o.day = 1;\n        // falls through\n        case \"weeks\":\n        case \"days\":\n          o.hour = 0;\n        // falls through\n        case \"hours\":\n          o.minute = 0;\n        // falls through\n        case \"minutes\":\n          o.second = 0;\n        // falls through\n        case \"seconds\":\n          o.millisecond = 0;\n          break;\n        // no default, invalid units throw in normalizeUnit()\n      }\n\n      if (normalizedUnit === \"weeks\") {\n        if (useLocaleWeeks) {\n          const startOfWeek = this.loc.getStartOfWeek();\n          const { weekday } = this;\n          if (weekday < startOfWeek) {\n            o.weekNumber = this.weekNumber - 1;\n          }\n          o.weekday = startOfWeek;\n        } else {\n          o.weekday = 1;\n        }\n      }\n\n      if (normalizedUnit === \"quarters\") {\n        const q = Math.ceil(this.month / 3);\n        o.month = (q - 1) * 3 + 1;\n      }\n\n      return this.set(o);\n    }\n\n    /**\n     * \"Set\" this DateTime to the end (meaning the last millisecond) of a unit of time\n     * @param {string} unit - The unit to go to the end of. Can be 'year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', or 'millisecond'.\n     * @param {Object} opts - options\n     * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week\n     * @example DateTime.local(2014, 3, 3).endOf('month').toISO(); //=> '2014-03-31T23:59:59.999-05:00'\n     * @example DateTime.local(2014, 3, 3).endOf('year').toISO(); //=> '2014-12-31T23:59:59.999-05:00'\n     * @example DateTime.local(2014, 3, 3).endOf('week').toISO(); // => '2014-03-09T23:59:59.999-05:00', weeks start on Mondays\n     * @example DateTime.local(2014, 3, 3, 5, 30).endOf('day').toISO(); //=> '2014-03-03T23:59:59.999-05:00'\n     * @example DateTime.local(2014, 3, 3, 5, 30).endOf('hour').toISO(); //=> '2014-03-03T05:59:59.999-05:00'\n     * @return {DateTime}\n     */\n    endOf(unit, opts) {\n      return this.isValid\n        ? this.plus({ [unit]: 1 })\n            .startOf(unit, opts)\n            .minus(1)\n        : this;\n    }\n\n    // OUTPUT\n\n    /**\n     * Returns a string representation of this DateTime formatted according to the specified format string.\n     * **You may not want this.** See {@link DateTime#toLocaleString} for a more flexible formatting tool. For a table of tokens and their interpretations, see [here](https://moment.github.io/luxon/#/formatting?id=table-of-tokens).\n     * Defaults to en-US if no locale has been specified, regardless of the system's locale.\n     * @param {string} fmt - the format string\n     * @param {Object} opts - opts to override the configuration options on this DateTime\n     * @example DateTime.now().toFormat('yyyy LLL dd') //=> '2017 Apr 22'\n     * @example DateTime.now().setLocale('fr').toFormat('yyyy LLL dd') //=> '2017 avr. 22'\n     * @example DateTime.now().toFormat('yyyy LLL dd', { locale: \"fr\" }) //=> '2017 avr. 22'\n     * @example DateTime.now().toFormat(\"HH 'hours and' mm 'minutes'\") //=> '20 hours and 55 minutes'\n     * @return {string}\n     */\n    toFormat(fmt, opts = {}) {\n      return this.isValid\n        ? Formatter.create(this.loc.redefaultToEN(opts)).formatDateTimeFromString(this, fmt)\n        : INVALID;\n    }\n\n    /**\n     * Returns a localized string representing this date. Accepts the same options as the Intl.DateTimeFormat constructor and any presets defined by Luxon, such as `DateTime.DATE_FULL` or `DateTime.TIME_SIMPLE`.\n     * The exact behavior of this method is browser-specific, but in general it will return an appropriate representation\n     * of the DateTime in the assigned locale.\n     * Defaults to the system's locale if no locale has been specified\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat\n     * @param formatOpts {Object} - Intl.DateTimeFormat constructor options and configuration options\n     * @param {Object} opts - opts to override the configuration options on this DateTime\n     * @example DateTime.now().toLocaleString(); //=> 4/20/2017\n     * @example DateTime.now().setLocale('en-gb').toLocaleString(); //=> '20/04/2017'\n     * @example DateTime.now().toLocaleString(DateTime.DATE_FULL); //=> 'April 20, 2017'\n     * @example DateTime.now().toLocaleString(DateTime.DATE_FULL, { locale: 'fr' }); //=> '28 ao\u00fbt 2022'\n     * @example DateTime.now().toLocaleString(DateTime.TIME_SIMPLE); //=> '11:32 AM'\n     * @example DateTime.now().toLocaleString(DateTime.DATETIME_SHORT); //=> '4/20/2017, 11:32 AM'\n     * @example DateTime.now().toLocaleString({ weekday: 'long', month: 'long', day: '2-digit' }); //=> 'Thursday, April 20'\n     * @example DateTime.now().toLocaleString({ weekday: 'short', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }); //=> 'Thu, Apr 20, 11:27 AM'\n     * @example DateTime.now().toLocaleString({ hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }); //=> '11:32'\n     * @return {string}\n     */\n    toLocaleString(formatOpts = DATE_SHORT, opts = {}) {\n      return this.isValid\n        ? Formatter.create(this.loc.clone(opts), formatOpts).formatDateTime(this)\n        : INVALID;\n    }\n\n    /**\n     * Returns an array of format \"parts\", meaning individual tokens along with metadata. This is allows callers to post-process individual sections of the formatted output.\n     * Defaults to the system's locale if no locale has been specified\n     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts\n     * @param opts {Object} - Intl.DateTimeFormat constructor options, same as `toLocaleString`.\n     * @example DateTime.now().toLocaleParts(); //=> [\n     *                                   //=>   { type: 'day', value: '25' },\n     *                                   //=>   { type: 'literal', value: '/' },\n     *                                   //=>   { type: 'month', value: '05' },\n     *                                   //=>   { type: 'literal', value: '/' },\n     *                                   //=>   { type: 'year', value: '1982' }\n     *                                   //=> ]\n     */\n    toLocaleParts(opts = {}) {\n      return this.isValid\n        ? Formatter.create(this.loc.clone(opts), opts).formatDateTimeParts(this)\n        : [];\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this DateTime\n     * @param {Object} opts - options\n     * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n     * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n     * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n     * @param {boolean} [opts.extendedZone=false] - add the time zone format extension\n     * @param {string} [opts.format='extended'] - choose between the basic and extended format\n     * @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'\n     * @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'\n     * @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'\n     * @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'\n     * @return {string}\n     */\n    toISO({\n      format = \"extended\",\n      suppressSeconds = false,\n      suppressMilliseconds = false,\n      includeOffset = true,\n      extendedZone = false,\n    } = {}) {\n      if (!this.isValid) {\n        return null;\n      }\n\n      const ext = format === \"extended\";\n\n      let c = toISODate(this, ext);\n      c += \"T\";\n      c += toISOTime(this, ext, suppressSeconds, suppressMilliseconds, includeOffset, extendedZone);\n      return c;\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this DateTime's date component\n     * @param {Object} opts - options\n     * @param {string} [opts.format='extended'] - choose between the basic and extended format\n     * @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'\n     * @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'\n     * @return {string}\n     */\n    toISODate({ format = \"extended\" } = {}) {\n      if (!this.isValid) {\n        return null;\n      }\n\n      return toISODate(this, format === \"extended\");\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this DateTime's week date\n     * @example DateTime.utc(1982, 5, 25).toISOWeekDate() //=> '1982-W21-2'\n     * @return {string}\n     */\n    toISOWeekDate() {\n      return toTechFormat(this, \"kkkk-'W'WW-c\");\n    }\n\n    /**\n     * Returns an ISO 8601-compliant string representation of this DateTime's time component\n     * @param {Object} opts - options\n     * @param {boolean} [opts.suppressMilliseconds=false] - exclude milliseconds from the format if they're 0\n     * @param {boolean} [opts.suppressSeconds=false] - exclude seconds from the format if they're 0\n     * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n     * @param {boolean} [opts.extendedZone=true] - add the time zone format extension\n     * @param {boolean} [opts.includePrefix=false] - include the `T` prefix\n     * @param {string} [opts.format='extended'] - choose between the basic and extended format\n     * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'\n     * @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'\n     * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'\n     * @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'\n     * @return {string}\n     */\n    toISOTime({\n      suppressMilliseconds = false,\n      suppressSeconds = false,\n      includeOffset = true,\n      includePrefix = false,\n      extendedZone = false,\n      format = \"extended\",\n    } = {}) {\n      if (!this.isValid) {\n        return null;\n      }\n\n      let c = includePrefix ? \"T\" : \"\";\n      return (\n        c +\n        toISOTime(\n          this,\n          format === \"extended\",\n          suppressSeconds,\n          suppressMilliseconds,\n          includeOffset,\n          extendedZone\n        )\n      );\n    }\n\n    /**\n     * Returns an RFC 2822-compatible string representation of this DateTime\n     * @example DateTime.utc(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 +0000'\n     * @example DateTime.local(2014, 7, 13).toRFC2822() //=> 'Sun, 13 Jul 2014 00:00:00 -0400'\n     * @return {string}\n     */\n    toRFC2822() {\n      return toTechFormat(this, \"EEE, dd LLL yyyy HH:mm:ss ZZZ\", false);\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for use in HTTP headers. The output is always expressed in GMT.\n     * Specifically, the string conforms to RFC 1123.\n     * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1\n     * @example DateTime.utc(2014, 7, 13).toHTTP() //=> 'Sun, 13 Jul 2014 00:00:00 GMT'\n     * @example DateTime.utc(2014, 7, 13, 19).toHTTP() //=> 'Sun, 13 Jul 2014 19:00:00 GMT'\n     * @return {string}\n     */\n    toHTTP() {\n      return toTechFormat(this.toUTC(), \"EEE, dd LLL yyyy HH:mm:ss 'GMT'\");\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for use in SQL Date\n     * @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'\n     * @return {string}\n     */\n    toSQLDate() {\n      if (!this.isValid) {\n        return null;\n      }\n      return toISODate(this, true);\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for use in SQL Time\n     * @param {Object} opts - options\n     * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.\n     * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n     * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'\n     * @example DateTime.utc().toSQL() //=> '05:15:16.345'\n     * @example DateTime.now().toSQL() //=> '05:15:16.345 -04:00'\n     * @example DateTime.now().toSQL({ includeOffset: false }) //=> '05:15:16.345'\n     * @example DateTime.now().toSQL({ includeZone: false }) //=> '05:15:16.345 America/New_York'\n     * @return {string}\n     */\n    toSQLTime({ includeOffset = true, includeZone = false, includeOffsetSpace = true } = {}) {\n      let fmt = \"HH:mm:ss.SSS\";\n\n      if (includeZone || includeOffset) {\n        if (includeOffsetSpace) {\n          fmt += \" \";\n        }\n        if (includeZone) {\n          fmt += \"z\";\n        } else if (includeOffset) {\n          fmt += \"ZZ\";\n        }\n      }\n\n      return toTechFormat(this, fmt, true);\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for use in SQL DateTime\n     * @param {Object} opts - options\n     * @param {boolean} [opts.includeZone=false] - include the zone, such as 'America/New_York'. Overrides includeOffset.\n     * @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'\n     * @param {boolean} [opts.includeOffsetSpace=true] - include the space between the time and the offset, such as '05:15:16.345 -04:00'\n     * @example DateTime.utc(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 Z'\n     * @example DateTime.local(2014, 7, 13).toSQL() //=> '2014-07-13 00:00:00.000 -04:00'\n     * @example DateTime.local(2014, 7, 13).toSQL({ includeOffset: false }) //=> '2014-07-13 00:00:00.000'\n     * @example DateTime.local(2014, 7, 13).toSQL({ includeZone: true }) //=> '2014-07-13 00:00:00.000 America/New_York'\n     * @return {string}\n     */\n    toSQL(opts = {}) {\n      if (!this.isValid) {\n        return null;\n      }\n\n      return `${this.toSQLDate()} ${this.toSQLTime(opts)}`;\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for debugging\n     * @return {string}\n     */\n    toString() {\n      return this.isValid ? this.toISO() : INVALID;\n    }\n\n    /**\n     * Returns a string representation of this DateTime appropriate for the REPL.\n     * @return {string}\n     */\n    [Symbol.for(\"nodejs.util.inspect.custom\")]() {\n      if (this.isValid) {\n        return `DateTime { ts: ${this.toISO()}, zone: ${this.zone.name}, locale: ${this.locale} }`;\n      } else {\n        return `DateTime { Invalid, reason: ${this.invalidReason} }`;\n      }\n    }\n\n    /**\n     * Returns the epoch milliseconds of this DateTime. Alias of {@link DateTime#toMillis}\n     * @return {number}\n     */\n    valueOf() {\n      return this.toMillis();\n    }\n\n    /**\n     * Returns the epoch milliseconds of this DateTime.\n     * @return {number}\n     */\n    toMillis() {\n      return this.isValid ? this.ts : NaN;\n    }\n\n    /**\n     * Returns the epoch seconds of this DateTime.\n     * @return {number}\n     */\n    toSeconds() {\n      return this.isValid ? this.ts / 1000 : NaN;\n    }\n\n    /**\n     * Returns the epoch seconds (as a whole number) of this DateTime.\n     * @return {number}\n     */\n    toUnixInteger() {\n      return this.isValid ? Math.floor(this.ts / 1000) : NaN;\n    }\n\n    /**\n     * Returns an ISO 8601 representation of this DateTime appropriate for use in JSON.\n     * @return {string}\n     */\n    toJSON() {\n      return this.toISO();\n    }\n\n    /**\n     * Returns a BSON serializable equivalent to this DateTime.\n     * @return {Date}\n     */\n    toBSON() {\n      return this.toJSDate();\n    }\n\n    /**\n     * Returns a JavaScript object with this DateTime's year, month, day, and so on.\n     * @param opts - options for generating the object\n     * @param {boolean} [opts.includeConfig=false] - include configuration attributes in the output\n     * @example DateTime.now().toObject() //=> { year: 2017, month: 4, day: 22, hour: 20, minute: 49, second: 42, millisecond: 268 }\n     * @return {Object}\n     */\n    toObject(opts = {}) {\n      if (!this.isValid) return {};\n\n      const base = { ...this.c };\n\n      if (opts.includeConfig) {\n        base.outputCalendar = this.outputCalendar;\n        base.numberingSystem = this.loc.numberingSystem;\n        base.locale = this.loc.locale;\n      }\n      return base;\n    }\n\n    /**\n     * Returns a JavaScript Date equivalent to this DateTime.\n     * @return {Date}\n     */\n    toJSDate() {\n      return new Date(this.isValid ? this.ts : NaN);\n    }\n\n    // COMPARE\n\n    /**\n     * Return the difference between two DateTimes as a Duration.\n     * @param {DateTime} otherDateTime - the DateTime to compare this one to\n     * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration.\n     * @param {Object} opts - options that affect the creation of the Duration\n     * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n     * @example\n     * var i1 = DateTime.fromISO('1982-05-25T09:45'),\n     *     i2 = DateTime.fromISO('1983-10-14T10:30');\n     * i2.diff(i1).toObject() //=> { milliseconds: 43807500000 }\n     * i2.diff(i1, 'hours').toObject() //=> { hours: 12168.75 }\n     * i2.diff(i1, ['months', 'days']).toObject() //=> { months: 16, days: 19.03125 }\n     * i2.diff(i1, ['months', 'days', 'hours']).toObject() //=> { months: 16, days: 19, hours: 0.75 }\n     * @return {Duration}\n     */\n    diff(otherDateTime, unit = \"milliseconds\", opts = {}) {\n      if (!this.isValid || !otherDateTime.isValid) {\n        return Duration.invalid(\"created by diffing an invalid DateTime\");\n      }\n\n      const durOpts = { locale: this.locale, numberingSystem: this.numberingSystem, ...opts };\n\n      const units = maybeArray(unit).map(Duration.normalizeUnit),\n        otherIsLater = otherDateTime.valueOf() > this.valueOf(),\n        earlier = otherIsLater ? this : otherDateTime,\n        later = otherIsLater ? otherDateTime : this,\n        diffed = diff(earlier, later, units, durOpts);\n\n      return otherIsLater ? diffed.negate() : diffed;\n    }\n\n    /**\n     * Return the difference between this DateTime and right now.\n     * See {@link DateTime#diff}\n     * @param {string|string[]} [unit=['milliseconds']] - the unit or units units (such as 'hours' or 'days') to include in the duration\n     * @param {Object} opts - options that affect the creation of the Duration\n     * @param {string} [opts.conversionAccuracy='casual'] - the conversion system to use\n     * @return {Duration}\n     */\n    diffNow(unit = \"milliseconds\", opts = {}) {\n      return this.diff(DateTime.now(), unit, opts);\n    }\n\n    /**\n     * Return an Interval spanning between this DateTime and another DateTime\n     * @param {DateTime} otherDateTime - the other end point of the Interval\n     * @return {Interval}\n     */\n    until(otherDateTime) {\n      return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;\n    }\n\n    /**\n     * Return whether this DateTime is in the same unit of time as another DateTime.\n     * Higher-order units must also be identical for this function to return `true`.\n     * Note that time zones are **ignored** in this comparison, which compares the **local** calendar time. Use {@link DateTime#setZone} to convert one of the dates if needed.\n     * @param {DateTime} otherDateTime - the other DateTime\n     * @param {string} unit - the unit of time to check sameness on\n     * @param {Object} opts - options\n     * @param {boolean} [opts.useLocaleWeeks=false] - If true, use weeks based on the locale, i.e. use the locale-dependent start of the week; only the locale of this DateTime is used\n     * @example DateTime.now().hasSame(otherDT, 'day'); //~> true if otherDT is in the same current calendar day\n     * @return {boolean}\n     */\n    hasSame(otherDateTime, unit, opts) {\n      if (!this.isValid) return false;\n\n      const inputMs = otherDateTime.valueOf();\n      const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });\n      return (\n        adjustedToZone.startOf(unit, opts) <= inputMs && inputMs <= adjustedToZone.endOf(unit, opts)\n      );\n    }\n\n    /**\n     * Equality check\n     * Two DateTimes are equal if and only if they represent the same millisecond, have the same zone and location, and are both valid.\n     * To compare just the millisecond values, use `+dt1 === +dt2`.\n     * @param {DateTime} other - the other DateTime\n     * @return {boolean}\n     */\n    equals(other) {\n      return (\n        this.isValid &&\n        other.isValid &&\n        this.valueOf() === other.valueOf() &&\n        this.zone.equals(other.zone) &&\n        this.loc.equals(other.loc)\n      );\n    }\n\n    /**\n     * Returns a string representation of a this time relative to now, such as \"in two days\". Can only internationalize if your\n     * platform supports Intl.RelativeTimeFormat. Rounds down by default.\n     * @param {Object} options - options that affect the output\n     * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.\n     * @param {string} [options.style=\"long\"] - the style of units, must be \"long\", \"short\", or \"narrow\"\n     * @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of \"years\", \"quarters\", \"months\", \"weeks\", \"days\", \"hours\", \"minutes\", or \"seconds\"\n     * @param {boolean} [options.round=true] - whether to round the numbers in the output.\n     * @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.\n     * @param {string} options.locale - override the locale of this DateTime\n     * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this\n     * @example DateTime.now().plus({ days: 1 }).toRelative() //=> \"in 1 day\"\n     * @example DateTime.now().setLocale(\"es\").toRelative({ days: 1 }) //=> \"dentro de 1 d\u00eda\"\n     * @example DateTime.now().plus({ days: 1 }).toRelative({ locale: \"fr\" }) //=> \"dans 23 heures\"\n     * @example DateTime.now().minus({ days: 2 }).toRelative() //=> \"2 days ago\"\n     * @example DateTime.now().minus({ days: 2 }).toRelative({ unit: \"hours\" }) //=> \"48 hours ago\"\n     * @example DateTime.now().minus({ hours: 36 }).toRelative({ round: false }) //=> \"1.5 days ago\"\n     */\n    toRelative(options = {}) {\n      if (!this.isValid) return null;\n      const base = options.base || DateTime.fromObject({}, { zone: this.zone }),\n        padding = options.padding ? (this < base ? -options.padding : options.padding) : 0;\n      let units = [\"years\", \"months\", \"days\", \"hours\", \"minutes\", \"seconds\"];\n      let unit = options.unit;\n      if (Array.isArray(options.unit)) {\n        units = options.unit;\n        unit = undefined;\n      }\n      return diffRelative(base, this.plus(padding), {\n        ...options,\n        numeric: \"always\",\n        units,\n        unit,\n      });\n    }\n\n    /**\n     * Returns a string representation of this date relative to today, such as \"yesterday\" or \"next month\".\n     * Only internationalizes on platforms that supports Intl.RelativeTimeFormat.\n     * @param {Object} options - options that affect the output\n     * @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.\n     * @param {string} options.locale - override the locale of this DateTime\n     * @param {string} options.unit - use a specific unit; if omitted, the method will pick the unit. Use one of \"years\", \"quarters\", \"months\", \"weeks\", or \"days\"\n     * @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this\n     * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar() //=> \"tomorrow\"\n     * @example DateTime.now().setLocale(\"es\").plus({ days: 1 }).toRelative() //=> \"\"ma\u00f1ana\"\n     * @example DateTime.now().plus({ days: 1 }).toRelativeCalendar({ locale: \"fr\" }) //=> \"demain\"\n     * @example DateTime.now().minus({ days: 2 }).toRelativeCalendar() //=> \"2 days ago\"\n     */\n    toRelativeCalendar(options = {}) {\n      if (!this.isValid) return null;\n\n      return diffRelative(options.base || DateTime.fromObject({}, { zone: this.zone }), this, {\n        ...options,\n        numeric: \"auto\",\n        units: [\"years\", \"months\", \"days\"],\n        calendary: true,\n      });\n    }\n\n    /**\n     * Return the min of several date times\n     * @param {...DateTime} dateTimes - the DateTimes from which to choose the minimum\n     * @return {DateTime} the min DateTime, or undefined if called with no argument\n     */\n    static min(...dateTimes) {\n      if (!dateTimes.every(DateTime.isDateTime)) {\n        throw new InvalidArgumentError(\"min requires all arguments be DateTimes\");\n      }\n      return bestBy(dateTimes, (i) => i.valueOf(), Math.min);\n    }\n\n    /**\n     * Return the max of several date times\n     * @param {...DateTime} dateTimes - the DateTimes from which to choose the maximum\n     * @return {DateTime} the max DateTime, or undefined if called with no argument\n     */\n    static max(...dateTimes) {\n      if (!dateTimes.every(DateTime.isDateTime)) {\n        throw new InvalidArgumentError(\"max requires all arguments be DateTimes\");\n      }\n      return bestBy(dateTimes, (i) => i.valueOf(), Math.max);\n    }\n\n    // MISC\n\n    /**\n     * Explain how a string would be parsed by fromFormat()\n     * @param {string} text - the string to parse\n     * @param {string} fmt - the format the string is expected to be in (see description)\n     * @param {Object} options - options taken by fromFormat()\n     * @return {Object}\n     */\n    static fromFormatExplain(text, fmt, options = {}) {\n      const { locale = null, numberingSystem = null } = options,\n        localeToUse = Locale.fromOpts({\n          locale,\n          numberingSystem,\n          defaultToEN: true,\n        });\n      return explainFromTokens(localeToUse, text, fmt);\n    }\n\n    /**\n     * @deprecated use fromFormatExplain instead\n     */\n    static fromStringExplain(text, fmt, options = {}) {\n      return DateTime.fromFormatExplain(text, fmt, options);\n    }\n\n    // FORMAT PRESETS\n\n    /**\n     * {@link DateTime#toLocaleString} format like 10/14/1983\n     * @type {Object}\n     */\n    static get DATE_SHORT() {\n      return DATE_SHORT;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Oct 14, 1983'\n     * @type {Object}\n     */\n    static get DATE_MED() {\n      return DATE_MED;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Fri, Oct 14, 1983'\n     * @type {Object}\n     */\n    static get DATE_MED_WITH_WEEKDAY() {\n      return DATE_MED_WITH_WEEKDAY;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'October 14, 1983'\n     * @type {Object}\n     */\n    static get DATE_FULL() {\n      return DATE_FULL;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Tuesday, October 14, 1983'\n     * @type {Object}\n     */\n    static get DATE_HUGE() {\n      return DATE_HUGE;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get TIME_SIMPLE() {\n      return TIME_SIMPLE;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get TIME_WITH_SECONDS() {\n      return TIME_WITH_SECONDS;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23 AM EDT'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get TIME_WITH_SHORT_OFFSET() {\n      return TIME_WITH_SHORT_OFFSET;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get TIME_WITH_LONG_OFFSET() {\n      return TIME_WITH_LONG_OFFSET;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30', always 24-hour.\n     * @type {Object}\n     */\n    static get TIME_24_SIMPLE() {\n      return TIME_24_SIMPLE;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23', always 24-hour.\n     * @type {Object}\n     */\n    static get TIME_24_WITH_SECONDS() {\n      return TIME_24_WITH_SECONDS;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23 EDT', always 24-hour.\n     * @type {Object}\n     */\n    static get TIME_24_WITH_SHORT_OFFSET() {\n      return TIME_24_WITH_SHORT_OFFSET;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '09:30:23 Eastern Daylight Time', always 24-hour.\n     * @type {Object}\n     */\n    static get TIME_24_WITH_LONG_OFFSET() {\n      return TIME_24_WITH_LONG_OFFSET;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_SHORT() {\n      return DATETIME_SHORT;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like '10/14/1983, 9:30:33 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_SHORT_WITH_SECONDS() {\n      return DATETIME_SHORT_WITH_SECONDS;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_MED() {\n      return DATETIME_MED;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Oct 14, 1983, 9:30:33 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_MED_WITH_SECONDS() {\n      return DATETIME_MED_WITH_SECONDS;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Fri, 14 Oct 1983, 9:30 AM'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_MED_WITH_WEEKDAY() {\n      return DATETIME_MED_WITH_WEEKDAY;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30 AM EDT'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_FULL() {\n      return DATETIME_FULL;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'October 14, 1983, 9:30:33 AM EDT'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_FULL_WITH_SECONDS() {\n      return DATETIME_FULL_WITH_SECONDS;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_HUGE() {\n      return DATETIME_HUGE;\n    }\n\n    /**\n     * {@link DateTime#toLocaleString} format like 'Friday, October 14, 1983, 9:30:33 AM Eastern Daylight Time'. Only 12-hour if the locale is.\n     * @type {Object}\n     */\n    static get DATETIME_HUGE_WITH_SECONDS() {\n      return DATETIME_HUGE_WITH_SECONDS;\n    }\n  }\n\n  /**\n   * @private\n   */\n  function friendlyDateTime(dateTimeish) {\n    if (DateTime.isDateTime(dateTimeish)) {\n      return dateTimeish;\n    } else if (dateTimeish && dateTimeish.valueOf && isNumber(dateTimeish.valueOf())) {\n      return DateTime.fromJSDate(dateTimeish);\n    } else if (dateTimeish && typeof dateTimeish === \"object\") {\n      return DateTime.fromObject(dateTimeish);\n    } else {\n      throw new InvalidArgumentError(\n        `Unknown datetime argument: ${dateTimeish}, of type ${typeof dateTimeish}`\n      );\n    }\n  }\n\n  const VERSION = \"3.4.4\";\n\n  exports.DateTime = DateTime;\n  exports.Duration = Duration;\n  exports.FixedOffsetZone = FixedOffsetZone;\n  exports.IANAZone = IANAZone;\n  exports.Info = Info;\n  exports.Interval = Interval;\n  exports.InvalidZone = InvalidZone;\n  exports.Settings = Settings;\n  exports.SystemZone = SystemZone;\n  exports.VERSION = VERSION;\n  exports.Zone = Zone;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n  return exports;\n\n})({});\n// start Odoo customization\n// The following prevents luxon objects from being made reactive by Owl, because they are immutable\nluxon.DateTime.prototype[Symbol.toStringTag] = \"LuxonDateTime\";\nluxon.Duration.prototype[Symbol.toStringTag] = \"LuxonDuration\";\nluxon.Interval.prototype[Symbol.toStringTag] = \"LuxonInterval\";\nluxon.Settings.prototype[Symbol.toStringTag] = \"LuxonSettings\";\nluxon.Info.prototype[Symbol.toStringTag] = \"LuxonInfo\";\nluxon.Zone.prototype[Symbol.toStringTag] = \"LuxonZone\";\n// end Odoo customization\n//# sourceMappingURL=luxon.js.map\n", "// @odoo-module ignore\nif (!Object.hasOwn) {\n    Object.hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);\n}\n", "// @odoo-module ignore\nif (!Array.prototype.at) {\n    Object.defineProperty(Array.prototype, \"at\", {\n        enumerable: false,\n        value: function (index) {\n            if (index >= 0) {\n                return this[index];\n            }\n            return this[this.length + index];\n        }\n    });\n}\n", "// @odoo-module ignore\n\n(function () {\n'use strict';\n\n/**\n * This file makes sure textarea elements with a specific editor class are\n * tweaked as soon as the DOM is ready so that they appear to be loading.\n *\n * They must then be loaded using standard Odoo modules system. In particular,\n * @see @web_editor/js/frontend/loadWysiwygFromTextarea\n */\n\ndocument.addEventListener('DOMContentLoaded', () => {\n    // Standard loop for better browser support\n    var textareaEls = document.querySelectorAll('textarea.o_wysiwyg_loader');\n    for (var i = 0; i < textareaEls.length; i++) {\n        var textarea = textareaEls[i];\n        var wrapper = document.createElement('div');\n        wrapper.classList.add('position-relative', 'o_wysiwyg_textarea_wrapper');\n\n        var loadingElement = document.createElement('div');\n        loadingElement.classList.add('o_wysiwyg_loading');\n        var loadingIcon = document.createElement('i');\n        loadingIcon.classList.add('text-600', 'text-center',\n            'fa', 'fa-circle-o-notch', 'fa-spin', 'fa-2x');\n        loadingElement.appendChild(loadingIcon);\n        wrapper.appendChild(loadingElement);\n\n        textarea.parentNode.insertBefore(wrapper, textarea);\n        wrapper.insertBefore(textarea, loadingElement);\n    }\n});\n\n})();\n", "(function (exports) {\r\n    'use strict';\r\n\r\n    function filterOutModifiersFromData(dataList) {\r\n        dataList = dataList.slice();\r\n        const modifiers = [];\r\n        let elm;\r\n        while ((elm = dataList[0]) && typeof elm === \"string\") {\r\n            modifiers.push(dataList.shift());\r\n        }\r\n        return { modifiers, data: dataList };\r\n    }\r\n    const config = {\r\n        // whether or not blockdom should normalize DOM whenever a block is created.\r\n        // Normalizing dom mean removing empty text nodes (or containing only spaces)\r\n        shouldNormalizeDom: true,\r\n        // this is the main event handler. Every event handler registered with blockdom\r\n        // will go through this function, giving it the data registered in the block\r\n        // and the event\r\n        mainEventHandler: (data, ev, currentTarget) => {\r\n            if (typeof data === \"function\") {\r\n                data(ev);\r\n            }\r\n            else if (Array.isArray(data)) {\r\n                data = filterOutModifiersFromData(data).data;\r\n                data[0](data[1], ev);\r\n            }\r\n            return false;\r\n        },\r\n    };\r\n\r\n    // -----------------------------------------------------------------------------\r\n    // Toggler node\r\n    // -----------------------------------------------------------------------------\r\n    class VToggler {\r\n        constructor(key, child) {\r\n            this.key = key;\r\n            this.child = child;\r\n        }\r\n        mount(parent, afterNode) {\r\n            this.parentEl = parent;\r\n            this.child.mount(parent, afterNode);\r\n        }\r\n        moveBeforeDOMNode(node, parent) {\r\n            this.child.moveBeforeDOMNode(node, parent);\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            this.moveBeforeDOMNode((other && other.firstNode()) || afterNode);\r\n        }\r\n        patch(other, withBeforeRemove) {\r\n            if (this === other) {\r\n                return;\r\n            }\r\n            let child1 = this.child;\r\n            let child2 = other.child;\r\n            if (this.key === other.key) {\r\n                child1.patch(child2, withBeforeRemove);\r\n            }\r\n            else {\r\n                child2.mount(this.parentEl, child1.firstNode());\r\n                if (withBeforeRemove) {\r\n                    child1.beforeRemove();\r\n                }\r\n                child1.remove();\r\n                this.child = child2;\r\n                this.key = other.key;\r\n            }\r\n        }\r\n        beforeRemove() {\r\n            this.child.beforeRemove();\r\n        }\r\n        remove() {\r\n            this.child.remove();\r\n        }\r\n        firstNode() {\r\n            return this.child.firstNode();\r\n        }\r\n        toString() {\r\n            return this.child.toString();\r\n        }\r\n    }\r\n    function toggler(key, child) {\r\n        return new VToggler(key, child);\r\n    }\r\n\r\n    // Custom error class that wraps error that happen in the owl lifecycle\r\n    class OwlError extends Error {\r\n    }\r\n\r\n    const { setAttribute: elemSetAttribute, removeAttribute } = Element.prototype;\r\n    const tokenList = DOMTokenList.prototype;\r\n    const tokenListAdd = tokenList.add;\r\n    const tokenListRemove = tokenList.remove;\r\n    const isArray = Array.isArray;\r\n    const { split, trim } = String.prototype;\r\n    const wordRegexp = /\\s+/;\r\n    /**\r\n     * We regroup here all code related to updating attributes in a very loose sense:\r\n     * attributes, properties and classs are all managed by the functions in this\r\n     * file.\r\n     */\r\n    function setAttribute(key, value) {\r\n        switch (value) {\r\n            case false:\r\n            case undefined:\r\n                removeAttribute.call(this, key);\r\n                break;\r\n            case true:\r\n                elemSetAttribute.call(this, key, \"\");\r\n                break;\r\n            default:\r\n                elemSetAttribute.call(this, key, value);\r\n        }\r\n    }\r\n    function createAttrUpdater(attr) {\r\n        return function (value) {\r\n            setAttribute.call(this, attr, value);\r\n        };\r\n    }\r\n    function attrsSetter(attrs) {\r\n        if (isArray(attrs)) {\r\n            if (attrs[0] === \"class\") {\r\n                setClass.call(this, attrs[1]);\r\n            }\r\n            else {\r\n                setAttribute.call(this, attrs[0], attrs[1]);\r\n            }\r\n        }\r\n        else {\r\n            for (let k in attrs) {\r\n                if (k === \"class\") {\r\n                    setClass.call(this, attrs[k]);\r\n                }\r\n                else {\r\n                    setAttribute.call(this, k, attrs[k]);\r\n                }\r\n            }\r\n        }\r\n    }\r\n    function attrsUpdater(attrs, oldAttrs) {\r\n        if (isArray(attrs)) {\r\n            const name = attrs[0];\r\n            const val = attrs[1];\r\n            if (name === oldAttrs[0]) {\r\n                if (val === oldAttrs[1]) {\r\n                    return;\r\n                }\r\n                if (name === \"class\") {\r\n                    updateClass.call(this, val, oldAttrs[1]);\r\n                }\r\n                else {\r\n                    setAttribute.call(this, name, val);\r\n                }\r\n            }\r\n            else {\r\n                removeAttribute.call(this, oldAttrs[0]);\r\n                setAttribute.call(this, name, val);\r\n            }\r\n        }\r\n        else {\r\n            for (let k in oldAttrs) {\r\n                if (!(k in attrs)) {\r\n                    if (k === \"class\") {\r\n                        updateClass.call(this, \"\", oldAttrs[k]);\r\n                    }\r\n                    else {\r\n                        removeAttribute.call(this, k);\r\n                    }\r\n                }\r\n            }\r\n            for (let k in attrs) {\r\n                const val = attrs[k];\r\n                if (val !== oldAttrs[k]) {\r\n                    if (k === \"class\") {\r\n                        updateClass.call(this, val, oldAttrs[k]);\r\n                    }\r\n                    else {\r\n                        setAttribute.call(this, k, val);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n    function toClassObj(expr) {\r\n        const result = {};\r\n        switch (typeof expr) {\r\n            case \"string\":\r\n                // we transform here a list of classes into an object:\r\n                //  'hey you' becomes {hey: true, you: true}\r\n                const str = trim.call(expr);\r\n                if (!str) {\r\n                    return {};\r\n                }\r\n                let words = split.call(str, wordRegexp);\r\n                for (let i = 0, l = words.length; i < l; i++) {\r\n                    result[words[i]] = true;\r\n                }\r\n                return result;\r\n            case \"object\":\r\n                // this is already an object but we may need to split keys:\r\n                // {'a': true, 'b c': true} should become {a: true, b: true, c: true}\r\n                for (let key in expr) {\r\n                    const value = expr[key];\r\n                    if (value) {\r\n                        key = trim.call(key);\r\n                        if (!key) {\r\n                            continue;\r\n                        }\r\n                        const words = split.call(key, wordRegexp);\r\n                        for (let word of words) {\r\n                            result[word] = value;\r\n                        }\r\n                    }\r\n                }\r\n                return result;\r\n            case \"undefined\":\r\n                return {};\r\n            case \"number\":\r\n                return { [expr]: true };\r\n            default:\r\n                return { [expr]: true };\r\n        }\r\n    }\r\n    function setClass(val) {\r\n        val = val === \"\" ? {} : toClassObj(val);\r\n        // add classes\r\n        const cl = this.classList;\r\n        for (let c in val) {\r\n            tokenListAdd.call(cl, c);\r\n        }\r\n    }\r\n    function updateClass(val, oldVal) {\r\n        oldVal = oldVal === \"\" ? {} : toClassObj(oldVal);\r\n        val = val === \"\" ? {} : toClassObj(val);\r\n        const cl = this.classList;\r\n        // remove classes\r\n        for (let c in oldVal) {\r\n            if (!(c in val)) {\r\n                tokenListRemove.call(cl, c);\r\n            }\r\n        }\r\n        // add classes\r\n        for (let c in val) {\r\n            if (!(c in oldVal)) {\r\n                tokenListAdd.call(cl, c);\r\n            }\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Creates a batched version of a callback so that all calls to it in the same\r\n     * microtick will only call the original callback once.\r\n     *\r\n     * @param callback the callback to batch\r\n     * @returns a batched version of the original callback\r\n     */\r\n    function batched(callback) {\r\n        let scheduled = false;\r\n        return async (...args) => {\r\n            if (!scheduled) {\r\n                scheduled = true;\r\n                await Promise.resolve();\r\n                scheduled = false;\r\n                callback(...args);\r\n            }\r\n        };\r\n    }\r\n    /**\r\n     * Determine whether the given element is contained in its ownerDocument:\r\n     * either directly or with a shadow root in between.\r\n     */\r\n    function inOwnerDocument(el) {\r\n        if (!el) {\r\n            return false;\r\n        }\r\n        if (el.ownerDocument.contains(el)) {\r\n            return true;\r\n        }\r\n        const rootNode = el.getRootNode();\r\n        return rootNode instanceof ShadowRoot && el.ownerDocument.contains(rootNode.host);\r\n    }\r\n    function validateTarget(target) {\r\n        // Get the document and HTMLElement corresponding to the target to allow mounting in iframes\r\n        const document = target && target.ownerDocument;\r\n        if (document) {\r\n            const HTMLElement = document.defaultView.HTMLElement;\r\n            if (target instanceof HTMLElement || target instanceof ShadowRoot) {\r\n                if (!document.body.contains(target instanceof HTMLElement ? target : target.host)) {\r\n                    throw new OwlError(\"Cannot mount a component on a detached dom node\");\r\n                }\r\n                return;\r\n            }\r\n        }\r\n        throw new OwlError(\"Cannot mount component: the target is not a valid DOM element\");\r\n    }\r\n    class EventBus extends EventTarget {\r\n        trigger(name, payload) {\r\n            this.dispatchEvent(new CustomEvent(name, { detail: payload }));\r\n        }\r\n    }\r\n    function whenReady(fn) {\r\n        return new Promise(function (resolve) {\r\n            if (document.readyState !== \"loading\") {\r\n                resolve(true);\r\n            }\r\n            else {\r\n                document.addEventListener(\"DOMContentLoaded\", resolve, false);\r\n            }\r\n        }).then(fn || function () { });\r\n    }\r\n    async function loadFile(url) {\r\n        const result = await fetch(url);\r\n        if (!result.ok) {\r\n            throw new OwlError(\"Error while fetching xml templates\");\r\n        }\r\n        return await result.text();\r\n    }\r\n    /*\r\n     * This class just transports the fact that a string is safe\r\n     * to be injected as HTML. Overriding a JS primitive is quite painful though\r\n     * so we need to redfine toString and valueOf.\r\n     */\r\n    class Markup extends String {\r\n    }\r\n    /*\r\n     * Marks a value as safe, that is, a value that can be injected as HTML directly.\r\n     * It should be used to wrap the value passed to a t-out directive to allow a raw rendering.\r\n     */\r\n    function markup(value) {\r\n        return new Markup(value);\r\n    }\r\n\r\n    function createEventHandler(rawEvent) {\r\n        const eventName = rawEvent.split(\".\")[0];\r\n        const capture = rawEvent.includes(\".capture\");\r\n        if (rawEvent.includes(\".synthetic\")) {\r\n            return createSyntheticHandler(eventName, capture);\r\n        }\r\n        else {\r\n            return createElementHandler(eventName, capture);\r\n        }\r\n    }\r\n    // Native listener\r\n    let nextNativeEventId = 1;\r\n    function createElementHandler(evName, capture = false) {\r\n        let eventKey = `__event__${evName}_${nextNativeEventId++}`;\r\n        if (capture) {\r\n            eventKey = `${eventKey}_capture`;\r\n        }\r\n        function listener(ev) {\r\n            const currentTarget = ev.currentTarget;\r\n            if (!currentTarget || !inOwnerDocument(currentTarget))\r\n                return;\r\n            const data = currentTarget[eventKey];\r\n            if (!data)\r\n                return;\r\n            config.mainEventHandler(data, ev, currentTarget);\r\n        }\r\n        function setup(data) {\r\n            this[eventKey] = data;\r\n            this.addEventListener(evName, listener, { capture });\r\n        }\r\n        function remove() {\r\n            delete this[eventKey];\r\n            this.removeEventListener(evName, listener, { capture });\r\n        }\r\n        function update(data) {\r\n            this[eventKey] = data;\r\n        }\r\n        return { setup, update, remove };\r\n    }\r\n    // Synthetic handler: a form of event delegation that allows placing only one\r\n    // listener per event type.\r\n    let nextSyntheticEventId = 1;\r\n    function createSyntheticHandler(evName, capture = false) {\r\n        let eventKey = `__event__synthetic_${evName}`;\r\n        if (capture) {\r\n            eventKey = `${eventKey}_capture`;\r\n        }\r\n        setupSyntheticEvent(evName, eventKey, capture);\r\n        const currentId = nextSyntheticEventId++;\r\n        function setup(data) {\r\n            const _data = this[eventKey] || {};\r\n            _data[currentId] = data;\r\n            this[eventKey] = _data;\r\n        }\r\n        function remove() {\r\n            delete this[eventKey];\r\n        }\r\n        return { setup, update: setup, remove };\r\n    }\r\n    function nativeToSyntheticEvent(eventKey, event) {\r\n        let dom = event.target;\r\n        while (dom !== null) {\r\n            const _data = dom[eventKey];\r\n            if (_data) {\r\n                for (const data of Object.values(_data)) {\r\n                    const stopped = config.mainEventHandler(data, event, dom);\r\n                    if (stopped)\r\n                        return;\r\n                }\r\n            }\r\n            dom = dom.parentNode;\r\n        }\r\n    }\r\n    const CONFIGURED_SYNTHETIC_EVENTS = {};\r\n    function setupSyntheticEvent(evName, eventKey, capture = false) {\r\n        if (CONFIGURED_SYNTHETIC_EVENTS[eventKey]) {\r\n            return;\r\n        }\r\n        document.addEventListener(evName, (event) => nativeToSyntheticEvent(eventKey, event), {\r\n            capture,\r\n        });\r\n        CONFIGURED_SYNTHETIC_EVENTS[eventKey] = true;\r\n    }\r\n\r\n    const getDescriptor$3 = (o, p) => Object.getOwnPropertyDescriptor(o, p);\r\n    const nodeProto$4 = Node.prototype;\r\n    const nodeInsertBefore$3 = nodeProto$4.insertBefore;\r\n    const nodeSetTextContent$1 = getDescriptor$3(nodeProto$4, \"textContent\").set;\r\n    const nodeRemoveChild$3 = nodeProto$4.removeChild;\r\n    // -----------------------------------------------------------------------------\r\n    // Multi NODE\r\n    // -----------------------------------------------------------------------------\r\n    class VMulti {\r\n        constructor(children) {\r\n            this.children = children;\r\n        }\r\n        mount(parent, afterNode) {\r\n            const children = this.children;\r\n            const l = children.length;\r\n            const anchors = new Array(l);\r\n            for (let i = 0; i < l; i++) {\r\n                let child = children[i];\r\n                if (child) {\r\n                    child.mount(parent, afterNode);\r\n                }\r\n                else {\r\n                    const childAnchor = document.createTextNode(\"\");\r\n                    anchors[i] = childAnchor;\r\n                    nodeInsertBefore$3.call(parent, childAnchor, afterNode);\r\n                }\r\n            }\r\n            this.anchors = anchors;\r\n            this.parentEl = parent;\r\n        }\r\n        moveBeforeDOMNode(node, parent = this.parentEl) {\r\n            this.parentEl = parent;\r\n            const children = this.children;\r\n            const anchors = this.anchors;\r\n            for (let i = 0, l = children.length; i < l; i++) {\r\n                let child = children[i];\r\n                if (child) {\r\n                    child.moveBeforeDOMNode(node, parent);\r\n                }\r\n                else {\r\n                    const anchor = anchors[i];\r\n                    nodeInsertBefore$3.call(parent, anchor, node);\r\n                }\r\n            }\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            if (other) {\r\n                const next = other.children[0];\r\n                afterNode = (next ? next.firstNode() : other.anchors[0]) || null;\r\n            }\r\n            const children = this.children;\r\n            const parent = this.parentEl;\r\n            const anchors = this.anchors;\r\n            for (let i = 0, l = children.length; i < l; i++) {\r\n                let child = children[i];\r\n                if (child) {\r\n                    child.moveBeforeVNode(null, afterNode);\r\n                }\r\n                else {\r\n                    const anchor = anchors[i];\r\n                    nodeInsertBefore$3.call(parent, anchor, afterNode);\r\n                }\r\n            }\r\n        }\r\n        patch(other, withBeforeRemove) {\r\n            if (this === other) {\r\n                return;\r\n            }\r\n            const children1 = this.children;\r\n            const children2 = other.children;\r\n            const anchors = this.anchors;\r\n            const parentEl = this.parentEl;\r\n            for (let i = 0, l = children1.length; i < l; i++) {\r\n                const vn1 = children1[i];\r\n                const vn2 = children2[i];\r\n                if (vn1) {\r\n                    if (vn2) {\r\n                        vn1.patch(vn2, withBeforeRemove);\r\n                    }\r\n                    else {\r\n                        const afterNode = vn1.firstNode();\r\n                        const anchor = document.createTextNode(\"\");\r\n                        anchors[i] = anchor;\r\n                        nodeInsertBefore$3.call(parentEl, anchor, afterNode);\r\n                        if (withBeforeRemove) {\r\n                            vn1.beforeRemove();\r\n                        }\r\n                        vn1.remove();\r\n                        children1[i] = undefined;\r\n                    }\r\n                }\r\n                else if (vn2) {\r\n                    children1[i] = vn2;\r\n                    const anchor = anchors[i];\r\n                    vn2.mount(parentEl, anchor);\r\n                    nodeRemoveChild$3.call(parentEl, anchor);\r\n                }\r\n            }\r\n        }\r\n        beforeRemove() {\r\n            const children = this.children;\r\n            for (let i = 0, l = children.length; i < l; i++) {\r\n                const child = children[i];\r\n                if (child) {\r\n                    child.beforeRemove();\r\n                }\r\n            }\r\n        }\r\n        remove() {\r\n            const parentEl = this.parentEl;\r\n            if (this.isOnlyChild) {\r\n                nodeSetTextContent$1.call(parentEl, \"\");\r\n            }\r\n            else {\r\n                const children = this.children;\r\n                const anchors = this.anchors;\r\n                for (let i = 0, l = children.length; i < l; i++) {\r\n                    const child = children[i];\r\n                    if (child) {\r\n                        child.remove();\r\n                    }\r\n                    else {\r\n                        nodeRemoveChild$3.call(parentEl, anchors[i]);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        firstNode() {\r\n            const child = this.children[0];\r\n            return child ? child.firstNode() : this.anchors[0];\r\n        }\r\n        toString() {\r\n            return this.children.map((c) => (c ? c.toString() : \"\")).join(\"\");\r\n        }\r\n    }\r\n    function multi(children) {\r\n        return new VMulti(children);\r\n    }\r\n\r\n    const getDescriptor$2 = (o, p) => Object.getOwnPropertyDescriptor(o, p);\r\n    const nodeProto$3 = Node.prototype;\r\n    const characterDataProto$1 = CharacterData.prototype;\r\n    const nodeInsertBefore$2 = nodeProto$3.insertBefore;\r\n    const characterDataSetData$1 = getDescriptor$2(characterDataProto$1, \"data\").set;\r\n    const nodeRemoveChild$2 = nodeProto$3.removeChild;\r\n    class VSimpleNode {\r\n        constructor(text) {\r\n            this.text = text;\r\n        }\r\n        mountNode(node, parent, afterNode) {\r\n            this.parentEl = parent;\r\n            nodeInsertBefore$2.call(parent, node, afterNode);\r\n            this.el = node;\r\n        }\r\n        moveBeforeDOMNode(node, parent = this.parentEl) {\r\n            this.parentEl = parent;\r\n            nodeInsertBefore$2.call(parent, this.el, node);\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            nodeInsertBefore$2.call(this.parentEl, this.el, other ? other.el : afterNode);\r\n        }\r\n        beforeRemove() { }\r\n        remove() {\r\n            nodeRemoveChild$2.call(this.parentEl, this.el);\r\n        }\r\n        firstNode() {\r\n            return this.el;\r\n        }\r\n        toString() {\r\n            return this.text;\r\n        }\r\n    }\r\n    class VText$1 extends VSimpleNode {\r\n        mount(parent, afterNode) {\r\n            this.mountNode(document.createTextNode(toText(this.text)), parent, afterNode);\r\n        }\r\n        patch(other) {\r\n            const text2 = other.text;\r\n            if (this.text !== text2) {\r\n                characterDataSetData$1.call(this.el, toText(text2));\r\n                this.text = text2;\r\n            }\r\n        }\r\n    }\r\n    class VComment extends VSimpleNode {\r\n        mount(parent, afterNode) {\r\n            this.mountNode(document.createComment(toText(this.text)), parent, afterNode);\r\n        }\r\n        patch() { }\r\n    }\r\n    function text(str) {\r\n        return new VText$1(str);\r\n    }\r\n    function comment(str) {\r\n        return new VComment(str);\r\n    }\r\n    function toText(value) {\r\n        switch (typeof value) {\r\n            case \"string\":\r\n                return value;\r\n            case \"number\":\r\n                return String(value);\r\n            case \"boolean\":\r\n                return value ? \"true\" : \"false\";\r\n            default:\r\n                return value || \"\";\r\n        }\r\n    }\r\n\r\n    const getDescriptor$1 = (o, p) => Object.getOwnPropertyDescriptor(o, p);\r\n    const nodeProto$2 = Node.prototype;\r\n    const elementProto = Element.prototype;\r\n    const characterDataProto = CharacterData.prototype;\r\n    const characterDataSetData = getDescriptor$1(characterDataProto, \"data\").set;\r\n    const nodeGetFirstChild = getDescriptor$1(nodeProto$2, \"firstChild\").get;\r\n    const nodeGetNextSibling = getDescriptor$1(nodeProto$2, \"nextSibling\").get;\r\n    const NO_OP = () => { };\r\n    function makePropSetter(name) {\r\n        return function setProp(value) {\r\n            // support 0, fallback to empty string for other falsy values\r\n            this[name] = value === 0 ? 0 : value ? value.valueOf() : \"\";\r\n        };\r\n    }\r\n    const cache$1 = {};\r\n    /**\r\n     * Compiling blocks is a multi-step process:\r\n     *\r\n     * 1. build an IntermediateTree from the HTML element. This intermediate tree\r\n     *    is a binary tree structure that encode dynamic info sub nodes, and the\r\n     *    path required to reach them\r\n     * 2. process the tree to build a block context, which is an object that aggregate\r\n     *    all dynamic info in a list, and also, all ref indexes.\r\n     * 3. process the context to build appropriate builder/setter functions\r\n     * 4. make a dynamic block class, which will efficiently collect references and\r\n     *    create/update dynamic locations/children\r\n     *\r\n     * @param str\r\n     * @returns a new block type, that can build concrete blocks\r\n     */\r\n    function createBlock(str) {\r\n        if (str in cache$1) {\r\n            return cache$1[str];\r\n        }\r\n        // step 0: prepare html base element\r\n        const doc = new DOMParser().parseFromString(`<t>${str}</t>`, \"text/xml\");\r\n        const node = doc.firstChild.firstChild;\r\n        if (config.shouldNormalizeDom) {\r\n            normalizeNode(node);\r\n        }\r\n        // step 1: prepare intermediate tree\r\n        const tree = buildTree(node);\r\n        // step 2: prepare block context\r\n        const context = buildContext(tree);\r\n        // step 3: build the final block class\r\n        const template = tree.el;\r\n        const Block = buildBlock(template, context);\r\n        cache$1[str] = Block;\r\n        return Block;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Helper\r\n    // -----------------------------------------------------------------------------\r\n    function normalizeNode(node) {\r\n        if (node.nodeType === Node.TEXT_NODE) {\r\n            if (!/\\S/.test(node.textContent)) {\r\n                node.remove();\r\n                return;\r\n            }\r\n        }\r\n        if (node.nodeType === Node.ELEMENT_NODE) {\r\n            if (node.tagName === \"pre\") {\r\n                return;\r\n            }\r\n        }\r\n        for (let i = node.childNodes.length - 1; i >= 0; --i) {\r\n            normalizeNode(node.childNodes.item(i));\r\n        }\r\n    }\r\n    function buildTree(node, parent = null, domParentTree = null) {\r\n        switch (node.nodeType) {\r\n            case Node.ELEMENT_NODE: {\r\n                // HTMLElement\r\n                let currentNS = domParentTree && domParentTree.currentNS;\r\n                const tagName = node.tagName;\r\n                let el = undefined;\r\n                const info = [];\r\n                if (tagName.startsWith(\"block-text-\")) {\r\n                    const index = parseInt(tagName.slice(11), 10);\r\n                    info.push({ type: \"text\", idx: index });\r\n                    el = document.createTextNode(\"\");\r\n                }\r\n                if (tagName.startsWith(\"block-child-\")) {\r\n                    if (!domParentTree.isRef) {\r\n                        addRef(domParentTree);\r\n                    }\r\n                    const index = parseInt(tagName.slice(12), 10);\r\n                    info.push({ type: \"child\", idx: index });\r\n                    el = document.createTextNode(\"\");\r\n                }\r\n                currentNS || (currentNS = node.namespaceURI);\r\n                if (!el) {\r\n                    el = currentNS\r\n                        ? document.createElementNS(currentNS, tagName)\r\n                        : document.createElement(tagName);\r\n                }\r\n                if (el instanceof Element) {\r\n                    if (!domParentTree) {\r\n                        // some html elements may have side effects when setting their attributes.\r\n                        // For example, setting the src attribute of an <img/> will trigger a\r\n                        // request to get the corresponding image. This is something that we\r\n                        // don't want at compile time. We avoid that by putting the content of\r\n                        // the block in a <template/> element\r\n                        const fragment = document.createElement(\"template\").content;\r\n                        fragment.appendChild(el);\r\n                    }\r\n                    const attrs = node.attributes;\r\n                    for (let i = 0; i < attrs.length; i++) {\r\n                        const attrName = attrs[i].name;\r\n                        const attrValue = attrs[i].value;\r\n                        if (attrName.startsWith(\"block-handler-\")) {\r\n                            const idx = parseInt(attrName.slice(14), 10);\r\n                            info.push({\r\n                                type: \"handler\",\r\n                                idx,\r\n                                event: attrValue,\r\n                            });\r\n                        }\r\n                        else if (attrName.startsWith(\"block-attribute-\")) {\r\n                            const idx = parseInt(attrName.slice(16), 10);\r\n                            info.push({\r\n                                type: \"attribute\",\r\n                                idx,\r\n                                name: attrValue,\r\n                                tag: tagName,\r\n                            });\r\n                        }\r\n                        else if (attrName.startsWith(\"block-property-\")) {\r\n                            const idx = parseInt(attrName.slice(15), 10);\r\n                            info.push({\r\n                                type: \"property\",\r\n                                idx,\r\n                                name: attrValue,\r\n                                tag: tagName,\r\n                            });\r\n                        }\r\n                        else if (attrName === \"block-attributes\") {\r\n                            info.push({\r\n                                type: \"attributes\",\r\n                                idx: parseInt(attrValue, 10),\r\n                            });\r\n                        }\r\n                        else if (attrName === \"block-ref\") {\r\n                            info.push({\r\n                                type: \"ref\",\r\n                                idx: parseInt(attrValue, 10),\r\n                            });\r\n                        }\r\n                        else {\r\n                            el.setAttribute(attrs[i].name, attrValue);\r\n                        }\r\n                    }\r\n                }\r\n                const tree = {\r\n                    parent,\r\n                    firstChild: null,\r\n                    nextSibling: null,\r\n                    el,\r\n                    info,\r\n                    refN: 0,\r\n                    currentNS,\r\n                };\r\n                if (node.firstChild) {\r\n                    const childNode = node.childNodes[0];\r\n                    if (node.childNodes.length === 1 &&\r\n                        childNode.nodeType === Node.ELEMENT_NODE &&\r\n                        childNode.tagName.startsWith(\"block-child-\")) {\r\n                        const tagName = childNode.tagName;\r\n                        const index = parseInt(tagName.slice(12), 10);\r\n                        info.push({ idx: index, type: \"child\", isOnlyChild: true });\r\n                    }\r\n                    else {\r\n                        tree.firstChild = buildTree(node.firstChild, tree, tree);\r\n                        el.appendChild(tree.firstChild.el);\r\n                        let curNode = node.firstChild;\r\n                        let curTree = tree.firstChild;\r\n                        while ((curNode = curNode.nextSibling)) {\r\n                            curTree.nextSibling = buildTree(curNode, curTree, tree);\r\n                            el.appendChild(curTree.nextSibling.el);\r\n                            curTree = curTree.nextSibling;\r\n                        }\r\n                    }\r\n                }\r\n                if (tree.info.length) {\r\n                    addRef(tree);\r\n                }\r\n                return tree;\r\n            }\r\n            case Node.TEXT_NODE:\r\n            case Node.COMMENT_NODE: {\r\n                // text node or comment node\r\n                const el = node.nodeType === Node.TEXT_NODE\r\n                    ? document.createTextNode(node.textContent)\r\n                    : document.createComment(node.textContent);\r\n                return {\r\n                    parent: parent,\r\n                    firstChild: null,\r\n                    nextSibling: null,\r\n                    el,\r\n                    info: [],\r\n                    refN: 0,\r\n                    currentNS: null,\r\n                };\r\n            }\r\n        }\r\n        throw new OwlError(\"boom\");\r\n    }\r\n    function addRef(tree) {\r\n        tree.isRef = true;\r\n        do {\r\n            tree.refN++;\r\n        } while ((tree = tree.parent));\r\n    }\r\n    function parentTree(tree) {\r\n        let parent = tree.parent;\r\n        while (parent && parent.nextSibling === tree) {\r\n            tree = parent;\r\n            parent = parent.parent;\r\n        }\r\n        return parent;\r\n    }\r\n    function buildContext(tree, ctx, fromIdx) {\r\n        if (!ctx) {\r\n            const children = new Array(tree.info.filter((v) => v.type === \"child\").length);\r\n            ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN, refList: [] };\r\n            fromIdx = 0;\r\n        }\r\n        if (tree.refN) {\r\n            const initialIdx = fromIdx;\r\n            const isRef = tree.isRef;\r\n            const firstChild = tree.firstChild ? tree.firstChild.refN : 0;\r\n            const nextSibling = tree.nextSibling ? tree.nextSibling.refN : 0;\r\n            //node\r\n            if (isRef) {\r\n                for (let info of tree.info) {\r\n                    info.refIdx = initialIdx;\r\n                }\r\n                tree.refIdx = initialIdx;\r\n                updateCtx(ctx, tree);\r\n                fromIdx++;\r\n            }\r\n            // right\r\n            if (nextSibling) {\r\n                const idx = fromIdx + firstChild;\r\n                ctx.collectors.push({ idx, prevIdx: initialIdx, getVal: nodeGetNextSibling });\r\n                buildContext(tree.nextSibling, ctx, idx);\r\n            }\r\n            // left\r\n            if (firstChild) {\r\n                ctx.collectors.push({ idx: fromIdx, prevIdx: initialIdx, getVal: nodeGetFirstChild });\r\n                buildContext(tree.firstChild, ctx, fromIdx);\r\n            }\r\n        }\r\n        return ctx;\r\n    }\r\n    function updateCtx(ctx, tree) {\r\n        for (let info of tree.info) {\r\n            switch (info.type) {\r\n                case \"text\":\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx: info.refIdx,\r\n                        setData: setText,\r\n                        updateData: setText,\r\n                    });\r\n                    break;\r\n                case \"child\":\r\n                    if (info.isOnlyChild) {\r\n                        // tree is the parentnode here\r\n                        ctx.children[info.idx] = {\r\n                            parentRefIdx: info.refIdx,\r\n                            isOnlyChild: true,\r\n                        };\r\n                    }\r\n                    else {\r\n                        // tree is the anchor text node\r\n                        ctx.children[info.idx] = {\r\n                            parentRefIdx: parentTree(tree).refIdx,\r\n                            afterRefIdx: info.refIdx,\r\n                        };\r\n                    }\r\n                    break;\r\n                case \"property\": {\r\n                    const refIdx = info.refIdx;\r\n                    const setProp = makePropSetter(info.name);\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx,\r\n                        setData: setProp,\r\n                        updateData: setProp,\r\n                    });\r\n                    break;\r\n                }\r\n                case \"attribute\": {\r\n                    const refIdx = info.refIdx;\r\n                    let updater;\r\n                    let setter;\r\n                    if (info.name === \"class\") {\r\n                        setter = setClass;\r\n                        updater = updateClass;\r\n                    }\r\n                    else {\r\n                        setter = createAttrUpdater(info.name);\r\n                        updater = setter;\r\n                    }\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx,\r\n                        setData: setter,\r\n                        updateData: updater,\r\n                    });\r\n                    break;\r\n                }\r\n                case \"attributes\":\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx: info.refIdx,\r\n                        setData: attrsSetter,\r\n                        updateData: attrsUpdater,\r\n                    });\r\n                    break;\r\n                case \"handler\": {\r\n                    const { setup, update } = createEventHandler(info.event);\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx: info.refIdx,\r\n                        setData: setup,\r\n                        updateData: update,\r\n                    });\r\n                    break;\r\n                }\r\n                case \"ref\":\r\n                    const index = ctx.cbRefs.push(info.idx) - 1;\r\n                    ctx.locations.push({\r\n                        idx: info.idx,\r\n                        refIdx: info.refIdx,\r\n                        setData: makeRefSetter(index, ctx.refList),\r\n                        updateData: NO_OP,\r\n                    });\r\n            }\r\n        }\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // building the concrete block class\r\n    // -----------------------------------------------------------------------------\r\n    function buildBlock(template, ctx) {\r\n        let B = createBlockClass(template, ctx);\r\n        if (ctx.cbRefs.length) {\r\n            const cbRefs = ctx.cbRefs;\r\n            const refList = ctx.refList;\r\n            let cbRefsNumber = cbRefs.length;\r\n            B = class extends B {\r\n                mount(parent, afterNode) {\r\n                    refList.push(new Array(cbRefsNumber));\r\n                    super.mount(parent, afterNode);\r\n                    for (let cbRef of refList.pop()) {\r\n                        cbRef();\r\n                    }\r\n                }\r\n                remove() {\r\n                    super.remove();\r\n                    for (let cbRef of cbRefs) {\r\n                        let fn = this.data[cbRef];\r\n                        fn(null);\r\n                    }\r\n                }\r\n            };\r\n        }\r\n        if (ctx.children.length) {\r\n            B = class extends B {\r\n                constructor(data, children) {\r\n                    super(data);\r\n                    this.children = children;\r\n                }\r\n            };\r\n            B.prototype.beforeRemove = VMulti.prototype.beforeRemove;\r\n            return (data, children = []) => new B(data, children);\r\n        }\r\n        return (data) => new B(data);\r\n    }\r\n    function createBlockClass(template, ctx) {\r\n        const { refN, collectors, children } = ctx;\r\n        const colN = collectors.length;\r\n        ctx.locations.sort((a, b) => a.idx - b.idx);\r\n        const locations = ctx.locations.map((loc) => ({\r\n            refIdx: loc.refIdx,\r\n            setData: loc.setData,\r\n            updateData: loc.updateData,\r\n        }));\r\n        const locN = locations.length;\r\n        const childN = children.length;\r\n        const childrenLocs = children;\r\n        const isDynamic = refN > 0;\r\n        // these values are defined here to make them faster to lookup in the class\r\n        // block scope\r\n        const nodeCloneNode = nodeProto$2.cloneNode;\r\n        const nodeInsertBefore = nodeProto$2.insertBefore;\r\n        const elementRemove = elementProto.remove;\r\n        class Block {\r\n            constructor(data) {\r\n                this.data = data;\r\n            }\r\n            beforeRemove() { }\r\n            remove() {\r\n                elementRemove.call(this.el);\r\n            }\r\n            firstNode() {\r\n                return this.el;\r\n            }\r\n            moveBeforeDOMNode(node, parent = this.parentEl) {\r\n                this.parentEl = parent;\r\n                nodeInsertBefore.call(parent, this.el, node);\r\n            }\r\n            moveBeforeVNode(other, afterNode) {\r\n                nodeInsertBefore.call(this.parentEl, this.el, other ? other.el : afterNode);\r\n            }\r\n            toString() {\r\n                const div = document.createElement(\"div\");\r\n                this.mount(div, null);\r\n                return div.innerHTML;\r\n            }\r\n            mount(parent, afterNode) {\r\n                const el = nodeCloneNode.call(template, true);\r\n                nodeInsertBefore.call(parent, el, afterNode);\r\n                this.el = el;\r\n                this.parentEl = parent;\r\n            }\r\n            patch(other, withBeforeRemove) { }\r\n        }\r\n        if (isDynamic) {\r\n            Block.prototype.mount = function mount(parent, afterNode) {\r\n                const el = nodeCloneNode.call(template, true);\r\n                // collecting references\r\n                const refs = new Array(refN);\r\n                this.refs = refs;\r\n                refs[0] = el;\r\n                for (let i = 0; i < colN; i++) {\r\n                    const w = collectors[i];\r\n                    refs[w.idx] = w.getVal.call(refs[w.prevIdx]);\r\n                }\r\n                // applying data to all update points\r\n                if (locN) {\r\n                    const data = this.data;\r\n                    for (let i = 0; i < locN; i++) {\r\n                        const loc = locations[i];\r\n                        loc.setData.call(refs[loc.refIdx], data[i]);\r\n                    }\r\n                }\r\n                nodeInsertBefore.call(parent, el, afterNode);\r\n                // preparing all children\r\n                if (childN) {\r\n                    const children = this.children;\r\n                    for (let i = 0; i < childN; i++) {\r\n                        const child = children[i];\r\n                        if (child) {\r\n                            const loc = childrenLocs[i];\r\n                            const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null;\r\n                            child.isOnlyChild = loc.isOnlyChild;\r\n                            child.mount(refs[loc.parentRefIdx], afterNode);\r\n                        }\r\n                    }\r\n                }\r\n                this.el = el;\r\n                this.parentEl = parent;\r\n            };\r\n            Block.prototype.patch = function patch(other, withBeforeRemove) {\r\n                if (this === other) {\r\n                    return;\r\n                }\r\n                const refs = this.refs;\r\n                // update texts/attributes/\r\n                if (locN) {\r\n                    const data1 = this.data;\r\n                    const data2 = other.data;\r\n                    for (let i = 0; i < locN; i++) {\r\n                        const val1 = data1[i];\r\n                        const val2 = data2[i];\r\n                        if (val1 !== val2) {\r\n                            const loc = locations[i];\r\n                            loc.updateData.call(refs[loc.refIdx], val2, val1);\r\n                        }\r\n                    }\r\n                    this.data = data2;\r\n                }\r\n                // update children\r\n                if (childN) {\r\n                    let children1 = this.children;\r\n                    const children2 = other.children;\r\n                    for (let i = 0; i < childN; i++) {\r\n                        const child1 = children1[i];\r\n                        const child2 = children2[i];\r\n                        if (child1) {\r\n                            if (child2) {\r\n                                child1.patch(child2, withBeforeRemove);\r\n                            }\r\n                            else {\r\n                                if (withBeforeRemove) {\r\n                                    child1.beforeRemove();\r\n                                }\r\n                                child1.remove();\r\n                                children1[i] = undefined;\r\n                            }\r\n                        }\r\n                        else if (child2) {\r\n                            const loc = childrenLocs[i];\r\n                            const afterNode = loc.afterRefIdx ? refs[loc.afterRefIdx] : null;\r\n                            child2.mount(refs[loc.parentRefIdx], afterNode);\r\n                            children1[i] = child2;\r\n                        }\r\n                    }\r\n                }\r\n            };\r\n        }\r\n        return Block;\r\n    }\r\n    function setText(value) {\r\n        characterDataSetData.call(this, toText(value));\r\n    }\r\n    function makeRefSetter(index, refs) {\r\n        return function setRef(fn) {\r\n            refs[refs.length - 1][index] = () => fn(this);\r\n        };\r\n    }\r\n\r\n    const getDescriptor = (o, p) => Object.getOwnPropertyDescriptor(o, p);\r\n    const nodeProto$1 = Node.prototype;\r\n    const nodeInsertBefore$1 = nodeProto$1.insertBefore;\r\n    const nodeAppendChild = nodeProto$1.appendChild;\r\n    const nodeRemoveChild$1 = nodeProto$1.removeChild;\r\n    const nodeSetTextContent = getDescriptor(nodeProto$1, \"textContent\").set;\r\n    // -----------------------------------------------------------------------------\r\n    // List Node\r\n    // -----------------------------------------------------------------------------\r\n    class VList {\r\n        constructor(children) {\r\n            this.children = children;\r\n        }\r\n        mount(parent, afterNode) {\r\n            const children = this.children;\r\n            const _anchor = document.createTextNode(\"\");\r\n            this.anchor = _anchor;\r\n            nodeInsertBefore$1.call(parent, _anchor, afterNode);\r\n            const l = children.length;\r\n            if (l) {\r\n                const mount = children[0].mount;\r\n                for (let i = 0; i < l; i++) {\r\n                    mount.call(children[i], parent, _anchor);\r\n                }\r\n            }\r\n            this.parentEl = parent;\r\n        }\r\n        moveBeforeDOMNode(node, parent = this.parentEl) {\r\n            this.parentEl = parent;\r\n            const children = this.children;\r\n            for (let i = 0, l = children.length; i < l; i++) {\r\n                children[i].moveBeforeDOMNode(node, parent);\r\n            }\r\n            parent.insertBefore(this.anchor, node);\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            if (other) {\r\n                const next = other.children[0];\r\n                afterNode = (next ? next.firstNode() : other.anchor) || null;\r\n            }\r\n            const children = this.children;\r\n            for (let i = 0, l = children.length; i < l; i++) {\r\n                children[i].moveBeforeVNode(null, afterNode);\r\n            }\r\n            this.parentEl.insertBefore(this.anchor, afterNode);\r\n        }\r\n        patch(other, withBeforeRemove) {\r\n            if (this === other) {\r\n                return;\r\n            }\r\n            const ch1 = this.children;\r\n            const ch2 = other.children;\r\n            if (ch2.length === 0 && ch1.length === 0) {\r\n                return;\r\n            }\r\n            this.children = ch2;\r\n            const proto = ch2[0] || ch1[0];\r\n            const { mount: cMount, patch: cPatch, remove: cRemove, beforeRemove, moveBeforeVNode: cMoveBefore, firstNode: cFirstNode, } = proto;\r\n            const _anchor = this.anchor;\r\n            const isOnlyChild = this.isOnlyChild;\r\n            const parent = this.parentEl;\r\n            // fast path: no new child => only remove\r\n            if (ch2.length === 0 && isOnlyChild) {\r\n                if (withBeforeRemove) {\r\n                    for (let i = 0, l = ch1.length; i < l; i++) {\r\n                        beforeRemove.call(ch1[i]);\r\n                    }\r\n                }\r\n                nodeSetTextContent.call(parent, \"\");\r\n                nodeAppendChild.call(parent, _anchor);\r\n                return;\r\n            }\r\n            let startIdx1 = 0;\r\n            let startIdx2 = 0;\r\n            let startVn1 = ch1[0];\r\n            let startVn2 = ch2[0];\r\n            let endIdx1 = ch1.length - 1;\r\n            let endIdx2 = ch2.length - 1;\r\n            let endVn1 = ch1[endIdx1];\r\n            let endVn2 = ch2[endIdx2];\r\n            let mapping = undefined;\r\n            while (startIdx1 <= endIdx1 && startIdx2 <= endIdx2) {\r\n                // -------------------------------------------------------------------\r\n                if (startVn1 === null) {\r\n                    startVn1 = ch1[++startIdx1];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                if (endVn1 === null) {\r\n                    endVn1 = ch1[--endIdx1];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                let startKey1 = startVn1.key;\r\n                let startKey2 = startVn2.key;\r\n                if (startKey1 === startKey2) {\r\n                    cPatch.call(startVn1, startVn2, withBeforeRemove);\r\n                    ch2[startIdx2] = startVn1;\r\n                    startVn1 = ch1[++startIdx1];\r\n                    startVn2 = ch2[++startIdx2];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                let endKey1 = endVn1.key;\r\n                let endKey2 = endVn2.key;\r\n                if (endKey1 === endKey2) {\r\n                    cPatch.call(endVn1, endVn2, withBeforeRemove);\r\n                    ch2[endIdx2] = endVn1;\r\n                    endVn1 = ch1[--endIdx1];\r\n                    endVn2 = ch2[--endIdx2];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                if (startKey1 === endKey2) {\r\n                    // bnode moved right\r\n                    cPatch.call(startVn1, endVn2, withBeforeRemove);\r\n                    ch2[endIdx2] = startVn1;\r\n                    const nextChild = ch2[endIdx2 + 1];\r\n                    cMoveBefore.call(startVn1, nextChild, _anchor);\r\n                    startVn1 = ch1[++startIdx1];\r\n                    endVn2 = ch2[--endIdx2];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                if (endKey1 === startKey2) {\r\n                    // bnode moved left\r\n                    cPatch.call(endVn1, startVn2, withBeforeRemove);\r\n                    ch2[startIdx2] = endVn1;\r\n                    const nextChild = ch1[startIdx1];\r\n                    cMoveBefore.call(endVn1, nextChild, _anchor);\r\n                    endVn1 = ch1[--endIdx1];\r\n                    startVn2 = ch2[++startIdx2];\r\n                    continue;\r\n                }\r\n                // -------------------------------------------------------------------\r\n                mapping = mapping || createMapping(ch1, startIdx1, endIdx1);\r\n                let idxInOld = mapping[startKey2];\r\n                if (idxInOld === undefined) {\r\n                    cMount.call(startVn2, parent, cFirstNode.call(startVn1) || null);\r\n                }\r\n                else {\r\n                    const elmToMove = ch1[idxInOld];\r\n                    cMoveBefore.call(elmToMove, startVn1, null);\r\n                    cPatch.call(elmToMove, startVn2, withBeforeRemove);\r\n                    ch2[startIdx2] = elmToMove;\r\n                    ch1[idxInOld] = null;\r\n                }\r\n                startVn2 = ch2[++startIdx2];\r\n            }\r\n            // ---------------------------------------------------------------------\r\n            if (startIdx1 <= endIdx1 || startIdx2 <= endIdx2) {\r\n                if (startIdx1 > endIdx1) {\r\n                    const nextChild = ch2[endIdx2 + 1];\r\n                    const anchor = nextChild ? cFirstNode.call(nextChild) || null : _anchor;\r\n                    for (let i = startIdx2; i <= endIdx2; i++) {\r\n                        cMount.call(ch2[i], parent, anchor);\r\n                    }\r\n                }\r\n                else {\r\n                    for (let i = startIdx1; i <= endIdx1; i++) {\r\n                        let ch = ch1[i];\r\n                        if (ch) {\r\n                            if (withBeforeRemove) {\r\n                                beforeRemove.call(ch);\r\n                            }\r\n                            cRemove.call(ch);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        beforeRemove() {\r\n            const children = this.children;\r\n            const l = children.length;\r\n            if (l) {\r\n                const beforeRemove = children[0].beforeRemove;\r\n                for (let i = 0; i < l; i++) {\r\n                    beforeRemove.call(children[i]);\r\n                }\r\n            }\r\n        }\r\n        remove() {\r\n            const { parentEl, anchor } = this;\r\n            if (this.isOnlyChild) {\r\n                nodeSetTextContent.call(parentEl, \"\");\r\n            }\r\n            else {\r\n                const children = this.children;\r\n                const l = children.length;\r\n                if (l) {\r\n                    const remove = children[0].remove;\r\n                    for (let i = 0; i < l; i++) {\r\n                        remove.call(children[i]);\r\n                    }\r\n                }\r\n                nodeRemoveChild$1.call(parentEl, anchor);\r\n            }\r\n        }\r\n        firstNode() {\r\n            const child = this.children[0];\r\n            return child ? child.firstNode() : undefined;\r\n        }\r\n        toString() {\r\n            return this.children.map((c) => c.toString()).join(\"\");\r\n        }\r\n    }\r\n    function list(children) {\r\n        return new VList(children);\r\n    }\r\n    function createMapping(ch1, startIdx1, endIdx2) {\r\n        let mapping = {};\r\n        for (let i = startIdx1; i <= endIdx2; i++) {\r\n            mapping[ch1[i].key] = i;\r\n        }\r\n        return mapping;\r\n    }\r\n\r\n    const nodeProto = Node.prototype;\r\n    const nodeInsertBefore = nodeProto.insertBefore;\r\n    const nodeRemoveChild = nodeProto.removeChild;\r\n    class VHtml {\r\n        constructor(html) {\r\n            this.content = [];\r\n            this.html = html;\r\n        }\r\n        mount(parent, afterNode) {\r\n            this.parentEl = parent;\r\n            const template = document.createElement(\"template\");\r\n            template.innerHTML = this.html;\r\n            this.content = [...template.content.childNodes];\r\n            for (let elem of this.content) {\r\n                nodeInsertBefore.call(parent, elem, afterNode);\r\n            }\r\n            if (!this.content.length) {\r\n                const textNode = document.createTextNode(\"\");\r\n                this.content.push(textNode);\r\n                nodeInsertBefore.call(parent, textNode, afterNode);\r\n            }\r\n        }\r\n        moveBeforeDOMNode(node, parent = this.parentEl) {\r\n            this.parentEl = parent;\r\n            for (let elem of this.content) {\r\n                nodeInsertBefore.call(parent, elem, node);\r\n            }\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            const target = other ? other.content[0] : afterNode;\r\n            this.moveBeforeDOMNode(target);\r\n        }\r\n        patch(other) {\r\n            if (this === other) {\r\n                return;\r\n            }\r\n            const html2 = other.html;\r\n            if (this.html !== html2) {\r\n                const parent = this.parentEl;\r\n                // insert new html in front of current\r\n                const afterNode = this.content[0];\r\n                const template = document.createElement(\"template\");\r\n                template.innerHTML = html2;\r\n                const content = [...template.content.childNodes];\r\n                for (let elem of content) {\r\n                    nodeInsertBefore.call(parent, elem, afterNode);\r\n                }\r\n                if (!content.length) {\r\n                    const textNode = document.createTextNode(\"\");\r\n                    content.push(textNode);\r\n                    nodeInsertBefore.call(parent, textNode, afterNode);\r\n                }\r\n                // remove current content\r\n                this.remove();\r\n                this.content = content;\r\n                this.html = other.html;\r\n            }\r\n        }\r\n        beforeRemove() { }\r\n        remove() {\r\n            const parent = this.parentEl;\r\n            for (let elem of this.content) {\r\n                nodeRemoveChild.call(parent, elem);\r\n            }\r\n        }\r\n        firstNode() {\r\n            return this.content[0];\r\n        }\r\n        toString() {\r\n            return this.html;\r\n        }\r\n    }\r\n    function html(str) {\r\n        return new VHtml(str);\r\n    }\r\n\r\n    function createCatcher(eventsSpec) {\r\n        const n = Object.keys(eventsSpec).length;\r\n        class VCatcher {\r\n            constructor(child, handlers) {\r\n                this.handlerFns = [];\r\n                this.afterNode = null;\r\n                this.child = child;\r\n                this.handlerData = handlers;\r\n            }\r\n            mount(parent, afterNode) {\r\n                this.parentEl = parent;\r\n                this.child.mount(parent, afterNode);\r\n                this.afterNode = document.createTextNode(\"\");\r\n                parent.insertBefore(this.afterNode, afterNode);\r\n                this.wrapHandlerData();\r\n                for (let name in eventsSpec) {\r\n                    const index = eventsSpec[name];\r\n                    const handler = createEventHandler(name);\r\n                    this.handlerFns[index] = handler;\r\n                    handler.setup.call(parent, this.handlerData[index]);\r\n                }\r\n            }\r\n            wrapHandlerData() {\r\n                for (let i = 0; i < n; i++) {\r\n                    let handler = this.handlerData[i];\r\n                    // handler = [...mods, fn, comp], so we need to replace second to last elem\r\n                    let idx = handler.length - 2;\r\n                    let origFn = handler[idx];\r\n                    const self = this;\r\n                    handler[idx] = function (ev) {\r\n                        const target = ev.target;\r\n                        let currentNode = self.child.firstNode();\r\n                        const afterNode = self.afterNode;\r\n                        while (currentNode && currentNode !== afterNode) {\r\n                            if (currentNode.contains(target)) {\r\n                                return origFn.call(this, ev);\r\n                            }\r\n                            currentNode = currentNode.nextSibling;\r\n                        }\r\n                    };\r\n                }\r\n            }\r\n            moveBeforeDOMNode(node, parent = this.parentEl) {\r\n                this.parentEl = parent;\r\n                this.child.moveBeforeDOMNode(node, parent);\r\n                parent.insertBefore(this.afterNode, node);\r\n            }\r\n            moveBeforeVNode(other, afterNode) {\r\n                if (other) {\r\n                    // check this with @ged-odoo for use in foreach\r\n                    afterNode = other.firstNode() || afterNode;\r\n                }\r\n                this.child.moveBeforeVNode(other ? other.child : null, afterNode);\r\n                this.parentEl.insertBefore(this.afterNode, afterNode);\r\n            }\r\n            patch(other, withBeforeRemove) {\r\n                if (this === other) {\r\n                    return;\r\n                }\r\n                this.handlerData = other.handlerData;\r\n                this.wrapHandlerData();\r\n                for (let i = 0; i < n; i++) {\r\n                    this.handlerFns[i].update.call(this.parentEl, this.handlerData[i]);\r\n                }\r\n                this.child.patch(other.child, withBeforeRemove);\r\n            }\r\n            beforeRemove() {\r\n                this.child.beforeRemove();\r\n            }\r\n            remove() {\r\n                for (let i = 0; i < n; i++) {\r\n                    this.handlerFns[i].remove.call(this.parentEl);\r\n                }\r\n                this.child.remove();\r\n                this.afterNode.remove();\r\n            }\r\n            firstNode() {\r\n                return this.child.firstNode();\r\n            }\r\n            toString() {\r\n                return this.child.toString();\r\n            }\r\n        }\r\n        return function (child, handlers) {\r\n            return new VCatcher(child, handlers);\r\n        };\r\n    }\r\n\r\n    function mount$1(vnode, fixture, afterNode = null) {\r\n        vnode.mount(fixture, afterNode);\r\n    }\r\n    function patch(vnode1, vnode2, withBeforeRemove = false) {\r\n        vnode1.patch(vnode2, withBeforeRemove);\r\n    }\r\n    function remove(vnode, withBeforeRemove = false) {\r\n        if (withBeforeRemove) {\r\n            vnode.beforeRemove();\r\n        }\r\n        vnode.remove();\r\n    }\r\n\r\n    // Maps fibers to thrown errors\r\n    const fibersInError = new WeakMap();\r\n    const nodeErrorHandlers = new WeakMap();\r\n    function _handleError(node, error) {\r\n        if (!node) {\r\n            return false;\r\n        }\r\n        const fiber = node.fiber;\r\n        if (fiber) {\r\n            fibersInError.set(fiber, error);\r\n        }\r\n        const errorHandlers = nodeErrorHandlers.get(node);\r\n        if (errorHandlers) {\r\n            let handled = false;\r\n            // execute in the opposite order\r\n            for (let i = errorHandlers.length - 1; i >= 0; i--) {\r\n                try {\r\n                    errorHandlers[i](error);\r\n                    handled = true;\r\n                    break;\r\n                }\r\n                catch (e) {\r\n                    error = e;\r\n                }\r\n            }\r\n            if (handled) {\r\n                return true;\r\n            }\r\n        }\r\n        return _handleError(node.parent, error);\r\n    }\r\n    function handleError(params) {\r\n        let { error } = params;\r\n        // Wrap error if it wasn't wrapped by wrapError (ie when not in dev mode)\r\n        if (!(error instanceof OwlError)) {\r\n            error = Object.assign(new OwlError(`An error occured in the owl lifecycle (see this Error's \"cause\" property)`), { cause: error });\r\n        }\r\n        const node = \"node\" in params ? params.node : params.fiber.node;\r\n        const fiber = \"fiber\" in params ? params.fiber : node.fiber;\r\n        if (fiber) {\r\n            // resets the fibers on components if possible. This is important so that\r\n            // new renderings can be properly included in the initial one, if any.\r\n            let current = fiber;\r\n            do {\r\n                current.node.fiber = current;\r\n                current = current.parent;\r\n            } while (current);\r\n            fibersInError.set(fiber.root, error);\r\n        }\r\n        const handled = _handleError(node, error);\r\n        if (!handled) {\r\n            console.warn(`[Owl] Unhandled error. Destroying the root component`);\r\n            try {\r\n                node.app.destroy();\r\n            }\r\n            catch (e) {\r\n                console.error(e);\r\n            }\r\n            throw error;\r\n        }\r\n    }\r\n\r\n    function makeChildFiber(node, parent) {\r\n        let current = node.fiber;\r\n        if (current) {\r\n            cancelFibers(current.children);\r\n            current.root = null;\r\n        }\r\n        return new Fiber(node, parent);\r\n    }\r\n    function makeRootFiber(node) {\r\n        let current = node.fiber;\r\n        if (current) {\r\n            let root = current.root;\r\n            // lock root fiber because canceling children fibers may destroy components,\r\n            // which means any arbitrary code can be run in onWillDestroy, which may\r\n            // trigger new renderings\r\n            root.locked = true;\r\n            root.setCounter(root.counter + 1 - cancelFibers(current.children));\r\n            root.locked = false;\r\n            current.children = [];\r\n            current.childrenMap = {};\r\n            current.bdom = null;\r\n            if (fibersInError.has(current)) {\r\n                fibersInError.delete(current);\r\n                fibersInError.delete(root);\r\n                current.appliedToDom = false;\r\n                if (current instanceof RootFiber) {\r\n                    // it is possible that this fiber is a fiber that crashed while being\r\n                    // mounted, so the mounted list is possibly corrupted. We restore it to\r\n                    // its normal initial state (which is empty list or a list with a mount\r\n                    // fiber.\r\n                    current.mounted = current instanceof MountFiber ? [current] : [];\r\n                }\r\n            }\r\n            return current;\r\n        }\r\n        const fiber = new RootFiber(node, null);\r\n        if (node.willPatch.length) {\r\n            fiber.willPatch.push(fiber);\r\n        }\r\n        if (node.patched.length) {\r\n            fiber.patched.push(fiber);\r\n        }\r\n        return fiber;\r\n    }\r\n    function throwOnRender() {\r\n        throw new OwlError(\"Attempted to render cancelled fiber\");\r\n    }\r\n    /**\r\n     * @returns number of not-yet rendered fibers cancelled\r\n     */\r\n    function cancelFibers(fibers) {\r\n        let result = 0;\r\n        for (let fiber of fibers) {\r\n            let node = fiber.node;\r\n            fiber.render = throwOnRender;\r\n            if (node.status === 0 /* NEW */) {\r\n                node.cancel();\r\n            }\r\n            node.fiber = null;\r\n            if (fiber.bdom) {\r\n                // if fiber has been rendered, this means that the component props have\r\n                // been updated. however, this fiber will not be patched to the dom, so\r\n                // it could happen that the next render compare the current props with\r\n                // the same props, and skip the render completely. With the next line,\r\n                // we kindly request the component code to force a render, so it works as\r\n                // expected.\r\n                node.forceNextRender = true;\r\n            }\r\n            else {\r\n                result++;\r\n            }\r\n            result += cancelFibers(fiber.children);\r\n        }\r\n        return result;\r\n    }\r\n    class Fiber {\r\n        constructor(node, parent) {\r\n            this.bdom = null;\r\n            this.children = [];\r\n            this.appliedToDom = false;\r\n            this.deep = false;\r\n            this.childrenMap = {};\r\n            this.node = node;\r\n            this.parent = parent;\r\n            if (parent) {\r\n                this.deep = parent.deep;\r\n                const root = parent.root;\r\n                root.setCounter(root.counter + 1);\r\n                this.root = root;\r\n                parent.children.push(this);\r\n            }\r\n            else {\r\n                this.root = this;\r\n            }\r\n        }\r\n        render() {\r\n            // if some parent has a fiber => register in followup\r\n            let prev = this.root.node;\r\n            let scheduler = prev.app.scheduler;\r\n            let current = prev.parent;\r\n            while (current) {\r\n                if (current.fiber) {\r\n                    let root = current.fiber.root;\r\n                    if (root.counter === 0 && prev.parentKey in current.fiber.childrenMap) {\r\n                        current = root.node;\r\n                    }\r\n                    else {\r\n                        scheduler.delayedRenders.push(this);\r\n                        return;\r\n                    }\r\n                }\r\n                prev = current;\r\n                current = current.parent;\r\n            }\r\n            // there are no current rendering from above => we can render\r\n            this._render();\r\n        }\r\n        _render() {\r\n            const node = this.node;\r\n            const root = this.root;\r\n            if (root) {\r\n                try {\r\n                    this.bdom = true;\r\n                    this.bdom = node.renderFn();\r\n                }\r\n                catch (e) {\r\n                    node.app.handleError({ node, error: e });\r\n                }\r\n                root.setCounter(root.counter - 1);\r\n            }\r\n        }\r\n    }\r\n    class RootFiber extends Fiber {\r\n        constructor() {\r\n            super(...arguments);\r\n            this.counter = 1;\r\n            // only add stuff in this if they have registered some hooks\r\n            this.willPatch = [];\r\n            this.patched = [];\r\n            this.mounted = [];\r\n            // A fiber is typically locked when it is completing and the patch has not, or is being applied.\r\n            // i.e.: render triggered in onWillUnmount or in willPatch will be delayed\r\n            this.locked = false;\r\n        }\r\n        complete() {\r\n            const node = this.node;\r\n            this.locked = true;\r\n            let current = undefined;\r\n            let mountedFibers = this.mounted;\r\n            try {\r\n                // Step 1: calling all willPatch lifecycle hooks\r\n                for (current of this.willPatch) {\r\n                    // because of the asynchronous nature of the rendering, some parts of the\r\n                    // UI may have been rendered, then deleted in a followup rendering, and we\r\n                    // do not want to call onWillPatch in that case.\r\n                    let node = current.node;\r\n                    if (node.fiber === current) {\r\n                        const component = node.component;\r\n                        for (let cb of node.willPatch) {\r\n                            cb.call(component);\r\n                        }\r\n                    }\r\n                }\r\n                current = undefined;\r\n                // Step 2: patching the dom\r\n                node._patch();\r\n                this.locked = false;\r\n                // Step 4: calling all mounted lifecycle hooks\r\n                while ((current = mountedFibers.pop())) {\r\n                    current = current;\r\n                    if (current.appliedToDom) {\r\n                        for (let cb of current.node.mounted) {\r\n                            cb();\r\n                        }\r\n                    }\r\n                }\r\n                // Step 5: calling all patched hooks\r\n                let patchedFibers = this.patched;\r\n                while ((current = patchedFibers.pop())) {\r\n                    current = current;\r\n                    if (current.appliedToDom) {\r\n                        for (let cb of current.node.patched) {\r\n                            cb();\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            catch (e) {\r\n                // if mountedFibers is not empty, this means that a crash occured while\r\n                // calling the mounted hooks of some component. So, there may still be\r\n                // some component that have been mounted, but for which the mounted hooks\r\n                // have not been called. Here, we remove the willUnmount hooks for these\r\n                // specific component to prevent a worse situation (willUnmount being\r\n                // called even though mounted has not been called)\r\n                for (let fiber of mountedFibers) {\r\n                    fiber.node.willUnmount = [];\r\n                }\r\n                this.locked = false;\r\n                node.app.handleError({ fiber: current || this, error: e });\r\n            }\r\n        }\r\n        setCounter(newValue) {\r\n            this.counter = newValue;\r\n            if (newValue === 0) {\r\n                this.node.app.scheduler.flush();\r\n            }\r\n        }\r\n    }\r\n    class MountFiber extends RootFiber {\r\n        constructor(node, target, options = {}) {\r\n            super(node, null);\r\n            this.target = target;\r\n            this.position = options.position || \"last-child\";\r\n        }\r\n        complete() {\r\n            let current = this;\r\n            try {\r\n                const node = this.node;\r\n                node.children = this.childrenMap;\r\n                node.app.constructor.validateTarget(this.target);\r\n                if (node.bdom) {\r\n                    // this is a complicated situation: if we mount a fiber with an existing\r\n                    // bdom, this means that this same fiber was already completed, mounted,\r\n                    // but a crash occurred in some mounted hook. Then, it was handled and\r\n                    // the new rendering is being applied.\r\n                    node.updateDom();\r\n                }\r\n                else {\r\n                    node.bdom = this.bdom;\r\n                    if (this.position === \"last-child\" || this.target.childNodes.length === 0) {\r\n                        mount$1(node.bdom, this.target);\r\n                    }\r\n                    else {\r\n                        const firstChild = this.target.childNodes[0];\r\n                        mount$1(node.bdom, this.target, firstChild);\r\n                    }\r\n                }\r\n                // unregistering the fiber before mounted since it can do another render\r\n                // and that the current rendering is obviously completed\r\n                node.fiber = null;\r\n                node.status = 1 /* MOUNTED */;\r\n                this.appliedToDom = true;\r\n                let mountedFibers = this.mounted;\r\n                while ((current = mountedFibers.pop())) {\r\n                    if (current.appliedToDom) {\r\n                        for (let cb of current.node.mounted) {\r\n                            cb();\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            catch (e) {\r\n                this.node.app.handleError({ fiber: current, error: e });\r\n            }\r\n        }\r\n    }\r\n\r\n    // Special key to subscribe to, to be notified of key creation/deletion\r\n    const KEYCHANGES = Symbol(\"Key changes\");\r\n    // Used to specify the absence of a callback, can be used as WeakMap key but\r\n    // should only be used as a sentinel value and never called.\r\n    const NO_CALLBACK = () => {\r\n        throw new Error(\"Called NO_CALLBACK. Owl is broken, please report this to the maintainers.\");\r\n    };\r\n    const objectToString = Object.prototype.toString;\r\n    const objectHasOwnProperty = Object.prototype.hasOwnProperty;\r\n    // Use arrays because Array.includes is faster than Set.has for small arrays\r\n    const SUPPORTED_RAW_TYPES = [\"Object\", \"Array\", \"Set\", \"Map\", \"WeakMap\"];\r\n    const COLLECTION_RAW_TYPES = [\"Set\", \"Map\", \"WeakMap\"];\r\n    /**\r\n     * extract \"RawType\" from strings like \"[object RawType]\" => this lets us ignore\r\n     * many native objects such as Promise (whose toString is [object Promise])\r\n     * or Date ([object Date]), while also supporting collections without using\r\n     * instanceof in a loop\r\n     *\r\n     * @param obj the object to check\r\n     * @returns the raw type of the object\r\n     */\r\n    function rawType(obj) {\r\n        return objectToString.call(toRaw(obj)).slice(8, -1);\r\n    }\r\n    /**\r\n     * Checks whether a given value can be made into a reactive object.\r\n     *\r\n     * @param value the value to check\r\n     * @returns whether the value can be made reactive\r\n     */\r\n    function canBeMadeReactive(value) {\r\n        if (typeof value !== \"object\") {\r\n            return false;\r\n        }\r\n        return SUPPORTED_RAW_TYPES.includes(rawType(value));\r\n    }\r\n    /**\r\n     * Creates a reactive from the given object/callback if possible and returns it,\r\n     * returns the original object otherwise.\r\n     *\r\n     * @param value the value make reactive\r\n     * @returns a reactive for the given object when possible, the original otherwise\r\n     */\r\n    function possiblyReactive(val, cb) {\r\n        return canBeMadeReactive(val) ? reactive(val, cb) : val;\r\n    }\r\n    const skipped = new WeakSet();\r\n    /**\r\n     * Mark an object or array so that it is ignored by the reactivity system\r\n     *\r\n     * @param value the value to mark\r\n     * @returns the object itself\r\n     */\r\n    function markRaw(value) {\r\n        skipped.add(value);\r\n        return value;\r\n    }\r\n    /**\r\n     * Given a reactive objet, return the raw (non reactive) underlying object\r\n     *\r\n     * @param value a reactive value\r\n     * @returns the underlying value\r\n     */\r\n    function toRaw(value) {\r\n        return targets.has(value) ? targets.get(value) : value;\r\n    }\r\n    const targetToKeysToCallbacks = new WeakMap();\r\n    /**\r\n     * Observes a given key on a target with an callback. The callback will be\r\n     * called when the given key changes on the target.\r\n     *\r\n     * @param target the target whose key should be observed\r\n     * @param key the key to observe (or Symbol(KEYCHANGES) for key creation\r\n     *  or deletion)\r\n     * @param callback the function to call when the key changes\r\n     */\r\n    function observeTargetKey(target, key, callback) {\r\n        if (callback === NO_CALLBACK) {\r\n            return;\r\n        }\r\n        if (!targetToKeysToCallbacks.get(target)) {\r\n            targetToKeysToCallbacks.set(target, new Map());\r\n        }\r\n        const keyToCallbacks = targetToKeysToCallbacks.get(target);\r\n        if (!keyToCallbacks.get(key)) {\r\n            keyToCallbacks.set(key, new Set());\r\n        }\r\n        keyToCallbacks.get(key).add(callback);\r\n        if (!callbacksToTargets.has(callback)) {\r\n            callbacksToTargets.set(callback, new Set());\r\n        }\r\n        callbacksToTargets.get(callback).add(target);\r\n    }\r\n    /**\r\n     * Notify Reactives that are observing a given target that a key has changed on\r\n     * the target.\r\n     *\r\n     * @param target target whose Reactives should be notified that the target was\r\n     *  changed.\r\n     * @param key the key that changed (or Symbol `KEYCHANGES` if a key was created\r\n     *   or deleted)\r\n     */\r\n    function notifyReactives(target, key) {\r\n        const keyToCallbacks = targetToKeysToCallbacks.get(target);\r\n        if (!keyToCallbacks) {\r\n            return;\r\n        }\r\n        const callbacks = keyToCallbacks.get(key);\r\n        if (!callbacks) {\r\n            return;\r\n        }\r\n        // Loop on copy because clearReactivesForCallback will modify the set in place\r\n        for (const callback of [...callbacks]) {\r\n            clearReactivesForCallback(callback);\r\n            callback();\r\n        }\r\n    }\r\n    const callbacksToTargets = new WeakMap();\r\n    /**\r\n     * Clears all subscriptions of the Reactives associated with a given callback.\r\n     *\r\n     * @param callback the callback for which the reactives need to be cleared\r\n     */\r\n    function clearReactivesForCallback(callback) {\r\n        const targetsToClear = callbacksToTargets.get(callback);\r\n        if (!targetsToClear) {\r\n            return;\r\n        }\r\n        for (const target of targetsToClear) {\r\n            const observedKeys = targetToKeysToCallbacks.get(target);\r\n            if (!observedKeys) {\r\n                continue;\r\n            }\r\n            for (const [key, callbacks] of observedKeys.entries()) {\r\n                callbacks.delete(callback);\r\n                if (!callbacks.size) {\r\n                    observedKeys.delete(key);\r\n                }\r\n            }\r\n        }\r\n        targetsToClear.clear();\r\n    }\r\n    function getSubscriptions(callback) {\r\n        const targets = callbacksToTargets.get(callback) || [];\r\n        return [...targets].map((target) => {\r\n            const keysToCallbacks = targetToKeysToCallbacks.get(target);\r\n            let keys = [];\r\n            if (keysToCallbacks) {\r\n                for (const [key, cbs] of keysToCallbacks) {\r\n                    if (cbs.has(callback)) {\r\n                        keys.push(key);\r\n                    }\r\n                }\r\n            }\r\n            return { target, keys };\r\n        });\r\n    }\r\n    // Maps reactive objects to the underlying target\r\n    const targets = new WeakMap();\r\n    const reactiveCache = new WeakMap();\r\n    /**\r\n     * Creates a reactive proxy for an object. Reading data on the reactive object\r\n     * subscribes to changes to the data. Writing data on the object will cause the\r\n     * notify callback to be called if there are suscriptions to that data. Nested\r\n     * objects and arrays are automatically made reactive as well.\r\n     *\r\n     * Whenever you are notified of a change, all subscriptions are cleared, and if\r\n     * you would like to be notified of any further changes, you should go read\r\n     * the underlying data again. We assume that if you don't go read it again after\r\n     * being notified, it means that you are no longer interested in that data.\r\n     *\r\n     * Subscriptions:\r\n     * + Reading a property on an object will subscribe you to changes in the value\r\n     *    of that property.\r\n     * + Accessing an object's keys (eg with Object.keys or with `for..in`) will\r\n     *    subscribe you to the creation/deletion of keys. Checking the presence of a\r\n     *    key on the object with 'in' has the same effect.\r\n     * - getOwnPropertyDescriptor does not currently subscribe you to the property.\r\n     *    This is a choice that was made because changing a key's value will trigger\r\n     *    this trap and we do not want to subscribe by writes. This also means that\r\n     *    Object.hasOwnProperty doesn't subscribe as it goes through this trap.\r\n     *\r\n     * @param target the object for which to create a reactive proxy\r\n     * @param callback the function to call when an observed property of the\r\n     *  reactive has changed\r\n     * @returns a proxy that tracks changes to it\r\n     */\r\n    function reactive(target, callback = NO_CALLBACK) {\r\n        if (!canBeMadeReactive(target)) {\r\n            throw new OwlError(`Cannot make the given value reactive`);\r\n        }\r\n        if (skipped.has(target)) {\r\n            return target;\r\n        }\r\n        if (targets.has(target)) {\r\n            // target is reactive, create a reactive on the underlying object instead\r\n            return reactive(targets.get(target), callback);\r\n        }\r\n        if (!reactiveCache.has(target)) {\r\n            reactiveCache.set(target, new WeakMap());\r\n        }\r\n        const reactivesForTarget = reactiveCache.get(target);\r\n        if (!reactivesForTarget.has(callback)) {\r\n            const targetRawType = rawType(target);\r\n            const handler = COLLECTION_RAW_TYPES.includes(targetRawType)\r\n                ? collectionsProxyHandler(target, callback, targetRawType)\r\n                : basicProxyHandler(callback);\r\n            const proxy = new Proxy(target, handler);\r\n            reactivesForTarget.set(callback, proxy);\r\n            targets.set(proxy, target);\r\n        }\r\n        return reactivesForTarget.get(callback);\r\n    }\r\n    /**\r\n     * Creates a basic proxy handler for regular objects and arrays.\r\n     *\r\n     * @param callback @see reactive\r\n     * @returns a proxy handler object\r\n     */\r\n    function basicProxyHandler(callback) {\r\n        return {\r\n            get(target, key, receiver) {\r\n                // non-writable non-configurable properties cannot be made reactive\r\n                const desc = Object.getOwnPropertyDescriptor(target, key);\r\n                if (desc && !desc.writable && !desc.configurable) {\r\n                    return Reflect.get(target, key, receiver);\r\n                }\r\n                observeTargetKey(target, key, callback);\r\n                return possiblyReactive(Reflect.get(target, key, receiver), callback);\r\n            },\r\n            set(target, key, value, receiver) {\r\n                const hadKey = objectHasOwnProperty.call(target, key);\r\n                const originalValue = Reflect.get(target, key, receiver);\r\n                const ret = Reflect.set(target, key, toRaw(value), receiver);\r\n                if (!hadKey && objectHasOwnProperty.call(target, key)) {\r\n                    notifyReactives(target, KEYCHANGES);\r\n                }\r\n                // While Array length may trigger the set trap, it's not actually set by this\r\n                // method but is updated behind the scenes, and the trap is not called with the\r\n                // new value. We disable the \"same-value-optimization\" for it because of that.\r\n                if (originalValue !== Reflect.get(target, key, receiver) ||\r\n                    (key === \"length\" && Array.isArray(target))) {\r\n                    notifyReactives(target, key);\r\n                }\r\n                return ret;\r\n            },\r\n            deleteProperty(target, key) {\r\n                const ret = Reflect.deleteProperty(target, key);\r\n                // TODO: only notify when something was actually deleted\r\n                notifyReactives(target, KEYCHANGES);\r\n                notifyReactives(target, key);\r\n                return ret;\r\n            },\r\n            ownKeys(target) {\r\n                observeTargetKey(target, KEYCHANGES, callback);\r\n                return Reflect.ownKeys(target);\r\n            },\r\n            has(target, key) {\r\n                // TODO: this observes all key changes instead of only the presence of the argument key\r\n                // observing the key itself would observe value changes instead of presence changes\r\n                // so we may need a finer grained system to distinguish observing value vs presence.\r\n                observeTargetKey(target, KEYCHANGES, callback);\r\n                return Reflect.has(target, key);\r\n            },\r\n        };\r\n    }\r\n    /**\r\n     * Creates a function that will observe the key that is passed to it when called\r\n     * and delegates to the underlying method.\r\n     *\r\n     * @param methodName name of the method to delegate to\r\n     * @param target @see reactive\r\n     * @param callback @see reactive\r\n     */\r\n    function makeKeyObserver(methodName, target, callback) {\r\n        return (key) => {\r\n            key = toRaw(key);\r\n            observeTargetKey(target, key, callback);\r\n            return possiblyReactive(target[methodName](key), callback);\r\n        };\r\n    }\r\n    /**\r\n     * Creates an iterable that will delegate to the underlying iteration method and\r\n     * observe keys as necessary.\r\n     *\r\n     * @param methodName name of the method to delegate to\r\n     * @param target @see reactive\r\n     * @param callback @see reactive\r\n     */\r\n    function makeIteratorObserver(methodName, target, callback) {\r\n        return function* () {\r\n            observeTargetKey(target, KEYCHANGES, callback);\r\n            const keys = target.keys();\r\n            for (const item of target[methodName]()) {\r\n                const key = keys.next().value;\r\n                observeTargetKey(target, key, callback);\r\n                yield possiblyReactive(item, callback);\r\n            }\r\n        };\r\n    }\r\n    /**\r\n     * Creates a forEach function that will delegate to forEach on the underlying\r\n     * collection while observing key changes, and keys as they're iterated over,\r\n     * and making the passed keys/values reactive.\r\n     *\r\n     * @param target @see reactive\r\n     * @param callback @see reactive\r\n     */\r\n    function makeForEachObserver(target, callback) {\r\n        return function forEach(forEachCb, thisArg) {\r\n            observeTargetKey(target, KEYCHANGES, callback);\r\n            target.forEach(function (val, key, targetObj) {\r\n                observeTargetKey(target, key, callback);\r\n                forEachCb.call(thisArg, possiblyReactive(val, callback), possiblyReactive(key, callback), possiblyReactive(targetObj, callback));\r\n            }, thisArg);\r\n        };\r\n    }\r\n    /**\r\n     * Creates a function that will delegate to an underlying method, and check if\r\n     * that method has modified the presence or value of a key, and notify the\r\n     * reactives appropriately.\r\n     *\r\n     * @param setterName name of the method to delegate to\r\n     * @param getterName name of the method which should be used to retrieve the\r\n     *  value before calling the delegate method for comparison purposes\r\n     * @param target @see reactive\r\n     */\r\n    function delegateAndNotify(setterName, getterName, target) {\r\n        return (key, value) => {\r\n            key = toRaw(key);\r\n            const hadKey = target.has(key);\r\n            const originalValue = target[getterName](key);\r\n            const ret = target[setterName](key, value);\r\n            const hasKey = target.has(key);\r\n            if (hadKey !== hasKey) {\r\n                notifyReactives(target, KEYCHANGES);\r\n            }\r\n            if (originalValue !== target[getterName](key)) {\r\n                notifyReactives(target, key);\r\n            }\r\n            return ret;\r\n        };\r\n    }\r\n    /**\r\n     * Creates a function that will clear the underlying collection and notify that\r\n     * the keys of the collection have changed.\r\n     *\r\n     * @param target @see reactive\r\n     */\r\n    function makeClearNotifier(target) {\r\n        return () => {\r\n            const allKeys = [...target.keys()];\r\n            target.clear();\r\n            notifyReactives(target, KEYCHANGES);\r\n            for (const key of allKeys) {\r\n                notifyReactives(target, key);\r\n            }\r\n        };\r\n    }\r\n    /**\r\n     * Maps raw type of an object to an object containing functions that can be used\r\n     * to build an appropritate proxy handler for that raw type. Eg: when making a\r\n     * reactive set, calling the has method should mark the key that is being\r\n     * retrieved as observed, and calling the add or delete method should notify the\r\n     * reactives that the key which is being added or deleted has been modified.\r\n     */\r\n    const rawTypeToFuncHandlers = {\r\n        Set: (target, callback) => ({\r\n            has: makeKeyObserver(\"has\", target, callback),\r\n            add: delegateAndNotify(\"add\", \"has\", target),\r\n            delete: delegateAndNotify(\"delete\", \"has\", target),\r\n            keys: makeIteratorObserver(\"keys\", target, callback),\r\n            values: makeIteratorObserver(\"values\", target, callback),\r\n            entries: makeIteratorObserver(\"entries\", target, callback),\r\n            [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),\r\n            forEach: makeForEachObserver(target, callback),\r\n            clear: makeClearNotifier(target),\r\n            get size() {\r\n                observeTargetKey(target, KEYCHANGES, callback);\r\n                return target.size;\r\n            },\r\n        }),\r\n        Map: (target, callback) => ({\r\n            has: makeKeyObserver(\"has\", target, callback),\r\n            get: makeKeyObserver(\"get\", target, callback),\r\n            set: delegateAndNotify(\"set\", \"get\", target),\r\n            delete: delegateAndNotify(\"delete\", \"has\", target),\r\n            keys: makeIteratorObserver(\"keys\", target, callback),\r\n            values: makeIteratorObserver(\"values\", target, callback),\r\n            entries: makeIteratorObserver(\"entries\", target, callback),\r\n            [Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),\r\n            forEach: makeForEachObserver(target, callback),\r\n            clear: makeClearNotifier(target),\r\n            get size() {\r\n                observeTargetKey(target, KEYCHANGES, callback);\r\n                return target.size;\r\n            },\r\n        }),\r\n        WeakMap: (target, callback) => ({\r\n            has: makeKeyObserver(\"has\", target, callback),\r\n            get: makeKeyObserver(\"get\", target, callback),\r\n            set: delegateAndNotify(\"set\", \"get\", target),\r\n            delete: delegateAndNotify(\"delete\", \"has\", target),\r\n        }),\r\n    };\r\n    /**\r\n     * Creates a proxy handler for collections (Set/Map/WeakMap)\r\n     *\r\n     * @param callback @see reactive\r\n     * @param target @see reactive\r\n     * @returns a proxy handler object\r\n     */\r\n    function collectionsProxyHandler(target, callback, targetRawType) {\r\n        // TODO: if performance is an issue we can create the special handlers lazily when each\r\n        // property is read.\r\n        const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);\r\n        return Object.assign(basicProxyHandler(callback), {\r\n            // FIXME: probably broken when part of prototype chain since we ignore the receiver\r\n            get(target, key) {\r\n                if (objectHasOwnProperty.call(specialHandlers, key)) {\r\n                    return specialHandlers[key];\r\n                }\r\n                observeTargetKey(target, key, callback);\r\n                return possiblyReactive(target[key], callback);\r\n            },\r\n        });\r\n    }\r\n\r\n    let currentNode = null;\r\n    function saveCurrent() {\r\n        let n = currentNode;\r\n        return () => {\r\n            currentNode = n;\r\n        };\r\n    }\r\n    function getCurrent() {\r\n        if (!currentNode) {\r\n            throw new OwlError(\"No active component (a hook function should only be called in 'setup')\");\r\n        }\r\n        return currentNode;\r\n    }\r\n    function useComponent() {\r\n        return currentNode.component;\r\n    }\r\n    /**\r\n     * Apply default props (only top level).\r\n     */\r\n    function applyDefaultProps(props, defaultProps) {\r\n        for (let propName in defaultProps) {\r\n            if (props[propName] === undefined) {\r\n                props[propName] = defaultProps[propName];\r\n            }\r\n        }\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Integration with reactivity system (useState)\r\n    // -----------------------------------------------------------------------------\r\n    const batchedRenderFunctions = new WeakMap();\r\n    /**\r\n     * Creates a reactive object that will be observed by the current component.\r\n     * Reading data from the returned object (eg during rendering) will cause the\r\n     * component to subscribe to that data and be rerendered when it changes.\r\n     *\r\n     * @param state the state to observe\r\n     * @returns a reactive object that will cause the component to re-render on\r\n     *  relevant changes\r\n     * @see reactive\r\n     */\r\n    function useState(state) {\r\n        const node = getCurrent();\r\n        let render = batchedRenderFunctions.get(node);\r\n        if (!render) {\r\n            render = batched(node.render.bind(node, false));\r\n            batchedRenderFunctions.set(node, render);\r\n            // manual implementation of onWillDestroy to break cyclic dependency\r\n            node.willDestroy.push(clearReactivesForCallback.bind(null, render));\r\n        }\r\n        return reactive(state, render);\r\n    }\r\n    class ComponentNode {\r\n        constructor(C, props, app, parent, parentKey) {\r\n            this.fiber = null;\r\n            this.bdom = null;\r\n            this.status = 0 /* NEW */;\r\n            this.forceNextRender = false;\r\n            this.nextProps = null;\r\n            this.children = Object.create(null);\r\n            this.refs = {};\r\n            this.willStart = [];\r\n            this.willUpdateProps = [];\r\n            this.willUnmount = [];\r\n            this.mounted = [];\r\n            this.willPatch = [];\r\n            this.patched = [];\r\n            this.willDestroy = [];\r\n            currentNode = this;\r\n            this.app = app;\r\n            this.parent = parent;\r\n            this.props = props;\r\n            this.parentKey = parentKey;\r\n            const defaultProps = C.defaultProps;\r\n            props = Object.assign({}, props);\r\n            if (defaultProps) {\r\n                applyDefaultProps(props, defaultProps);\r\n            }\r\n            const env = (parent && parent.childEnv) || app.env;\r\n            this.childEnv = env;\r\n            for (const key in props) {\r\n                const prop = props[key];\r\n                if (prop && typeof prop === \"object\" && targets.has(prop)) {\r\n                    props[key] = useState(prop);\r\n                }\r\n            }\r\n            this.component = new C(props, env, this);\r\n            const ctx = Object.assign(Object.create(this.component), { this: this.component });\r\n            this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);\r\n            this.component.setup();\r\n            currentNode = null;\r\n        }\r\n        mountComponent(target, options) {\r\n            const fiber = new MountFiber(this, target, options);\r\n            this.app.scheduler.addFiber(fiber);\r\n            this.initiateRender(fiber);\r\n        }\r\n        async initiateRender(fiber) {\r\n            this.fiber = fiber;\r\n            if (this.mounted.length) {\r\n                fiber.root.mounted.push(fiber);\r\n            }\r\n            const component = this.component;\r\n            try {\r\n                await Promise.all(this.willStart.map((f) => f.call(component)));\r\n            }\r\n            catch (e) {\r\n                this.app.handleError({ node: this, error: e });\r\n                return;\r\n            }\r\n            if (this.status === 0 /* NEW */ && this.fiber === fiber) {\r\n                fiber.render();\r\n            }\r\n        }\r\n        async render(deep) {\r\n            if (this.status >= 2 /* CANCELLED */) {\r\n                return;\r\n            }\r\n            let current = this.fiber;\r\n            if (current && (current.root.locked || current.bdom === true)) {\r\n                await Promise.resolve();\r\n                // situation may have changed after the microtask tick\r\n                current = this.fiber;\r\n            }\r\n            if (current) {\r\n                if (!current.bdom && !fibersInError.has(current)) {\r\n                    if (deep) {\r\n                        // we want the render from this point on to be with deep=true\r\n                        current.deep = deep;\r\n                    }\r\n                    return;\r\n                }\r\n                // if current rendering was with deep=true, we want this one to be the same\r\n                deep = deep || current.deep;\r\n            }\r\n            else if (!this.bdom) {\r\n                return;\r\n            }\r\n            const fiber = makeRootFiber(this);\r\n            fiber.deep = deep;\r\n            this.fiber = fiber;\r\n            this.app.scheduler.addFiber(fiber);\r\n            await Promise.resolve();\r\n            if (this.status >= 2 /* CANCELLED */) {\r\n                return;\r\n            }\r\n            // We only want to actually render the component if the following two\r\n            // conditions are true:\r\n            // * this.fiber: it could be null, in which case the render has been cancelled\r\n            // * (current || !fiber.parent): if current is not null, this means that the\r\n            //   render function was called when a render was already occurring. In this\r\n            //   case, the pending rendering was cancelled, and the fiber needs to be\r\n            //   rendered to complete the work.  If current is null, we check that the\r\n            //   fiber has no parent.  If that is the case, the fiber was downgraded from\r\n            //   a root fiber to a child fiber in the previous microtick, because it was\r\n            //   embedded in a rendering coming from above, so the fiber will be rendered\r\n            //   in the next microtick anyway, so we should not render it again.\r\n            if (this.fiber === fiber && (current || !fiber.parent)) {\r\n                fiber.render();\r\n            }\r\n        }\r\n        cancel() {\r\n            this._cancel();\r\n            delete this.parent.children[this.parentKey];\r\n            this.app.scheduler.scheduleDestroy(this);\r\n        }\r\n        _cancel() {\r\n            this.status = 2 /* CANCELLED */;\r\n            const children = this.children;\r\n            for (let childKey in children) {\r\n                children[childKey]._cancel();\r\n            }\r\n        }\r\n        destroy() {\r\n            let shouldRemove = this.status === 1 /* MOUNTED */;\r\n            this._destroy();\r\n            if (shouldRemove) {\r\n                this.bdom.remove();\r\n            }\r\n        }\r\n        _destroy() {\r\n            const component = this.component;\r\n            if (this.status === 1 /* MOUNTED */) {\r\n                for (let cb of this.willUnmount) {\r\n                    cb.call(component);\r\n                }\r\n            }\r\n            for (let child of Object.values(this.children)) {\r\n                child._destroy();\r\n            }\r\n            if (this.willDestroy.length) {\r\n                try {\r\n                    for (let cb of this.willDestroy) {\r\n                        cb.call(component);\r\n                    }\r\n                }\r\n                catch (e) {\r\n                    this.app.handleError({ error: e, node: this });\r\n                }\r\n            }\r\n            this.status = 3 /* DESTROYED */;\r\n        }\r\n        async updateAndRender(props, parentFiber) {\r\n            this.nextProps = props;\r\n            props = Object.assign({}, props);\r\n            // update\r\n            const fiber = makeChildFiber(this, parentFiber);\r\n            this.fiber = fiber;\r\n            const component = this.component;\r\n            const defaultProps = component.constructor.defaultProps;\r\n            if (defaultProps) {\r\n                applyDefaultProps(props, defaultProps);\r\n            }\r\n            currentNode = this;\r\n            for (const key in props) {\r\n                const prop = props[key];\r\n                if (prop && typeof prop === \"object\" && targets.has(prop)) {\r\n                    props[key] = useState(prop);\r\n                }\r\n            }\r\n            currentNode = null;\r\n            const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));\r\n            await prom;\r\n            if (fiber !== this.fiber) {\r\n                return;\r\n            }\r\n            component.props = props;\r\n            fiber.render();\r\n            const parentRoot = parentFiber.root;\r\n            if (this.willPatch.length) {\r\n                parentRoot.willPatch.push(fiber);\r\n            }\r\n            if (this.patched.length) {\r\n                parentRoot.patched.push(fiber);\r\n            }\r\n        }\r\n        /**\r\n         * Finds a child that has dom that is not yet updated, and update it. This\r\n         * method is meant to be used only in the context of repatching the dom after\r\n         * a mounted hook failed and was handled.\r\n         */\r\n        updateDom() {\r\n            if (!this.fiber) {\r\n                return;\r\n            }\r\n            if (this.bdom === this.fiber.bdom) {\r\n                // If the error was handled by some child component, we need to find it to\r\n                // apply its change\r\n                for (let k in this.children) {\r\n                    const child = this.children[k];\r\n                    child.updateDom();\r\n                }\r\n            }\r\n            else {\r\n                // if we get here, this is the component that handled the error and rerendered\r\n                // itself, so we can simply patch the dom\r\n                this.bdom.patch(this.fiber.bdom, false);\r\n                this.fiber.appliedToDom = true;\r\n                this.fiber = null;\r\n            }\r\n        }\r\n        /**\r\n         * Sets a ref to a given HTMLElement.\r\n         *\r\n         * @param name the name of the ref to set\r\n         * @param el the HTMLElement to set the ref to. The ref is not set if the el\r\n         *  is null, but useRef will not return elements that are not in the DOM\r\n         */\r\n        setRef(name, el) {\r\n            if (el) {\r\n                this.refs[name] = el;\r\n            }\r\n        }\r\n        // ---------------------------------------------------------------------------\r\n        // Block DOM methods\r\n        // ---------------------------------------------------------------------------\r\n        firstNode() {\r\n            const bdom = this.bdom;\r\n            return bdom ? bdom.firstNode() : undefined;\r\n        }\r\n        mount(parent, anchor) {\r\n            const bdom = this.fiber.bdom;\r\n            this.bdom = bdom;\r\n            bdom.mount(parent, anchor);\r\n            this.status = 1 /* MOUNTED */;\r\n            this.fiber.appliedToDom = true;\r\n            this.children = this.fiber.childrenMap;\r\n            this.fiber = null;\r\n        }\r\n        moveBeforeDOMNode(node, parent) {\r\n            this.bdom.moveBeforeDOMNode(node, parent);\r\n        }\r\n        moveBeforeVNode(other, afterNode) {\r\n            this.bdom.moveBeforeVNode(other ? other.bdom : null, afterNode);\r\n        }\r\n        patch() {\r\n            if (this.fiber && this.fiber.parent) {\r\n                // we only patch here renderings coming from above. renderings initiated\r\n                // by the component will be patched independently in the appropriate\r\n                // fiber.complete\r\n                this._patch();\r\n                this.props = this.nextProps;\r\n            }\r\n        }\r\n        _patch() {\r\n            let hasChildren = false;\r\n            // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n            for (let _k in this.children) {\r\n                hasChildren = true;\r\n                break;\r\n            }\r\n            const fiber = this.fiber;\r\n            this.children = fiber.childrenMap;\r\n            this.bdom.patch(fiber.bdom, hasChildren);\r\n            fiber.appliedToDom = true;\r\n            this.fiber = null;\r\n        }\r\n        beforeRemove() {\r\n            this._destroy();\r\n        }\r\n        remove() {\r\n            this.bdom.remove();\r\n        }\r\n        // ---------------------------------------------------------------------------\r\n        // Some debug helpers\r\n        // ---------------------------------------------------------------------------\r\n        get name() {\r\n            return this.component.constructor.name;\r\n        }\r\n        get subscriptions() {\r\n            const render = batchedRenderFunctions.get(this);\r\n            return render ? getSubscriptions(render) : [];\r\n        }\r\n    }\r\n\r\n    const TIMEOUT = Symbol(\"timeout\");\r\n    const HOOK_TIMEOUT = {\r\n        onWillStart: 3000,\r\n        onWillUpdateProps: 3000,\r\n    };\r\n    function wrapError(fn, hookName) {\r\n        const error = new OwlError();\r\n        const timeoutError = new OwlError();\r\n        const node = getCurrent();\r\n        return (...args) => {\r\n            const onError = (cause) => {\r\n                error.cause = cause;\r\n                error.message =\r\n                    cause instanceof Error\r\n                        ? `The following error occurred in ${hookName}: \"${cause.message}\"`\r\n                        : `Something that is not an Error was thrown in ${hookName} (see this Error's \"cause\" property)`;\r\n                throw error;\r\n            };\r\n            let result;\r\n            try {\r\n                result = fn(...args);\r\n            }\r\n            catch (cause) {\r\n                onError(cause);\r\n            }\r\n            if (!(result instanceof Promise)) {\r\n                return result;\r\n            }\r\n            const timeout = HOOK_TIMEOUT[hookName];\r\n            if (timeout) {\r\n                const fiber = node.fiber;\r\n                Promise.race([\r\n                    result.catch(() => { }),\r\n                    new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), timeout)),\r\n                ]).then((res) => {\r\n                    if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) {\r\n                        timeoutError.message = `${hookName}'s promise hasn't resolved after ${timeout / 1000} seconds`;\r\n                        console.log(timeoutError);\r\n                    }\r\n                });\r\n            }\r\n            return result.catch(onError);\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    //  hooks\r\n    // -----------------------------------------------------------------------------\r\n    function onWillStart(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.willStart.push(decorate(fn.bind(node.component), \"onWillStart\"));\r\n    }\r\n    function onWillUpdateProps(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.willUpdateProps.push(decorate(fn.bind(node.component), \"onWillUpdateProps\"));\r\n    }\r\n    function onMounted(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.mounted.push(decorate(fn.bind(node.component), \"onMounted\"));\r\n    }\r\n    function onWillPatch(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.willPatch.unshift(decorate(fn.bind(node.component), \"onWillPatch\"));\r\n    }\r\n    function onPatched(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.patched.push(decorate(fn.bind(node.component), \"onPatched\"));\r\n    }\r\n    function onWillUnmount(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.willUnmount.unshift(decorate(fn.bind(node.component), \"onWillUnmount\"));\r\n    }\r\n    function onWillDestroy(fn) {\r\n        const node = getCurrent();\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        node.willDestroy.push(decorate(fn.bind(node.component), \"onWillDestroy\"));\r\n    }\r\n    function onWillRender(fn) {\r\n        const node = getCurrent();\r\n        const renderFn = node.renderFn;\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        fn = decorate(fn.bind(node.component), \"onWillRender\");\r\n        node.renderFn = () => {\r\n            fn();\r\n            return renderFn();\r\n        };\r\n    }\r\n    function onRendered(fn) {\r\n        const node = getCurrent();\r\n        const renderFn = node.renderFn;\r\n        const decorate = node.app.dev ? wrapError : (fn) => fn;\r\n        fn = decorate(fn.bind(node.component), \"onRendered\");\r\n        node.renderFn = () => {\r\n            const result = renderFn();\r\n            fn();\r\n            return result;\r\n        };\r\n    }\r\n    function onError(callback) {\r\n        const node = getCurrent();\r\n        let handlers = nodeErrorHandlers.get(node);\r\n        if (!handlers) {\r\n            handlers = [];\r\n            nodeErrorHandlers.set(node, handlers);\r\n        }\r\n        handlers.push(callback.bind(node.component));\r\n    }\r\n\r\n    class Component {\r\n        constructor(props, env, node) {\r\n            this.props = props;\r\n            this.env = env;\r\n            this.__owl__ = node;\r\n        }\r\n        setup() { }\r\n        render(deep = false) {\r\n            this.__owl__.render(deep === true);\r\n        }\r\n    }\r\n    Component.template = \"\";\r\n\r\n    const VText = text(\"\").constructor;\r\n    class VPortal extends VText {\r\n        constructor(selector, content) {\r\n            super(\"\");\r\n            this.target = null;\r\n            this.selector = selector;\r\n            this.content = content;\r\n        }\r\n        mount(parent, anchor) {\r\n            super.mount(parent, anchor);\r\n            this.target = document.querySelector(this.selector);\r\n            if (this.target) {\r\n                this.content.mount(this.target, null);\r\n            }\r\n            else {\r\n                this.content.mount(parent, anchor);\r\n            }\r\n        }\r\n        beforeRemove() {\r\n            this.content.beforeRemove();\r\n        }\r\n        remove() {\r\n            if (this.content) {\r\n                super.remove();\r\n                this.content.remove();\r\n                this.content = null;\r\n            }\r\n        }\r\n        patch(other) {\r\n            super.patch(other);\r\n            if (this.content) {\r\n                this.content.patch(other.content, true);\r\n            }\r\n            else {\r\n                this.content = other.content;\r\n                this.content.mount(this.target, null);\r\n            }\r\n        }\r\n    }\r\n    /**\r\n     * kind of similar to <t t-slot=\"default\"/>, but it wraps it around a VPortal\r\n     */\r\n    function portalTemplate(app, bdom, helpers) {\r\n        let { callSlot } = helpers;\r\n        return function template(ctx, node, key = \"\") {\r\n            return new VPortal(ctx.props.target, callSlot(ctx, node, key, \"default\", false, null));\r\n        };\r\n    }\r\n    class Portal extends Component {\r\n        setup() {\r\n            const node = this.__owl__;\r\n            onMounted(() => {\r\n                const portal = node.bdom;\r\n                if (!portal.target) {\r\n                    const target = document.querySelector(this.props.target);\r\n                    if (target) {\r\n                        portal.content.moveBeforeDOMNode(target.firstChild, target);\r\n                    }\r\n                    else {\r\n                        throw new OwlError(\"invalid portal target\");\r\n                    }\r\n                }\r\n            });\r\n            onWillUnmount(() => {\r\n                const portal = node.bdom;\r\n                portal.remove();\r\n            });\r\n        }\r\n    }\r\n    Portal.template = \"__portal__\";\r\n    Portal.props = {\r\n        target: {\r\n            type: String,\r\n        },\r\n        slots: true,\r\n    };\r\n\r\n    // -----------------------------------------------------------------------------\r\n    // helpers\r\n    // -----------------------------------------------------------------------------\r\n    const isUnionType = (t) => Array.isArray(t);\r\n    const isBaseType = (t) => typeof t !== \"object\";\r\n    const isValueType = (t) => typeof t === \"object\" && t && \"value\" in t;\r\n    function isOptional(t) {\r\n        return typeof t === \"object\" && \"optional\" in t ? t.optional || false : false;\r\n    }\r\n    function describeType(type) {\r\n        return type === \"*\" || type === true ? \"value\" : type.name.toLowerCase();\r\n    }\r\n    function describe(info) {\r\n        if (isBaseType(info)) {\r\n            return describeType(info);\r\n        }\r\n        else if (isUnionType(info)) {\r\n            return info.map(describe).join(\" or \");\r\n        }\r\n        else if (isValueType(info)) {\r\n            return String(info.value);\r\n        }\r\n        if (\"element\" in info) {\r\n            return `list of ${describe({ type: info.element, optional: false })}s`;\r\n        }\r\n        if (\"shape\" in info) {\r\n            return `object`;\r\n        }\r\n        return describe(info.type || \"*\");\r\n    }\r\n    function toSchema(spec) {\r\n        return Object.fromEntries(spec.map((e) => e.endsWith(\"?\") ? [e.slice(0, -1), { optional: true }] : [e, { type: \"*\", optional: false }]));\r\n    }\r\n    /**\r\n     * Main validate function\r\n     */\r\n    function validate(obj, spec) {\r\n        let errors = validateSchema(obj, spec);\r\n        if (errors.length) {\r\n            throw new OwlError(\"Invalid object: \" + errors.join(\", \"));\r\n        }\r\n    }\r\n    /**\r\n     * Helper validate function, to get the list of errors. useful if one want to\r\n     * manipulate the errors without parsing an error object\r\n     */\r\n    function validateSchema(obj, schema) {\r\n        if (Array.isArray(schema)) {\r\n            schema = toSchema(schema);\r\n        }\r\n        obj = toRaw(obj);\r\n        let errors = [];\r\n        // check if each value in obj has correct shape\r\n        for (let key in obj) {\r\n            if (key in schema) {\r\n                let result = validateType(key, obj[key], schema[key]);\r\n                if (result) {\r\n                    errors.push(result);\r\n                }\r\n            }\r\n            else if (!(\"*\" in schema)) {\r\n                errors.push(`unknown key '${key}'`);\r\n            }\r\n        }\r\n        // check that all specified keys are defined in obj\r\n        for (let key in schema) {\r\n            const spec = schema[key];\r\n            if (key !== \"*\" && !isOptional(spec) && !(key in obj)) {\r\n                const isObj = typeof spec === \"object\" && !Array.isArray(spec);\r\n                const isAny = spec === \"*\" || (isObj && \"type\" in spec ? spec.type === \"*\" : isObj);\r\n                let detail = isAny ? \"\" : ` (should be a ${describe(spec)})`;\r\n                errors.push(`'${key}' is missing${detail}`);\r\n            }\r\n        }\r\n        return errors;\r\n    }\r\n    function validateBaseType(key, value, type) {\r\n        if (typeof type === \"function\") {\r\n            if (typeof value === \"object\") {\r\n                if (!(value instanceof type)) {\r\n                    return `'${key}' is not a ${describeType(type)}`;\r\n                }\r\n            }\r\n            else if (typeof value !== type.name.toLowerCase()) {\r\n                return `'${key}' is not a ${describeType(type)}`;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n    function validateArrayType(key, value, descr) {\r\n        if (!Array.isArray(value)) {\r\n            return `'${key}' is not a list of ${describe(descr)}s`;\r\n        }\r\n        for (let i = 0; i < value.length; i++) {\r\n            const error = validateType(`${key}[${i}]`, value[i], descr);\r\n            if (error) {\r\n                return error;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n    function validateType(key, value, descr) {\r\n        if (value === undefined) {\r\n            return isOptional(descr) ? null : `'${key}' is undefined (should be a ${describe(descr)})`;\r\n        }\r\n        else if (isBaseType(descr)) {\r\n            return validateBaseType(key, value, descr);\r\n        }\r\n        else if (isValueType(descr)) {\r\n            return value === descr.value ? null : `'${key}' is not equal to '${descr.value}'`;\r\n        }\r\n        else if (isUnionType(descr)) {\r\n            let validDescr = descr.find((p) => !validateType(key, value, p));\r\n            return validDescr ? null : `'${key}' is not a ${describe(descr)}`;\r\n        }\r\n        let result = null;\r\n        if (\"element\" in descr) {\r\n            result = validateArrayType(key, value, descr.element);\r\n        }\r\n        else if (\"shape\" in descr) {\r\n            if (typeof value !== \"object\" || Array.isArray(value)) {\r\n                result = `'${key}' is not an object`;\r\n            }\r\n            else {\r\n                const errors = validateSchema(value, descr.shape);\r\n                if (errors.length) {\r\n                    result = `'${key}' doesn't have the correct shape (${errors.join(\", \")})`;\r\n                }\r\n            }\r\n        }\r\n        else if (\"values\" in descr) {\r\n            if (typeof value !== \"object\" || Array.isArray(value)) {\r\n                result = `'${key}' is not an object`;\r\n            }\r\n            else {\r\n                const errors = Object.entries(value)\r\n                    .map(([key, value]) => validateType(key, value, descr.values))\r\n                    .filter(Boolean);\r\n                if (errors.length) {\r\n                    result = `some of the values in '${key}' are invalid (${errors.join(\", \")})`;\r\n                }\r\n            }\r\n        }\r\n        if (\"type\" in descr && !result) {\r\n            result = validateType(key, value, descr.type);\r\n        }\r\n        if (\"validate\" in descr && !result) {\r\n            result = !descr.validate(value) ? `'${key}' is not valid` : null;\r\n        }\r\n        return result;\r\n    }\r\n\r\n    const ObjectCreate = Object.create;\r\n    /**\r\n     * This file contains utility functions that will be injected in each template,\r\n     * to perform various useful tasks in the compiled code.\r\n     */\r\n    function withDefault(value, defaultValue) {\r\n        return value === undefined || value === null || value === false ? defaultValue : value;\r\n    }\r\n    function callSlot(ctx, parent, key, name, dynamic, extra, defaultContent) {\r\n        key = key + \"__slot_\" + name;\r\n        const slots = ctx.props.slots || {};\r\n        const { __render, __ctx, __scope } = slots[name] || {};\r\n        const slotScope = ObjectCreate(__ctx || {});\r\n        if (__scope) {\r\n            slotScope[__scope] = extra;\r\n        }\r\n        const slotBDom = __render ? __render(slotScope, parent, key) : null;\r\n        if (defaultContent) {\r\n            let child1 = undefined;\r\n            let child2 = undefined;\r\n            if (slotBDom) {\r\n                child1 = dynamic ? toggler(name, slotBDom) : slotBDom;\r\n            }\r\n            else {\r\n                child2 = defaultContent(ctx, parent, key);\r\n            }\r\n            return multi([child1, child2]);\r\n        }\r\n        return slotBDom || text(\"\");\r\n    }\r\n    function capture(ctx) {\r\n        const result = ObjectCreate(ctx);\r\n        for (let k in ctx) {\r\n            result[k] = ctx[k];\r\n        }\r\n        return result;\r\n    }\r\n    function withKey(elem, k) {\r\n        elem.key = k;\r\n        return elem;\r\n    }\r\n    function prepareList(collection) {\r\n        let keys;\r\n        let values;\r\n        if (Array.isArray(collection)) {\r\n            keys = collection;\r\n            values = collection;\r\n        }\r\n        else if (collection instanceof Map) {\r\n            keys = [...collection.keys()];\r\n            values = [...collection.values()];\r\n        }\r\n        else if (Symbol.iterator in Object(collection)) {\r\n            keys = [...collection];\r\n            values = keys;\r\n        }\r\n        else if (collection && typeof collection === \"object\") {\r\n            values = Object.values(collection);\r\n            keys = Object.keys(collection);\r\n        }\r\n        else {\r\n            throw new OwlError(`Invalid loop expression: \"${collection}\" is not iterable`);\r\n        }\r\n        const n = values.length;\r\n        return [keys, values, n, new Array(n)];\r\n    }\r\n    const isBoundary = Symbol(\"isBoundary\");\r\n    function setContextValue(ctx, key, value) {\r\n        const ctx0 = ctx;\r\n        while (!ctx.hasOwnProperty(key) && !ctx.hasOwnProperty(isBoundary)) {\r\n            const newCtx = ctx.__proto__;\r\n            if (!newCtx) {\r\n                ctx = ctx0;\r\n                break;\r\n            }\r\n            ctx = newCtx;\r\n        }\r\n        ctx[key] = value;\r\n    }\r\n    function toNumber(val) {\r\n        const n = parseFloat(val);\r\n        return isNaN(n) ? val : n;\r\n    }\r\n    function shallowEqual(l1, l2) {\r\n        for (let i = 0, l = l1.length; i < l; i++) {\r\n            if (l1[i] !== l2[i]) {\r\n                return false;\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n    class LazyValue {\r\n        constructor(fn, ctx, component, node, key) {\r\n            this.fn = fn;\r\n            this.ctx = capture(ctx);\r\n            this.component = component;\r\n            this.node = node;\r\n            this.key = key;\r\n        }\r\n        evaluate() {\r\n            return this.fn.call(this.component, this.ctx, this.node, this.key);\r\n        }\r\n        toString() {\r\n            return this.evaluate().toString();\r\n        }\r\n    }\r\n    /*\r\n     * Safely outputs `value` as a block depending on the nature of `value`\r\n     */\r\n    function safeOutput(value, defaultValue) {\r\n        if (value === undefined || value === null) {\r\n            return defaultValue ? toggler(\"default\", defaultValue) : toggler(\"undefined\", text(\"\"));\r\n        }\r\n        let safeKey;\r\n        let block;\r\n        switch (typeof value) {\r\n            case \"object\":\r\n                if (value instanceof Markup) {\r\n                    safeKey = `string_safe`;\r\n                    block = html(value);\r\n                }\r\n                else if (value instanceof LazyValue) {\r\n                    safeKey = `lazy_value`;\r\n                    block = value.evaluate();\r\n                }\r\n                else if (value instanceof String) {\r\n                    safeKey = \"string_unsafe\";\r\n                    block = text(value);\r\n                }\r\n                else {\r\n                    // Assuming it is a block\r\n                    safeKey = \"block_safe\";\r\n                    block = value;\r\n                }\r\n                break;\r\n            case \"string\":\r\n                safeKey = \"string_unsafe\";\r\n                block = text(value);\r\n                break;\r\n            default:\r\n                safeKey = \"string_unsafe\";\r\n                block = text(String(value));\r\n        }\r\n        return toggler(safeKey, block);\r\n    }\r\n    /**\r\n     * Validate the component props (or next props) against the (static) props\r\n     * description.  This is potentially an expensive operation: it may needs to\r\n     * visit recursively the props and all the children to check if they are valid.\r\n     * This is why it is only done in 'dev' mode.\r\n     */\r\n    function validateProps(name, props, comp) {\r\n        const ComponentClass = typeof name !== \"string\"\r\n            ? name\r\n            : comp.constructor.components[name];\r\n        if (!ComponentClass) {\r\n            // this is an error, wrong component. We silently return here instead so the\r\n            // error is triggered by the usual path ('component' function)\r\n            return;\r\n        }\r\n        const schema = ComponentClass.props;\r\n        if (!schema) {\r\n            if (comp.__owl__.app.warnIfNoStaticProps) {\r\n                console.warn(`Component '${ComponentClass.name}' does not have a static props description`);\r\n            }\r\n            return;\r\n        }\r\n        const defaultProps = ComponentClass.defaultProps;\r\n        if (defaultProps) {\r\n            let isMandatory = (name) => Array.isArray(schema)\r\n                ? schema.includes(name)\r\n                : name in schema && !(\"*\" in schema) && !isOptional(schema[name]);\r\n            for (let p in defaultProps) {\r\n                if (isMandatory(p)) {\r\n                    throw new OwlError(`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`);\r\n                }\r\n            }\r\n        }\r\n        const errors = validateSchema(props, schema);\r\n        if (errors.length) {\r\n            throw new OwlError(`Invalid props for component '${ComponentClass.name}': ` + errors.join(\", \"));\r\n        }\r\n    }\r\n    function makeRefWrapper(node) {\r\n        let refNames = new Set();\r\n        return (name, fn) => {\r\n            if (refNames.has(name)) {\r\n                throw new OwlError(`Cannot set the same ref more than once in the same component, ref \"${name}\" was set multiple times in ${node.name}`);\r\n            }\r\n            refNames.add(name);\r\n            return fn;\r\n        };\r\n    }\r\n    const helpers = {\r\n        withDefault,\r\n        zero: Symbol(\"zero\"),\r\n        isBoundary,\r\n        callSlot,\r\n        capture,\r\n        withKey,\r\n        prepareList,\r\n        setContextValue,\r\n        shallowEqual,\r\n        toNumber,\r\n        validateProps,\r\n        LazyValue,\r\n        safeOutput,\r\n        createCatcher,\r\n        markRaw,\r\n        OwlError,\r\n        makeRefWrapper,\r\n    };\r\n\r\n    /**\r\n     * Parses an XML string into an XML document, throwing errors on parser errors\r\n     * instead of returning an XML document containing the parseerror.\r\n     *\r\n     * @param xml the string to parse\r\n     * @returns an XML document corresponding to the content of the string\r\n     */\r\n    function parseXML(xml) {\r\n        const parser = new DOMParser();\r\n        const doc = parser.parseFromString(xml, \"text/xml\");\r\n        if (doc.getElementsByTagName(\"parsererror\").length) {\r\n            let msg = \"Invalid XML in template.\";\r\n            const parsererrorText = doc.getElementsByTagName(\"parsererror\")[0].textContent;\r\n            if (parsererrorText) {\r\n                msg += \"\\nThe parser has produced the following error message:\\n\" + parsererrorText;\r\n                const re = /\\d+/g;\r\n                const firstMatch = re.exec(parsererrorText);\r\n                if (firstMatch) {\r\n                    const lineNumber = Number(firstMatch[0]);\r\n                    const line = xml.split(\"\\n\")[lineNumber - 1];\r\n                    const secondMatch = re.exec(parsererrorText);\r\n                    if (line && secondMatch) {\r\n                        const columnIndex = Number(secondMatch[0]) - 1;\r\n                        if (line[columnIndex]) {\r\n                            msg +=\r\n                                `\\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\\n` +\r\n                                    `${line}\\n${\"-\".repeat(columnIndex - 1)}^`;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            throw new OwlError(msg);\r\n        }\r\n        return doc;\r\n    }\r\n\r\n    const bdom = { text, createBlock, list, multi, html, toggler, comment };\r\n    class TemplateSet {\r\n        constructor(config = {}) {\r\n            this.rawTemplates = Object.create(globalTemplates);\r\n            this.templates = {};\r\n            this.Portal = Portal;\r\n            this.dev = config.dev || false;\r\n            this.translateFn = config.translateFn;\r\n            this.translatableAttributes = config.translatableAttributes;\r\n            if (config.templates) {\r\n                if (config.templates instanceof Document || typeof config.templates === \"string\") {\r\n                    this.addTemplates(config.templates);\r\n                }\r\n                else {\r\n                    for (const name in config.templates) {\r\n                        this.addTemplate(name, config.templates[name]);\r\n                    }\r\n                }\r\n            }\r\n            this.getRawTemplate = config.getTemplate;\r\n            this.customDirectives = config.customDirectives || {};\r\n            this.runtimeUtils = { ...helpers, __globals__: config.globalValues || {} };\r\n            this.hasGlobalValues = Boolean(config.globalValues && Object.keys(config.globalValues).length);\r\n        }\r\n        static registerTemplate(name, fn) {\r\n            globalTemplates[name] = fn;\r\n        }\r\n        addTemplate(name, template) {\r\n            if (name in this.rawTemplates) {\r\n                // this check can be expensive, just silently ignore double definitions outside dev mode\r\n                if (!this.dev) {\r\n                    return;\r\n                }\r\n                const rawTemplate = this.rawTemplates[name];\r\n                const currentAsString = typeof rawTemplate === \"string\"\r\n                    ? rawTemplate\r\n                    : rawTemplate instanceof Element\r\n                        ? rawTemplate.outerHTML\r\n                        : rawTemplate.toString();\r\n                const newAsString = typeof template === \"string\" ? template : template.outerHTML;\r\n                if (currentAsString === newAsString) {\r\n                    return;\r\n                }\r\n                throw new OwlError(`Template ${name} already defined with different content`);\r\n            }\r\n            this.rawTemplates[name] = template;\r\n        }\r\n        addTemplates(xml) {\r\n            if (!xml) {\r\n                // empty string\r\n                return;\r\n            }\r\n            xml = xml instanceof Document ? xml : parseXML(xml);\r\n            for (const template of xml.querySelectorAll(\"[t-name]\")) {\r\n                const name = template.getAttribute(\"t-name\");\r\n                this.addTemplate(name, template);\r\n            }\r\n        }\r\n        getTemplate(name) {\r\n            var _a;\r\n            if (!(name in this.templates)) {\r\n                const rawTemplate = ((_a = this.getRawTemplate) === null || _a === void 0 ? void 0 : _a.call(this, name)) || this.rawTemplates[name];\r\n                if (rawTemplate === undefined) {\r\n                    let extraInfo = \"\";\r\n                    try {\r\n                        const componentName = getCurrent().component.constructor.name;\r\n                        extraInfo = ` (for component \"${componentName}\")`;\r\n                    }\r\n                    catch { }\r\n                    throw new OwlError(`Missing template: \"${name}\"${extraInfo}`);\r\n                }\r\n                const isFn = typeof rawTemplate === \"function\" && !(rawTemplate instanceof Element);\r\n                const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);\r\n                // first add a function to lazily get the template, in case there is a\r\n                // recursive call to the template name\r\n                const templates = this.templates;\r\n                this.templates[name] = function (context, parent) {\r\n                    return templates[name].call(this, context, parent);\r\n                };\r\n                const template = templateFn(this, bdom, this.runtimeUtils);\r\n                this.templates[name] = template;\r\n            }\r\n            return this.templates[name];\r\n        }\r\n        _compileTemplate(name, template) {\r\n            throw new OwlError(`Unable to compile a template. Please use owl full build instead`);\r\n        }\r\n        callTemplate(owner, subTemplate, ctx, parent, key) {\r\n            const template = this.getTemplate(subTemplate);\r\n            return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));\r\n        }\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    //  xml tag helper\r\n    // -----------------------------------------------------------------------------\r\n    const globalTemplates = {};\r\n    function xml(...args) {\r\n        const name = `__template__${xml.nextId++}`;\r\n        const value = String.raw(...args);\r\n        globalTemplates[name] = value;\r\n        return name;\r\n    }\r\n    xml.nextId = 1;\r\n    TemplateSet.registerTemplate(\"__portal__\", portalTemplate);\r\n\r\n    /**\r\n     * Owl QWeb Expression Parser\r\n     *\r\n     * Owl needs in various contexts to be able to understand the structure of a\r\n     * string representing a javascript expression.  The usual goal is to be able\r\n     * to rewrite some variables.  For example, if a template has\r\n     *\r\n     *  ```xml\r\n     *  <t t-if=\"computeSomething({val: state.val})\">...</t>\r\n     * ```\r\n     *\r\n     * this needs to be translated in something like this:\r\n     *\r\n     * ```js\r\n     *   if (context[\"computeSomething\"]({val: context[\"state\"].val})) { ... }\r\n     * ```\r\n     *\r\n     * This file contains the implementation of an extremely naive tokenizer/parser\r\n     * and evaluator for javascript expressions.  The supported grammar is basically\r\n     * only expressive enough to understand the shape of objects, of arrays, and\r\n     * various operators.\r\n     */\r\n    //------------------------------------------------------------------------------\r\n    // Misc types, constants and helpers\r\n    //------------------------------------------------------------------------------\r\n    const RESERVED_WORDS = \"true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,eval,void,Math,RegExp,Array,Object,Date,__globals__\".split(\",\");\r\n    const WORD_REPLACEMENT = Object.assign(Object.create(null), {\r\n        and: \"&&\",\r\n        or: \"||\",\r\n        gt: \">\",\r\n        gte: \">=\",\r\n        lt: \"<\",\r\n        lte: \"<=\",\r\n    });\r\n    const STATIC_TOKEN_MAP = Object.assign(Object.create(null), {\r\n        \"{\": \"LEFT_BRACE\",\r\n        \"}\": \"RIGHT_BRACE\",\r\n        \"[\": \"LEFT_BRACKET\",\r\n        \"]\": \"RIGHT_BRACKET\",\r\n        \":\": \"COLON\",\r\n        \",\": \"COMMA\",\r\n        \"(\": \"LEFT_PAREN\",\r\n        \")\": \"RIGHT_PAREN\",\r\n    });\r\n    // note that the space after typeof is relevant. It makes sure that the formatted\r\n    // expression has a space after typeof. Currently we don't support delete and void\r\n    const OPERATORS = \"...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ,new ,|,&,^,~\".split(\",\");\r\n    let tokenizeString = function (expr) {\r\n        let s = expr[0];\r\n        let start = s;\r\n        if (s !== \"'\" && s !== '\"' && s !== \"`\") {\r\n            return false;\r\n        }\r\n        let i = 1;\r\n        let cur;\r\n        while (expr[i] && expr[i] !== start) {\r\n            cur = expr[i];\r\n            s += cur;\r\n            if (cur === \"\\\\\") {\r\n                i++;\r\n                cur = expr[i];\r\n                if (!cur) {\r\n                    throw new OwlError(\"Invalid expression\");\r\n                }\r\n                s += cur;\r\n            }\r\n            i++;\r\n        }\r\n        if (expr[i] !== start) {\r\n            throw new OwlError(\"Invalid expression\");\r\n        }\r\n        s += start;\r\n        if (start === \"`\") {\r\n            return {\r\n                type: \"TEMPLATE_STRING\",\r\n                value: s,\r\n                replace(replacer) {\r\n                    return s.replace(/\\$\\{(.*?)\\}/g, (match, group) => {\r\n                        return \"${\" + replacer(group) + \"}\";\r\n                    });\r\n                },\r\n            };\r\n        }\r\n        return { type: \"VALUE\", value: s };\r\n    };\r\n    let tokenizeNumber = function (expr) {\r\n        let s = expr[0];\r\n        if (s && s.match(/[0-9]/)) {\r\n            let i = 1;\r\n            while (expr[i] && expr[i].match(/[0-9]|\\./)) {\r\n                s += expr[i];\r\n                i++;\r\n            }\r\n            return { type: \"VALUE\", value: s };\r\n        }\r\n        else {\r\n            return false;\r\n        }\r\n    };\r\n    let tokenizeSymbol = function (expr) {\r\n        let s = expr[0];\r\n        if (s && s.match(/[a-zA-Z_\\$]/)) {\r\n            let i = 1;\r\n            while (expr[i] && expr[i].match(/\\w/)) {\r\n                s += expr[i];\r\n                i++;\r\n            }\r\n            if (s in WORD_REPLACEMENT) {\r\n                return { type: \"OPERATOR\", value: WORD_REPLACEMENT[s], size: s.length };\r\n            }\r\n            return { type: \"SYMBOL\", value: s };\r\n        }\r\n        else {\r\n            return false;\r\n        }\r\n    };\r\n    const tokenizeStatic = function (expr) {\r\n        const char = expr[0];\r\n        if (char && char in STATIC_TOKEN_MAP) {\r\n            return { type: STATIC_TOKEN_MAP[char], value: char };\r\n        }\r\n        return false;\r\n    };\r\n    const tokenizeOperator = function (expr) {\r\n        for (let op of OPERATORS) {\r\n            if (expr.startsWith(op)) {\r\n                return { type: \"OPERATOR\", value: op };\r\n            }\r\n        }\r\n        return false;\r\n    };\r\n    const TOKENIZERS = [\r\n        tokenizeString,\r\n        tokenizeNumber,\r\n        tokenizeOperator,\r\n        tokenizeSymbol,\r\n        tokenizeStatic,\r\n    ];\r\n    /**\r\n     * Convert a javascript expression (as a string) into a list of tokens. For\r\n     * example: `tokenize(\"1 + b\")` will return:\r\n     * ```js\r\n     *  [\r\n     *   {type: \"VALUE\", value: \"1\"},\r\n     *   {type: \"OPERATOR\", value: \"+\"},\r\n     *   {type: \"SYMBOL\", value: \"b\"}\r\n     * ]\r\n     * ```\r\n     */\r\n    function tokenize(expr) {\r\n        const result = [];\r\n        let token = true;\r\n        let error;\r\n        let current = expr;\r\n        try {\r\n            while (token) {\r\n                current = current.trim();\r\n                if (current) {\r\n                    for (let tokenizer of TOKENIZERS) {\r\n                        token = tokenizer(current);\r\n                        if (token) {\r\n                            result.push(token);\r\n                            current = current.slice(token.size || token.value.length);\r\n                            break;\r\n                        }\r\n                    }\r\n                }\r\n                else {\r\n                    token = false;\r\n                }\r\n            }\r\n        }\r\n        catch (e) {\r\n            error = e; // Silence all errors and throw a generic error below\r\n        }\r\n        if (current.length || error) {\r\n            throw new OwlError(`Tokenizer error: could not tokenize \\`${expr}\\``);\r\n        }\r\n        return result;\r\n    }\r\n    //------------------------------------------------------------------------------\r\n    // Expression \"evaluator\"\r\n    //------------------------------------------------------------------------------\r\n    const isLeftSeparator = (token) => token && (token.type === \"LEFT_BRACE\" || token.type === \"COMMA\");\r\n    const isRightSeparator = (token) => token && (token.type === \"RIGHT_BRACE\" || token.type === \"COMMA\");\r\n    /**\r\n     * This is the main function exported by this file. This is the code that will\r\n     * process an expression (given as a string) and returns another expression with\r\n     * proper lookups in the context.\r\n     *\r\n     * Usually, this kind of code would be very simple to do if we had an AST (so,\r\n     * if we had a javascript parser), since then, we would only need to find the\r\n     * variables and replace them.  However, a parser is more complicated, and there\r\n     * are no standard builtin parser API.\r\n     *\r\n     * Since this method is applied to simple javasript expressions, and the work to\r\n     * be done is actually quite simple, we actually can get away with not using a\r\n     * parser, which helps with the code size.\r\n     *\r\n     * Here is the heuristic used by this method to determine if a token is a\r\n     * variable:\r\n     * - by default, all symbols are considered a variable\r\n     * - unless the previous token is a dot (in that case, this is a property: `a.b`)\r\n     * - or if the previous token is a left brace or a comma, and the next token is\r\n     *   a colon (in that case, this is an object key: `{a: b}`)\r\n     *\r\n     * Some specific code is also required to support arrow functions. If we detect\r\n     * the arrow operator, then we add the current (or some previous tokens) token to\r\n     * the list of variables so it does not get replaced by a lookup in the context\r\n     */\r\n    function compileExprToArray(expr) {\r\n        const localVars = new Set();\r\n        const tokens = tokenize(expr);\r\n        let i = 0;\r\n        let stack = []; // to track last opening (, [ or {\r\n        while (i < tokens.length) {\r\n            let token = tokens[i];\r\n            let prevToken = tokens[i - 1];\r\n            let nextToken = tokens[i + 1];\r\n            let groupType = stack[stack.length - 1];\r\n            switch (token.type) {\r\n                case \"LEFT_BRACE\":\r\n                case \"LEFT_BRACKET\":\r\n                case \"LEFT_PAREN\":\r\n                    stack.push(token.type);\r\n                    break;\r\n                case \"RIGHT_BRACE\":\r\n                case \"RIGHT_BRACKET\":\r\n                case \"RIGHT_PAREN\":\r\n                    stack.pop();\r\n            }\r\n            let isVar = token.type === \"SYMBOL\" && !RESERVED_WORDS.includes(token.value);\r\n            if (token.type === \"SYMBOL\" && !RESERVED_WORDS.includes(token.value)) {\r\n                if (prevToken) {\r\n                    // normalize missing tokens: {a} should be equivalent to {a:a}\r\n                    if (groupType === \"LEFT_BRACE\" &&\r\n                        isLeftSeparator(prevToken) &&\r\n                        isRightSeparator(nextToken)) {\r\n                        tokens.splice(i + 1, 0, { type: \"COLON\", value: \":\" }, { ...token });\r\n                        nextToken = tokens[i + 1];\r\n                    }\r\n                    if (prevToken.type === \"OPERATOR\" && prevToken.value === \".\") {\r\n                        isVar = false;\r\n                    }\r\n                    else if (prevToken.type === \"LEFT_BRACE\" || prevToken.type === \"COMMA\") {\r\n                        if (nextToken && nextToken.type === \"COLON\") {\r\n                            isVar = false;\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            if (token.type === \"TEMPLATE_STRING\") {\r\n                token.value = token.replace((expr) => compileExpr(expr));\r\n            }\r\n            if (nextToken && nextToken.type === \"OPERATOR\" && nextToken.value === \"=>\") {\r\n                if (token.type === \"RIGHT_PAREN\") {\r\n                    let j = i - 1;\r\n                    while (j > 0 && tokens[j].type !== \"LEFT_PAREN\") {\r\n                        if (tokens[j].type === \"SYMBOL\" && tokens[j].originalValue) {\r\n                            tokens[j].value = tokens[j].originalValue;\r\n                            localVars.add(tokens[j].value); //] = { id: tokens[j].value, expr: tokens[j].value };\r\n                        }\r\n                        j--;\r\n                    }\r\n                }\r\n                else {\r\n                    localVars.add(token.value); //] = { id: token.value, expr: token.value };\r\n                }\r\n            }\r\n            if (isVar) {\r\n                token.varName = token.value;\r\n                if (!localVars.has(token.value)) {\r\n                    token.originalValue = token.value;\r\n                    token.value = `ctx['${token.value}']`;\r\n                }\r\n            }\r\n            i++;\r\n        }\r\n        // Mark all variables that have been used locally.\r\n        // This assumes the expression has only one scope (incorrect but \"good enough for now\")\r\n        for (const token of tokens) {\r\n            if (token.type === \"SYMBOL\" && token.varName && localVars.has(token.value)) {\r\n                token.originalValue = token.value;\r\n                token.value = `_${token.value}`;\r\n                token.isLocal = true;\r\n            }\r\n        }\r\n        return tokens;\r\n    }\r\n    // Leading spaces are trimmed during tokenization, so they need to be added back for some values\r\n    const paddedValues = new Map([[\"in \", \" in \"]]);\r\n    function compileExpr(expr) {\r\n        return compileExprToArray(expr)\r\n            .map((t) => paddedValues.get(t.value) || t.value)\r\n            .join(\"\");\r\n    }\r\n    const INTERP_REGEXP = /\\{\\{.*?\\}\\}|\\#\\{.*?\\}/g;\r\n    function replaceDynamicParts(s, replacer) {\r\n        let matches = s.match(INTERP_REGEXP);\r\n        if (matches && matches[0].length === s.length) {\r\n            return `(${replacer(s.slice(2, matches[0][0] === \"{\" ? -2 : -1))})`;\r\n        }\r\n        let r = s.replace(INTERP_REGEXP, (s) => \"${\" + replacer(s.slice(2, s[0] === \"{\" ? -2 : -1)) + \"}\");\r\n        return \"`\" + r + \"`\";\r\n    }\r\n    function interpolate(s) {\r\n        return replaceDynamicParts(s, compileExpr);\r\n    }\r\n\r\n    const whitespaceRE = /\\s+/g;\r\n    // using a non-html document so that <inner/outer>HTML serializes as XML instead\r\n    // of HTML (as we will parse it as xml later)\r\n    const xmlDoc = document.implementation.createDocument(null, null, null);\r\n    const MODS = new Set([\"stop\", \"capture\", \"prevent\", \"self\", \"synthetic\"]);\r\n    let nextDataIds = {};\r\n    function generateId(prefix = \"\") {\r\n        nextDataIds[prefix] = (nextDataIds[prefix] || 0) + 1;\r\n        return prefix + nextDataIds[prefix];\r\n    }\r\n    function isProp(tag, key) {\r\n        switch (tag) {\r\n            case \"input\":\r\n                return (key === \"checked\" ||\r\n                    key === \"indeterminate\" ||\r\n                    key === \"value\" ||\r\n                    key === \"readonly\" ||\r\n                    key === \"readOnly\" ||\r\n                    key === \"disabled\");\r\n            case \"option\":\r\n                return key === \"selected\" || key === \"disabled\";\r\n            case \"textarea\":\r\n                return key === \"value\" || key === \"readonly\" || key === \"readOnly\" || key === \"disabled\";\r\n            case \"select\":\r\n                return key === \"value\" || key === \"disabled\";\r\n            case \"button\":\r\n            case \"optgroup\":\r\n                return key === \"disabled\";\r\n        }\r\n        return false;\r\n    }\r\n    /**\r\n     * Returns a template literal that evaluates to str. You can add interpolation\r\n     * sigils into the string if required\r\n     */\r\n    function toStringExpression(str) {\r\n        return `\\`${str.replace(/\\\\/g, \"\\\\\\\\\").replace(/`/g, \"\\\\`\").replace(/\\$\\{/, \"\\\\${\")}\\``;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // BlockDescription\r\n    // -----------------------------------------------------------------------------\r\n    class BlockDescription {\r\n        constructor(target, type) {\r\n            this.dynamicTagName = null;\r\n            this.isRoot = false;\r\n            this.hasDynamicChildren = false;\r\n            this.children = [];\r\n            this.data = [];\r\n            this.childNumber = 0;\r\n            this.parentVar = \"\";\r\n            this.id = BlockDescription.nextBlockId++;\r\n            this.varName = \"b\" + this.id;\r\n            this.blockName = \"block\" + this.id;\r\n            this.target = target;\r\n            this.type = type;\r\n        }\r\n        insertData(str, prefix = \"d\") {\r\n            const id = generateId(prefix);\r\n            this.target.addLine(`let ${id} = ${str};`);\r\n            return this.data.push(id) - 1;\r\n        }\r\n        insert(dom) {\r\n            if (this.currentDom) {\r\n                this.currentDom.appendChild(dom);\r\n            }\r\n            else {\r\n                this.dom = dom;\r\n            }\r\n        }\r\n        generateExpr(expr) {\r\n            if (this.type === \"block\") {\r\n                const hasChildren = this.children.length;\r\n                let params = this.data.length ? `[${this.data.join(\", \")}]` : hasChildren ? \"[]\" : \"\";\r\n                if (hasChildren) {\r\n                    params += \", [\" + this.children.map((c) => c.varName).join(\", \") + \"]\";\r\n                }\r\n                if (this.dynamicTagName) {\r\n                    return `toggler(${this.dynamicTagName}, ${this.blockName}(${this.dynamicTagName})(${params}))`;\r\n                }\r\n                return `${this.blockName}(${params})`;\r\n            }\r\n            else if (this.type === \"list\") {\r\n                return `list(c_block${this.id})`;\r\n            }\r\n            return expr;\r\n        }\r\n        asXmlString() {\r\n            // Can't use outerHTML on text/comment nodes\r\n            // append dom to any element and use innerHTML instead\r\n            const t = xmlDoc.createElement(\"t\");\r\n            t.appendChild(this.dom);\r\n            return t.innerHTML;\r\n        }\r\n    }\r\n    BlockDescription.nextBlockId = 1;\r\n    function createContext(parentCtx, params) {\r\n        return Object.assign({\r\n            block: null,\r\n            index: 0,\r\n            forceNewBlock: true,\r\n            translate: parentCtx.translate,\r\n            tKeyExpr: null,\r\n            nameSpace: parentCtx.nameSpace,\r\n            tModelSelectedExpr: parentCtx.tModelSelectedExpr,\r\n        }, params);\r\n    }\r\n    class CodeTarget {\r\n        constructor(name, on) {\r\n            this.indentLevel = 0;\r\n            this.loopLevel = 0;\r\n            this.code = [];\r\n            this.hasRoot = false;\r\n            this.hasCache = false;\r\n            this.shouldProtectScope = false;\r\n            this.hasRefWrapper = false;\r\n            this.name = name;\r\n            this.on = on || null;\r\n        }\r\n        addLine(line, idx) {\r\n            const prefix = new Array(this.indentLevel + 2).join(\"  \");\r\n            if (idx === undefined) {\r\n                this.code.push(prefix + line);\r\n            }\r\n            else {\r\n                this.code.splice(idx, 0, prefix + line);\r\n            }\r\n        }\r\n        generateCode() {\r\n            let result = [];\r\n            result.push(`function ${this.name}(ctx, node, key = \"\") {`);\r\n            if (this.shouldProtectScope) {\r\n                result.push(`  ctx = Object.create(ctx);`);\r\n                result.push(`  ctx[isBoundary] = 1`);\r\n            }\r\n            if (this.hasRefWrapper) {\r\n                result.push(`  let refWrapper = makeRefWrapper(this.__owl__);`);\r\n            }\r\n            if (this.hasCache) {\r\n                result.push(`  let cache = ctx.cache || {};`);\r\n                result.push(`  let nextCache = ctx.cache = {};`);\r\n            }\r\n            for (let line of this.code) {\r\n                result.push(line);\r\n            }\r\n            if (!this.hasRoot) {\r\n                result.push(`return text('');`);\r\n            }\r\n            result.push(`}`);\r\n            return result.join(\"\\n  \");\r\n        }\r\n        currentKey(ctx) {\r\n            let key = this.loopLevel ? `key${this.loopLevel}` : \"key\";\r\n            if (ctx.tKeyExpr) {\r\n                key = `${ctx.tKeyExpr} + ${key}`;\r\n            }\r\n            return key;\r\n        }\r\n    }\r\n    const TRANSLATABLE_ATTRS = [\"label\", \"title\", \"placeholder\", \"alt\"];\r\n    const translationRE = /^(\\s*)([\\s\\S]+?)(\\s*)$/;\r\n    class CodeGenerator {\r\n        constructor(ast, options) {\r\n            this.blocks = [];\r\n            this.nextBlockId = 1;\r\n            this.isDebug = false;\r\n            this.targets = [];\r\n            this.target = new CodeTarget(\"template\");\r\n            this.translatableAttributes = TRANSLATABLE_ATTRS;\r\n            this.staticDefs = [];\r\n            this.slotNames = new Set();\r\n            this.helpers = new Set();\r\n            this.translateFn = options.translateFn || ((s) => s);\r\n            if (options.translatableAttributes) {\r\n                const attrs = new Set(TRANSLATABLE_ATTRS);\r\n                for (let attr of options.translatableAttributes) {\r\n                    if (attr.startsWith(\"-\")) {\r\n                        attrs.delete(attr.slice(1));\r\n                    }\r\n                    else {\r\n                        attrs.add(attr);\r\n                    }\r\n                }\r\n                this.translatableAttributes = [...attrs];\r\n            }\r\n            this.hasSafeContext = options.hasSafeContext || false;\r\n            this.dev = options.dev || false;\r\n            this.ast = ast;\r\n            this.templateName = options.name;\r\n            if (options.hasGlobalValues) {\r\n                this.helpers.add(\"__globals__\");\r\n            }\r\n        }\r\n        generateCode() {\r\n            const ast = this.ast;\r\n            this.isDebug = ast.type === 12 /* TDebug */;\r\n            BlockDescription.nextBlockId = 1;\r\n            nextDataIds = {};\r\n            this.compileAST(ast, {\r\n                block: null,\r\n                index: 0,\r\n                forceNewBlock: false,\r\n                isLast: true,\r\n                translate: true,\r\n                tKeyExpr: null,\r\n            });\r\n            // define blocks and utility functions\r\n            let mainCode = [`  let { text, createBlock, list, multi, html, toggler, comment } = bdom;`];\r\n            if (this.helpers.size) {\r\n                mainCode.push(`let { ${[...this.helpers].join(\", \")} } = helpers;`);\r\n            }\r\n            if (this.templateName) {\r\n                mainCode.push(`// Template name: \"${this.templateName}\"`);\r\n            }\r\n            for (let { id, expr } of this.staticDefs) {\r\n                mainCode.push(`const ${id} = ${expr};`);\r\n            }\r\n            // define all blocks\r\n            if (this.blocks.length) {\r\n                mainCode.push(``);\r\n                for (let block of this.blocks) {\r\n                    if (block.dom) {\r\n                        let xmlString = toStringExpression(block.asXmlString());\r\n                        if (block.dynamicTagName) {\r\n                            xmlString = xmlString.replace(/^`<\\w+/, `\\`<\\${tag || '${block.dom.nodeName}'}`);\r\n                            xmlString = xmlString.replace(/\\w+>`$/, `\\${tag || '${block.dom.nodeName}'}>\\``);\r\n                            mainCode.push(`let ${block.blockName} = tag => createBlock(${xmlString});`);\r\n                        }\r\n                        else {\r\n                            mainCode.push(`let ${block.blockName} = createBlock(${xmlString});`);\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n            // define all slots/defaultcontent function\r\n            if (this.targets.length) {\r\n                for (let fn of this.targets) {\r\n                    mainCode.push(\"\");\r\n                    mainCode = mainCode.concat(fn.generateCode());\r\n                }\r\n            }\r\n            // generate main code\r\n            mainCode.push(\"\");\r\n            mainCode = mainCode.concat(\"return \" + this.target.generateCode());\r\n            const code = mainCode.join(\"\\n  \");\r\n            if (this.isDebug) {\r\n                const msg = `[Owl Debug]\\n${code}`;\r\n                console.log(msg);\r\n            }\r\n            return code;\r\n        }\r\n        compileInNewTarget(prefix, ast, ctx, on) {\r\n            const name = generateId(prefix);\r\n            const initialTarget = this.target;\r\n            const target = new CodeTarget(name, on);\r\n            this.targets.push(target);\r\n            this.target = target;\r\n            this.compileAST(ast, createContext(ctx));\r\n            this.target = initialTarget;\r\n            return name;\r\n        }\r\n        addLine(line, idx) {\r\n            this.target.addLine(line, idx);\r\n        }\r\n        define(varName, expr) {\r\n            this.addLine(`const ${varName} = ${expr};`);\r\n        }\r\n        insertAnchor(block, index = block.children.length) {\r\n            const tag = `block-child-${index}`;\r\n            const anchor = xmlDoc.createElement(tag);\r\n            block.insert(anchor);\r\n        }\r\n        createBlock(parentBlock, type, ctx) {\r\n            const hasRoot = this.target.hasRoot;\r\n            const block = new BlockDescription(this.target, type);\r\n            if (!hasRoot) {\r\n                this.target.hasRoot = true;\r\n                block.isRoot = true;\r\n            }\r\n            if (parentBlock) {\r\n                parentBlock.children.push(block);\r\n                if (parentBlock.type === \"list\") {\r\n                    block.parentVar = `c_block${parentBlock.id}`;\r\n                }\r\n            }\r\n            return block;\r\n        }\r\n        insertBlock(expression, block, ctx) {\r\n            let blockExpr = block.generateExpr(expression);\r\n            if (block.parentVar) {\r\n                let key = this.target.currentKey(ctx);\r\n                this.helpers.add(\"withKey\");\r\n                this.addLine(`${block.parentVar}[${ctx.index}] = withKey(${blockExpr}, ${key});`);\r\n                return;\r\n            }\r\n            if (ctx.tKeyExpr) {\r\n                blockExpr = `toggler(${ctx.tKeyExpr}, ${blockExpr})`;\r\n            }\r\n            if (block.isRoot) {\r\n                if (this.target.on) {\r\n                    blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);\r\n                }\r\n                this.addLine(`return ${blockExpr};`);\r\n            }\r\n            else {\r\n                this.define(block.varName, blockExpr);\r\n            }\r\n        }\r\n        /**\r\n         * Captures variables that are used inside of an expression. This is useful\r\n         * because in compiled code, almost all variables are accessed through the ctx\r\n         * object. In the case of functions, that lookup in the context can be delayed\r\n         * which can cause issues if the value has changed since the function was\r\n         * defined.\r\n         *\r\n         * @param expr the expression to capture\r\n         * @param forceCapture whether the expression should capture its scope even if\r\n         *  it doesn't contain a function. Useful when the expression will be used as\r\n         *  a function body.\r\n         * @returns a new expression that uses the captured values\r\n         */\r\n        captureExpression(expr, forceCapture = false) {\r\n            if (!forceCapture && !expr.includes(\"=>\")) {\r\n                return compileExpr(expr);\r\n            }\r\n            const tokens = compileExprToArray(expr);\r\n            const mapping = new Map();\r\n            return tokens\r\n                .map((tok) => {\r\n                if (tok.varName && !tok.isLocal) {\r\n                    if (!mapping.has(tok.varName)) {\r\n                        const varId = generateId(\"v\");\r\n                        mapping.set(tok.varName, varId);\r\n                        this.define(varId, tok.value);\r\n                    }\r\n                    tok.value = mapping.get(tok.varName);\r\n                }\r\n                return tok.value;\r\n            })\r\n                .join(\"\");\r\n        }\r\n        translate(str) {\r\n            const match = translationRE.exec(str);\r\n            return match[1] + this.translateFn(match[2]) + match[3];\r\n        }\r\n        /**\r\n         * @returns the newly created block name, if any\r\n         */\r\n        compileAST(ast, ctx) {\r\n            switch (ast.type) {\r\n                case 1 /* Comment */:\r\n                    return this.compileComment(ast, ctx);\r\n                case 0 /* Text */:\r\n                    return this.compileText(ast, ctx);\r\n                case 2 /* DomNode */:\r\n                    return this.compileTDomNode(ast, ctx);\r\n                case 4 /* TEsc */:\r\n                    return this.compileTEsc(ast, ctx);\r\n                case 8 /* TOut */:\r\n                    return this.compileTOut(ast, ctx);\r\n                case 5 /* TIf */:\r\n                    return this.compileTIf(ast, ctx);\r\n                case 9 /* TForEach */:\r\n                    return this.compileTForeach(ast, ctx);\r\n                case 10 /* TKey */:\r\n                    return this.compileTKey(ast, ctx);\r\n                case 3 /* Multi */:\r\n                    return this.compileMulti(ast, ctx);\r\n                case 7 /* TCall */:\r\n                    return this.compileTCall(ast, ctx);\r\n                case 15 /* TCallBlock */:\r\n                    return this.compileTCallBlock(ast, ctx);\r\n                case 6 /* TSet */:\r\n                    return this.compileTSet(ast, ctx);\r\n                case 11 /* TComponent */:\r\n                    return this.compileComponent(ast, ctx);\r\n                case 12 /* TDebug */:\r\n                    return this.compileDebug(ast, ctx);\r\n                case 13 /* TLog */:\r\n                    return this.compileLog(ast, ctx);\r\n                case 14 /* TSlot */:\r\n                    return this.compileTSlot(ast, ctx);\r\n                case 16 /* TTranslation */:\r\n                    return this.compileTTranslation(ast, ctx);\r\n                case 17 /* TPortal */:\r\n                    return this.compileTPortal(ast, ctx);\r\n            }\r\n        }\r\n        compileDebug(ast, ctx) {\r\n            this.addLine(`debugger;`);\r\n            if (ast.content) {\r\n                return this.compileAST(ast.content, ctx);\r\n            }\r\n            return null;\r\n        }\r\n        compileLog(ast, ctx) {\r\n            this.addLine(`console.log(${compileExpr(ast.expr)});`);\r\n            if (ast.content) {\r\n                return this.compileAST(ast.content, ctx);\r\n            }\r\n            return null;\r\n        }\r\n        compileComment(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            const isNewBlock = !block || forceNewBlock;\r\n            if (isNewBlock) {\r\n                block = this.createBlock(block, \"comment\", ctx);\r\n                this.insertBlock(`comment(${toStringExpression(ast.value)})`, block, {\r\n                    ...ctx,\r\n                    forceNewBlock: forceNewBlock && !block,\r\n                });\r\n            }\r\n            else {\r\n                const text = xmlDoc.createComment(ast.value);\r\n                block.insert(text);\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileText(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            let value = ast.value;\r\n            if (value && ctx.translate !== false) {\r\n                value = this.translate(value);\r\n            }\r\n            if (!ctx.inPreTag) {\r\n                value = value.replace(whitespaceRE, \" \");\r\n            }\r\n            if (!block || forceNewBlock) {\r\n                block = this.createBlock(block, \"text\", ctx);\r\n                this.insertBlock(`text(${toStringExpression(value)})`, block, {\r\n                    ...ctx,\r\n                    forceNewBlock: forceNewBlock && !block,\r\n                });\r\n            }\r\n            else {\r\n                const createFn = ast.type === 0 /* Text */ ? xmlDoc.createTextNode : xmlDoc.createComment;\r\n                block.insert(createFn.call(xmlDoc, value));\r\n            }\r\n            return block.varName;\r\n        }\r\n        generateHandlerCode(rawEvent, handler) {\r\n            const modifiers = rawEvent\r\n                .split(\".\")\r\n                .slice(1)\r\n                .map((m) => {\r\n                if (!MODS.has(m)) {\r\n                    throw new OwlError(`Unknown event modifier: '${m}'`);\r\n                }\r\n                return `\"${m}\"`;\r\n            });\r\n            let modifiersCode = \"\";\r\n            if (modifiers.length) {\r\n                modifiersCode = `${modifiers.join(\",\")}, `;\r\n            }\r\n            return `[${modifiersCode}${this.captureExpression(handler)}, ctx]`;\r\n        }\r\n        compileTDomNode(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            const isNewBlock = !block || forceNewBlock || ast.dynamicTag !== null || ast.ns;\r\n            let codeIdx = this.target.code.length;\r\n            if (isNewBlock) {\r\n                if ((ast.dynamicTag || ctx.tKeyExpr || ast.ns) && ctx.block) {\r\n                    this.insertAnchor(ctx.block);\r\n                }\r\n                block = this.createBlock(block, \"block\", ctx);\r\n                this.blocks.push(block);\r\n                if (ast.dynamicTag) {\r\n                    const tagExpr = generateId(\"tag\");\r\n                    this.define(tagExpr, compileExpr(ast.dynamicTag));\r\n                    block.dynamicTagName = tagExpr;\r\n                }\r\n            }\r\n            // attributes\r\n            const attrs = {};\r\n            for (let key in ast.attrs) {\r\n                let expr, attrName;\r\n                if (key.startsWith(\"t-attf\")) {\r\n                    expr = interpolate(ast.attrs[key]);\r\n                    const idx = block.insertData(expr, \"attr\");\r\n                    attrName = key.slice(7);\r\n                    attrs[\"block-attribute-\" + idx] = attrName;\r\n                }\r\n                else if (key.startsWith(\"t-att\")) {\r\n                    attrName = key === \"t-att\" ? null : key.slice(6);\r\n                    expr = compileExpr(ast.attrs[key]);\r\n                    if (attrName && isProp(ast.tag, attrName)) {\r\n                        if (attrName === \"readonly\") {\r\n                            // the property has a different name than the attribute\r\n                            attrName = \"readOnly\";\r\n                        }\r\n                        // we force a new string or new boolean to bypass the equality check in blockdom when patching same value\r\n                        if (attrName === \"value\") {\r\n                            // When the expression is falsy (except 0), fall back to an empty string\r\n                            expr = `new String((${expr}) === 0 ? 0 : ((${expr}) || \"\"))`;\r\n                        }\r\n                        else {\r\n                            expr = `new Boolean(${expr})`;\r\n                        }\r\n                        const idx = block.insertData(expr, \"prop\");\r\n                        attrs[`block-property-${idx}`] = attrName;\r\n                    }\r\n                    else {\r\n                        const idx = block.insertData(expr, \"attr\");\r\n                        if (key === \"t-att\") {\r\n                            attrs[`block-attributes`] = String(idx);\r\n                        }\r\n                        else {\r\n                            attrs[`block-attribute-${idx}`] = attrName;\r\n                        }\r\n                    }\r\n                }\r\n                else if (this.translatableAttributes.includes(key)) {\r\n                    attrs[key] = this.translateFn(ast.attrs[key]);\r\n                }\r\n                else {\r\n                    expr = `\"${ast.attrs[key]}\"`;\r\n                    attrName = key;\r\n                    attrs[key] = ast.attrs[key];\r\n                }\r\n                if (attrName === \"value\" && ctx.tModelSelectedExpr) {\r\n                    let selectedId = block.insertData(`${ctx.tModelSelectedExpr} === ${expr}`, \"attr\");\r\n                    attrs[`block-attribute-${selectedId}`] = \"selected\";\r\n                }\r\n            }\r\n            // t-model\r\n            let tModelSelectedExpr;\r\n            if (ast.model) {\r\n                const { hasDynamicChildren, baseExpr, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;\r\n                const baseExpression = compileExpr(baseExpr);\r\n                const bExprId = generateId(\"bExpr\");\r\n                this.define(bExprId, baseExpression);\r\n                const expression = compileExpr(expr);\r\n                const exprId = generateId(\"expr\");\r\n                this.define(exprId, expression);\r\n                const fullExpression = `${bExprId}[${exprId}]`;\r\n                let idx;\r\n                if (specialInitTargetAttr) {\r\n                    let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;\r\n                    if (!targetExpr && ast.attrs) {\r\n                        // look at the dynamic attribute counterpart\r\n                        const dynamicTgExpr = ast.attrs[`t-att-${targetAttr}`];\r\n                        if (dynamicTgExpr) {\r\n                            targetExpr = compileExpr(dynamicTgExpr);\r\n                        }\r\n                    }\r\n                    idx = block.insertData(`${fullExpression} === ${targetExpr}`, \"prop\");\r\n                    attrs[`block-property-${idx}`] = specialInitTargetAttr;\r\n                }\r\n                else if (hasDynamicChildren) {\r\n                    const bValueId = generateId(\"bValue\");\r\n                    tModelSelectedExpr = `${bValueId}`;\r\n                    this.define(tModelSelectedExpr, fullExpression);\r\n                }\r\n                else {\r\n                    idx = block.insertData(`${fullExpression}`, \"prop\");\r\n                    attrs[`block-property-${idx}`] = targetAttr;\r\n                }\r\n                this.helpers.add(\"toNumber\");\r\n                let valueCode = `ev.target.${targetAttr}`;\r\n                valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;\r\n                valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;\r\n                const handler = `[(ev) => { ${fullExpression} = ${valueCode}; }]`;\r\n                idx = block.insertData(handler, \"hdlr\");\r\n                attrs[`block-handler-${idx}`] = eventType;\r\n            }\r\n            // event handlers\r\n            for (let ev in ast.on) {\r\n                const name = this.generateHandlerCode(ev, ast.on[ev]);\r\n                const idx = block.insertData(name, \"hdlr\");\r\n                attrs[`block-handler-${idx}`] = ev;\r\n            }\r\n            // t-ref\r\n            if (ast.ref) {\r\n                if (this.dev) {\r\n                    this.helpers.add(\"makeRefWrapper\");\r\n                    this.target.hasRefWrapper = true;\r\n                }\r\n                const isDynamic = INTERP_REGEXP.test(ast.ref);\r\n                let name = `\\`${ast.ref}\\``;\r\n                if (isDynamic) {\r\n                    name = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));\r\n                }\r\n                let setRefStr = `(el) => this.__owl__.setRef((${name}), el)`;\r\n                if (this.dev) {\r\n                    setRefStr = `refWrapper(${name}, ${setRefStr})`;\r\n                }\r\n                const idx = block.insertData(setRefStr, \"ref\");\r\n                attrs[\"block-ref\"] = String(idx);\r\n            }\r\n            const nameSpace = ast.ns || ctx.nameSpace;\r\n            const dom = nameSpace\r\n                ? xmlDoc.createElementNS(nameSpace, ast.tag)\r\n                : xmlDoc.createElement(ast.tag);\r\n            for (const [attr, val] of Object.entries(attrs)) {\r\n                if (!(attr === \"class\" && val === \"\")) {\r\n                    dom.setAttribute(attr, val);\r\n                }\r\n            }\r\n            block.insert(dom);\r\n            if (ast.content.length) {\r\n                const initialDom = block.currentDom;\r\n                block.currentDom = dom;\r\n                const children = ast.content;\r\n                for (let i = 0; i < children.length; i++) {\r\n                    const child = ast.content[i];\r\n                    const subCtx = createContext(ctx, {\r\n                        block,\r\n                        index: block.childNumber,\r\n                        forceNewBlock: false,\r\n                        isLast: ctx.isLast && i === children.length - 1,\r\n                        tKeyExpr: ctx.tKeyExpr,\r\n                        nameSpace,\r\n                        tModelSelectedExpr,\r\n                        inPreTag: ctx.inPreTag || ast.tag === \"pre\",\r\n                    });\r\n                    this.compileAST(child, subCtx);\r\n                }\r\n                block.currentDom = initialDom;\r\n            }\r\n            if (isNewBlock) {\r\n                this.insertBlock(`${block.blockName}(ddd)`, block, ctx);\r\n                // may need to rewrite code!\r\n                if (block.children.length && block.hasDynamicChildren) {\r\n                    const code = this.target.code;\r\n                    const children = block.children.slice();\r\n                    let current = children.shift();\r\n                    for (let i = codeIdx; i < code.length; i++) {\r\n                        if (code[i].trimStart().startsWith(`const ${current.varName} `)) {\r\n                            code[i] = code[i].replace(`const ${current.varName}`, current.varName);\r\n                            current = children.shift();\r\n                            if (!current)\r\n                                break;\r\n                        }\r\n                    }\r\n                    this.addLine(`let ${block.children.map((c) => c.varName).join(\", \")};`, codeIdx);\r\n                }\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileTEsc(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            let expr;\r\n            if (ast.expr === \"0\") {\r\n                this.helpers.add(\"zero\");\r\n                expr = `ctx[zero]`;\r\n            }\r\n            else {\r\n                expr = compileExpr(ast.expr);\r\n                if (ast.defaultValue) {\r\n                    this.helpers.add(\"withDefault\");\r\n                    // FIXME: defaultValue is not translated\r\n                    expr = `withDefault(${expr}, ${toStringExpression(ast.defaultValue)})`;\r\n                }\r\n            }\r\n            if (!block || forceNewBlock) {\r\n                block = this.createBlock(block, \"text\", ctx);\r\n                this.insertBlock(`text(${expr})`, block, { ...ctx, forceNewBlock: forceNewBlock && !block });\r\n            }\r\n            else {\r\n                const idx = block.insertData(expr, \"txt\");\r\n                const text = xmlDoc.createElement(`block-text-${idx}`);\r\n                block.insert(text);\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileTOut(ast, ctx) {\r\n            let { block } = ctx;\r\n            if (block) {\r\n                this.insertAnchor(block);\r\n            }\r\n            block = this.createBlock(block, \"html\", ctx);\r\n            let blockStr;\r\n            if (ast.expr === \"0\") {\r\n                this.helpers.add(\"zero\");\r\n                blockStr = `ctx[zero]`;\r\n            }\r\n            else if (ast.body) {\r\n                let bodyValue = null;\r\n                bodyValue = BlockDescription.nextBlockId;\r\n                const subCtx = createContext(ctx);\r\n                this.compileAST({ type: 3 /* Multi */, content: ast.body }, subCtx);\r\n                this.helpers.add(\"safeOutput\");\r\n                blockStr = `safeOutput(${compileExpr(ast.expr)}, b${bodyValue})`;\r\n            }\r\n            else {\r\n                this.helpers.add(\"safeOutput\");\r\n                blockStr = `safeOutput(${compileExpr(ast.expr)})`;\r\n            }\r\n            this.insertBlock(blockStr, block, ctx);\r\n            return block.varName;\r\n        }\r\n        compileTIfBranch(content, block, ctx) {\r\n            this.target.indentLevel++;\r\n            let childN = block.children.length;\r\n            this.compileAST(content, createContext(ctx, { block, index: ctx.index }));\r\n            if (block.children.length > childN) {\r\n                // we have some content => need to insert an anchor at correct index\r\n                this.insertAnchor(block, childN);\r\n            }\r\n            this.target.indentLevel--;\r\n        }\r\n        compileTIf(ast, ctx, nextNode) {\r\n            let { block, forceNewBlock } = ctx;\r\n            const codeIdx = this.target.code.length;\r\n            const isNewBlock = !block || (block.type !== \"multi\" && forceNewBlock);\r\n            if (block) {\r\n                block.hasDynamicChildren = true;\r\n            }\r\n            if (!block || (block.type !== \"multi\" && forceNewBlock)) {\r\n                block = this.createBlock(block, \"multi\", ctx);\r\n            }\r\n            this.addLine(`if (${compileExpr(ast.condition)}) {`);\r\n            this.compileTIfBranch(ast.content, block, ctx);\r\n            if (ast.tElif) {\r\n                for (let clause of ast.tElif) {\r\n                    this.addLine(`} else if (${compileExpr(clause.condition)}) {`);\r\n                    this.compileTIfBranch(clause.content, block, ctx);\r\n                }\r\n            }\r\n            if (ast.tElse) {\r\n                this.addLine(`} else {`);\r\n                this.compileTIfBranch(ast.tElse, block, ctx);\r\n            }\r\n            this.addLine(\"}\");\r\n            if (isNewBlock) {\r\n                // note: this part is duplicated from end of compiledomnode:\r\n                if (block.children.length) {\r\n                    const code = this.target.code;\r\n                    const children = block.children.slice();\r\n                    let current = children.shift();\r\n                    for (let i = codeIdx; i < code.length; i++) {\r\n                        if (code[i].trimStart().startsWith(`const ${current.varName} `)) {\r\n                            code[i] = code[i].replace(`const ${current.varName}`, current.varName);\r\n                            current = children.shift();\r\n                            if (!current)\r\n                                break;\r\n                        }\r\n                    }\r\n                    this.addLine(`let ${block.children.map((c) => c.varName).join(\", \")};`, codeIdx);\r\n                }\r\n                // note: this part is duplicated from end of compilemulti:\r\n                const args = block.children.map((c) => c.varName).join(\", \");\r\n                this.insertBlock(`multi([${args}])`, block, ctx);\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileTForeach(ast, ctx) {\r\n            let { block } = ctx;\r\n            if (block) {\r\n                this.insertAnchor(block);\r\n            }\r\n            block = this.createBlock(block, \"list\", ctx);\r\n            this.target.loopLevel++;\r\n            const loopVar = `i${this.target.loopLevel}`;\r\n            this.addLine(`ctx = Object.create(ctx);`);\r\n            const vals = `v_block${block.id}`;\r\n            const keys = `k_block${block.id}`;\r\n            const l = `l_block${block.id}`;\r\n            const c = `c_block${block.id}`;\r\n            this.helpers.add(\"prepareList\");\r\n            this.define(`[${keys}, ${vals}, ${l}, ${c}]`, `prepareList(${compileExpr(ast.collection)});`);\r\n            // Throw errors on duplicate keys in dev mode\r\n            if (this.dev) {\r\n                this.define(`keys${block.id}`, `new Set()`);\r\n            }\r\n            this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);\r\n            this.target.indentLevel++;\r\n            this.addLine(`ctx[\\`${ast.elem}\\`] = ${keys}[${loopVar}];`);\r\n            if (!ast.hasNoFirst) {\r\n                this.addLine(`ctx[\\`${ast.elem}_first\\`] = ${loopVar} === 0;`);\r\n            }\r\n            if (!ast.hasNoLast) {\r\n                this.addLine(`ctx[\\`${ast.elem}_last\\`] = ${loopVar} === ${keys}.length - 1;`);\r\n            }\r\n            if (!ast.hasNoIndex) {\r\n                this.addLine(`ctx[\\`${ast.elem}_index\\`] = ${loopVar};`);\r\n            }\r\n            if (!ast.hasNoValue) {\r\n                this.addLine(`ctx[\\`${ast.elem}_value\\`] = ${vals}[${loopVar}];`);\r\n            }\r\n            this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);\r\n            if (this.dev) {\r\n                // Throw error on duplicate keys in dev mode\r\n                this.helpers.add(\"OwlError\");\r\n                this.addLine(`if (keys${block.id}.has(String(key${this.target.loopLevel}))) { throw new OwlError(\\`Got duplicate key in t-foreach: \\${key${this.target.loopLevel}}\\`)}`);\r\n                this.addLine(`keys${block.id}.add(String(key${this.target.loopLevel}));`);\r\n            }\r\n            let id;\r\n            if (ast.memo) {\r\n                this.target.hasCache = true;\r\n                id = generateId();\r\n                this.define(`memo${id}`, compileExpr(ast.memo));\r\n                this.define(`vnode${id}`, `cache[key${this.target.loopLevel}];`);\r\n                this.addLine(`if (vnode${id}) {`);\r\n                this.target.indentLevel++;\r\n                this.addLine(`if (shallowEqual(vnode${id}.memo, memo${id})) {`);\r\n                this.target.indentLevel++;\r\n                this.addLine(`${c}[${loopVar}] = vnode${id};`);\r\n                this.addLine(`nextCache[key${this.target.loopLevel}] = vnode${id};`);\r\n                this.addLine(`continue;`);\r\n                this.target.indentLevel--;\r\n                this.addLine(\"}\");\r\n                this.target.indentLevel--;\r\n                this.addLine(\"}\");\r\n            }\r\n            const subCtx = createContext(ctx, { block, index: loopVar });\r\n            this.compileAST(ast.body, subCtx);\r\n            if (ast.memo) {\r\n                this.addLine(`nextCache[key${this.target.loopLevel}] = Object.assign(${c}[${loopVar}], {memo: memo${id}});`);\r\n            }\r\n            this.target.indentLevel--;\r\n            this.target.loopLevel--;\r\n            this.addLine(`}`);\r\n            if (!ctx.isLast) {\r\n                this.addLine(`ctx = ctx.__proto__;`);\r\n            }\r\n            this.insertBlock(\"l\", block, ctx);\r\n            return block.varName;\r\n        }\r\n        compileTKey(ast, ctx) {\r\n            const tKeyExpr = generateId(\"tKey_\");\r\n            this.define(tKeyExpr, compileExpr(ast.expr));\r\n            ctx = createContext(ctx, {\r\n                tKeyExpr,\r\n                block: ctx.block,\r\n                index: ctx.index,\r\n            });\r\n            return this.compileAST(ast.content, ctx);\r\n        }\r\n        compileMulti(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            const isNewBlock = !block || forceNewBlock;\r\n            let codeIdx = this.target.code.length;\r\n            if (isNewBlock) {\r\n                const n = ast.content.filter((c) => c.type !== 6 /* TSet */).length;\r\n                let result = null;\r\n                if (n <= 1) {\r\n                    for (let child of ast.content) {\r\n                        const blockName = this.compileAST(child, ctx);\r\n                        result = result || blockName;\r\n                    }\r\n                    return result;\r\n                }\r\n                block = this.createBlock(block, \"multi\", ctx);\r\n            }\r\n            let index = 0;\r\n            for (let i = 0, l = ast.content.length; i < l; i++) {\r\n                const child = ast.content[i];\r\n                const isTSet = child.type === 6 /* TSet */;\r\n                const subCtx = createContext(ctx, {\r\n                    block,\r\n                    index,\r\n                    forceNewBlock: !isTSet,\r\n                    isLast: ctx.isLast && i === l - 1,\r\n                });\r\n                this.compileAST(child, subCtx);\r\n                if (!isTSet) {\r\n                    index++;\r\n                }\r\n            }\r\n            if (isNewBlock) {\r\n                if (block.hasDynamicChildren && block.children.length) {\r\n                    const code = this.target.code;\r\n                    const children = block.children.slice();\r\n                    let current = children.shift();\r\n                    for (let i = codeIdx; i < code.length; i++) {\r\n                        if (code[i].trimStart().startsWith(`const ${current.varName} `)) {\r\n                            code[i] = code[i].replace(`const ${current.varName}`, current.varName);\r\n                            current = children.shift();\r\n                            if (!current)\r\n                                break;\r\n                        }\r\n                    }\r\n                    this.addLine(`let ${block.children.map((c) => c.varName).join(\", \")};`, codeIdx);\r\n                }\r\n                const args = block.children.map((c) => c.varName).join(\", \");\r\n                this.insertBlock(`multi([${args}])`, block, ctx);\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileTCall(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            let ctxVar = ctx.ctxVar || \"ctx\";\r\n            if (ast.context) {\r\n                ctxVar = generateId(\"ctx\");\r\n                this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);\r\n            }\r\n            const isDynamic = INTERP_REGEXP.test(ast.name);\r\n            const subTemplate = isDynamic ? interpolate(ast.name) : \"`\" + ast.name + \"`\";\r\n            if (block && !forceNewBlock) {\r\n                this.insertAnchor(block);\r\n            }\r\n            block = this.createBlock(block, \"multi\", ctx);\r\n            if (ast.body) {\r\n                this.addLine(`${ctxVar} = Object.create(${ctxVar});`);\r\n                this.addLine(`${ctxVar}[isBoundary] = 1;`);\r\n                this.helpers.add(\"isBoundary\");\r\n                const subCtx = createContext(ctx, { ctxVar });\r\n                const bl = this.compileMulti({ type: 3 /* Multi */, content: ast.body }, subCtx);\r\n                if (bl) {\r\n                    this.helpers.add(\"zero\");\r\n                    this.addLine(`${ctxVar}[zero] = ${bl};`);\r\n                }\r\n            }\r\n            const key = this.generateComponentKey();\r\n            if (isDynamic) {\r\n                const templateVar = generateId(\"template\");\r\n                if (!this.staticDefs.find((d) => d.id === \"call\")) {\r\n                    this.staticDefs.push({ id: \"call\", expr: `app.callTemplate.bind(app)` });\r\n                }\r\n                this.define(templateVar, subTemplate);\r\n                this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block, {\r\n                    ...ctx,\r\n                    forceNewBlock: !block,\r\n                });\r\n            }\r\n            else {\r\n                const id = generateId(`callTemplate_`);\r\n                this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });\r\n                this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block, {\r\n                    ...ctx,\r\n                    forceNewBlock: !block,\r\n                });\r\n            }\r\n            if (ast.body && !ctx.isLast) {\r\n                this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`);\r\n            }\r\n            return block.varName;\r\n        }\r\n        compileTCallBlock(ast, ctx) {\r\n            let { block, forceNewBlock } = ctx;\r\n            if (block) {\r\n                if (!forceNewBlock) {\r\n                    this.insertAnchor(block);\r\n                }\r\n            }\r\n            block = this.createBlock(block, \"multi\", ctx);\r\n            this.insertBlock(compileExpr(ast.name), block, { ...ctx, forceNewBlock: !block });\r\n            return block.varName;\r\n        }\r\n        compileTSet(ast, ctx) {\r\n            this.target.shouldProtectScope = true;\r\n            this.helpers.add(\"isBoundary\").add(\"withDefault\");\r\n            const expr = ast.value ? compileExpr(ast.value || \"\") : \"null\";\r\n            if (ast.body) {\r\n                this.helpers.add(\"LazyValue\");\r\n                const bodyAst = { type: 3 /* Multi */, content: ast.body };\r\n                const name = this.compileInNewTarget(\"value\", bodyAst, ctx);\r\n                let key = this.target.currentKey(ctx);\r\n                let value = `new LazyValue(${name}, ctx, this, node, ${key})`;\r\n                value = ast.value ? (value ? `withDefault(${expr}, ${value})` : expr) : value;\r\n                this.addLine(`ctx[\\`${ast.name}\\`] = ${value};`);\r\n            }\r\n            else {\r\n                let value;\r\n                if (ast.defaultValue) {\r\n                    const defaultValue = toStringExpression(ctx.translate ? this.translate(ast.defaultValue) : ast.defaultValue);\r\n                    if (ast.value) {\r\n                        value = `withDefault(${expr}, ${defaultValue})`;\r\n                    }\r\n                    else {\r\n                        value = defaultValue;\r\n                    }\r\n                }\r\n                else {\r\n                    value = expr;\r\n                }\r\n                this.helpers.add(\"setContextValue\");\r\n                this.addLine(`setContextValue(${ctx.ctxVar || \"ctx\"}, \"${ast.name}\", ${value});`);\r\n            }\r\n            return null;\r\n        }\r\n        generateComponentKey(currentKey = \"key\") {\r\n            const parts = [generateId(\"__\")];\r\n            for (let i = 0; i < this.target.loopLevel; i++) {\r\n                parts.push(`\\${key${i + 1}}`);\r\n            }\r\n            return `${currentKey} + \\`${parts.join(\"__\")}\\``;\r\n        }\r\n        /**\r\n         * Formats a prop name and value into a string suitable to be inserted in the\r\n         * generated code. For example:\r\n         *\r\n         * Name              Value            Result\r\n         * ---------------------------------------------------------\r\n         * \"number\"          \"state\"          \"number: ctx['state']\"\r\n         * \"something\"       \"\"               \"something: undefined\"\r\n         * \"some-prop\"       \"state\"          \"'some-prop': ctx['state']\"\r\n         * \"onClick.bind\"    \"onClick\"        \"onClick: bind(ctx, ctx['onClick'])\"\r\n         */\r\n        formatProp(name, value) {\r\n            if (name.endsWith(\".translate\")) {\r\n                value = toStringExpression(this.translateFn(value));\r\n            }\r\n            else {\r\n                value = this.captureExpression(value);\r\n            }\r\n            if (name.includes(\".\")) {\r\n                let [_name, suffix] = name.split(\".\");\r\n                name = _name;\r\n                switch (suffix) {\r\n                    case \"bind\":\r\n                        value = `(${value}).bind(this)`;\r\n                        break;\r\n                    case \"alike\":\r\n                    case \"translate\":\r\n                        break;\r\n                    default:\r\n                        throw new OwlError(`Invalid prop suffix: ${suffix}`);\r\n                }\r\n            }\r\n            name = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;\r\n            return `${name}: ${value || undefined}`;\r\n        }\r\n        formatPropObject(obj) {\r\n            return Object.entries(obj).map(([k, v]) => this.formatProp(k, v));\r\n        }\r\n        getPropString(props, dynProps) {\r\n            let propString = `{${props.join(\",\")}}`;\r\n            if (dynProps) {\r\n                propString = `Object.assign({}, ${compileExpr(dynProps)}${props.length ? \", \" + propString : \"\"})`;\r\n            }\r\n            return propString;\r\n        }\r\n        compileComponent(ast, ctx) {\r\n            let { block } = ctx;\r\n            // props\r\n            const hasSlotsProp = \"slots\" in (ast.props || {});\r\n            const props = ast.props ? this.formatPropObject(ast.props) : [];\r\n            // slots\r\n            let slotDef = \"\";\r\n            if (ast.slots) {\r\n                let ctxStr = \"ctx\";\r\n                if (this.target.loopLevel || !this.hasSafeContext) {\r\n                    ctxStr = generateId(\"ctx\");\r\n                    this.helpers.add(\"capture\");\r\n                    this.define(ctxStr, `capture(ctx)`);\r\n                }\r\n                let slotStr = [];\r\n                for (let slotName in ast.slots) {\r\n                    const slotAst = ast.slots[slotName];\r\n                    const params = [];\r\n                    if (slotAst.content) {\r\n                        const name = this.compileInNewTarget(\"slot\", slotAst.content, ctx, slotAst.on);\r\n                        params.push(`__render: ${name}.bind(this), __ctx: ${ctxStr}`);\r\n                    }\r\n                    const scope = ast.slots[slotName].scope;\r\n                    if (scope) {\r\n                        params.push(`__scope: \"${scope}\"`);\r\n                    }\r\n                    if (ast.slots[slotName].attrs) {\r\n                        params.push(...this.formatPropObject(ast.slots[slotName].attrs));\r\n                    }\r\n                    const slotInfo = `{${params.join(\", \")}}`;\r\n                    slotStr.push(`'${slotName}': ${slotInfo}`);\r\n                }\r\n                slotDef = `{${slotStr.join(\", \")}}`;\r\n            }\r\n            if (slotDef && !(ast.dynamicProps || hasSlotsProp)) {\r\n                this.helpers.add(\"markRaw\");\r\n                props.push(`slots: markRaw(${slotDef})`);\r\n            }\r\n            let propString = this.getPropString(props, ast.dynamicProps);\r\n            let propVar;\r\n            if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {\r\n                propVar = generateId(\"props\");\r\n                this.define(propVar, propString);\r\n                propString = propVar;\r\n            }\r\n            if (slotDef && (ast.dynamicProps || hasSlotsProp)) {\r\n                this.helpers.add(\"markRaw\");\r\n                this.addLine(`${propVar}.slots = markRaw(Object.assign(${slotDef}, ${propVar}.slots))`);\r\n            }\r\n            // cmap key\r\n            let expr;\r\n            if (ast.isDynamic) {\r\n                expr = generateId(\"Comp\");\r\n                this.define(expr, compileExpr(ast.name));\r\n            }\r\n            else {\r\n                expr = `\\`${ast.name}\\``;\r\n            }\r\n            if (this.dev) {\r\n                this.addLine(`helpers.validateProps(${expr}, ${propVar}, this);`);\r\n            }\r\n            if (block && (ctx.forceNewBlock === false || ctx.tKeyExpr)) {\r\n                // todo: check the forcenewblock condition\r\n                this.insertAnchor(block);\r\n            }\r\n            let keyArg = this.generateComponentKey();\r\n            if (ctx.tKeyExpr) {\r\n                keyArg = `${ctx.tKeyExpr} + ${keyArg}`;\r\n            }\r\n            let id = generateId(\"comp\");\r\n            const propList = [];\r\n            for (let p in ast.props || {}) {\r\n                let [name, suffix] = p.split(\".\");\r\n                if (!suffix) {\r\n                    propList.push(`\"${name}\"`);\r\n                }\r\n            }\r\n            this.staticDefs.push({\r\n                id,\r\n                expr: `app.createComponent(${ast.isDynamic ? null : expr}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, [${propList}])`,\r\n            });\r\n            if (ast.isDynamic) {\r\n                // If the component class changes, this can cause delayed renders to go\r\n                // through if the key doesn't change. Use the component name for now.\r\n                // This means that two component classes with the same name isn't supported\r\n                // in t-component. We can generate a unique id per class later if needed.\r\n                keyArg = `(${expr}).name + ${keyArg}`;\r\n            }\r\n            let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`;\r\n            if (ast.isDynamic) {\r\n                blockExpr = `toggler(${expr}, ${blockExpr})`;\r\n            }\r\n            // event handling\r\n            if (ast.on) {\r\n                blockExpr = this.wrapWithEventCatcher(blockExpr, ast.on);\r\n            }\r\n            block = this.createBlock(block, \"multi\", ctx);\r\n            this.insertBlock(blockExpr, block, ctx);\r\n            return block.varName;\r\n        }\r\n        wrapWithEventCatcher(expr, on) {\r\n            this.helpers.add(\"createCatcher\");\r\n            let name = generateId(\"catcher\");\r\n            let spec = {};\r\n            let handlers = [];\r\n            for (let ev in on) {\r\n                let handlerId = generateId(\"hdlr\");\r\n                let idx = handlers.push(handlerId) - 1;\r\n                spec[ev] = idx;\r\n                const handler = this.generateHandlerCode(ev, on[ev]);\r\n                this.define(handlerId, handler);\r\n            }\r\n            this.staticDefs.push({ id: name, expr: `createCatcher(${JSON.stringify(spec)})` });\r\n            return `${name}(${expr}, [${handlers.join(\",\")}])`;\r\n        }\r\n        compileTSlot(ast, ctx) {\r\n            this.helpers.add(\"callSlot\");\r\n            let { block } = ctx;\r\n            let blockString;\r\n            let slotName;\r\n            let dynamic = false;\r\n            let isMultiple = false;\r\n            if (ast.name.match(INTERP_REGEXP)) {\r\n                dynamic = true;\r\n                isMultiple = true;\r\n                slotName = interpolate(ast.name);\r\n            }\r\n            else {\r\n                slotName = \"'\" + ast.name + \"'\";\r\n                isMultiple = isMultiple || this.slotNames.has(ast.name);\r\n                this.slotNames.add(ast.name);\r\n            }\r\n            const dynProps = ast.attrs ? ast.attrs[\"t-props\"] : null;\r\n            if (ast.attrs) {\r\n                delete ast.attrs[\"t-props\"];\r\n            }\r\n            let key = this.target.loopLevel ? `key${this.target.loopLevel}` : \"key\";\r\n            if (isMultiple) {\r\n                key = this.generateComponentKey(key);\r\n            }\r\n            const props = ast.attrs ? this.formatPropObject(ast.attrs) : [];\r\n            const scope = this.getPropString(props, dynProps);\r\n            if (ast.defaultContent) {\r\n                const name = this.compileInNewTarget(\"defaultContent\", ast.defaultContent, ctx);\r\n                blockString = `callSlot(ctx, node, ${key}, ${slotName}, ${dynamic}, ${scope}, ${name}.bind(this))`;\r\n            }\r\n            else {\r\n                if (dynamic) {\r\n                    let name = generateId(\"slot\");\r\n                    this.define(name, slotName);\r\n                    blockString = `toggler(${name}, callSlot(ctx, node, ${key}, ${name}, ${dynamic}, ${scope}))`;\r\n                }\r\n                else {\r\n                    blockString = `callSlot(ctx, node, ${key}, ${slotName}, ${dynamic}, ${scope})`;\r\n                }\r\n            }\r\n            // event handling\r\n            if (ast.on) {\r\n                blockString = this.wrapWithEventCatcher(blockString, ast.on);\r\n            }\r\n            if (block) {\r\n                this.insertAnchor(block);\r\n            }\r\n            block = this.createBlock(block, \"multi\", ctx);\r\n            this.insertBlock(blockString, block, { ...ctx, forceNewBlock: false });\r\n            return block.varName;\r\n        }\r\n        compileTTranslation(ast, ctx) {\r\n            if (ast.content) {\r\n                return this.compileAST(ast.content, Object.assign({}, ctx, { translate: false }));\r\n            }\r\n            return null;\r\n        }\r\n        compileTPortal(ast, ctx) {\r\n            if (!this.staticDefs.find((d) => d.id === \"Portal\")) {\r\n                this.staticDefs.push({ id: \"Portal\", expr: `app.Portal` });\r\n            }\r\n            let { block } = ctx;\r\n            const name = this.compileInNewTarget(\"slot\", ast.content, ctx);\r\n            let ctxStr = \"ctx\";\r\n            if (this.target.loopLevel || !this.hasSafeContext) {\r\n                ctxStr = generateId(\"ctx\");\r\n                this.helpers.add(\"capture\");\r\n                this.define(ctxStr, `capture(ctx)`);\r\n            }\r\n            let id = generateId(\"comp\");\r\n            this.staticDefs.push({\r\n                id,\r\n                expr: `app.createComponent(null, false, true, false, false)`,\r\n            });\r\n            const target = compileExpr(ast.target);\r\n            const key = this.generateComponentKey();\r\n            const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ${ctxStr}}}}, ${key}, node, ctx, Portal)`;\r\n            if (block) {\r\n                this.insertAnchor(block);\r\n            }\r\n            block = this.createBlock(block, \"multi\", ctx);\r\n            this.insertBlock(blockString, block, { ...ctx, forceNewBlock: false });\r\n            return block.varName;\r\n        }\r\n    }\r\n\r\n    // -----------------------------------------------------------------------------\r\n    // Parser\r\n    // -----------------------------------------------------------------------------\r\n    const cache = new WeakMap();\r\n    function parse(xml, customDir) {\r\n        const ctx = {\r\n            inPreTag: false,\r\n            customDirectives: customDir,\r\n        };\r\n        if (typeof xml === \"string\") {\r\n            const elem = parseXML(`<t>${xml}</t>`).firstChild;\r\n            return _parse(elem, ctx);\r\n        }\r\n        let ast = cache.get(xml);\r\n        if (!ast) {\r\n            // we clone here the xml to prevent modifying it in place\r\n            ast = _parse(xml.cloneNode(true), ctx);\r\n            cache.set(xml, ast);\r\n        }\r\n        return ast;\r\n    }\r\n    function _parse(xml, ctx) {\r\n        normalizeXML(xml);\r\n        return parseNode(xml, ctx) || { type: 0 /* Text */, value: \"\" };\r\n    }\r\n    function parseNode(node, ctx) {\r\n        if (!(node instanceof Element)) {\r\n            return parseTextCommentNode(node, ctx);\r\n        }\r\n        return (parseTCustom(node, ctx) ||\r\n            parseTDebugLog(node, ctx) ||\r\n            parseTForEach(node, ctx) ||\r\n            parseTIf(node, ctx) ||\r\n            parseTPortal(node, ctx) ||\r\n            parseTCall(node, ctx) ||\r\n            parseTCallBlock(node) ||\r\n            parseTEscNode(node, ctx) ||\r\n            parseTOutNode(node, ctx) ||\r\n            parseTKey(node, ctx) ||\r\n            parseTTranslation(node, ctx) ||\r\n            parseTSlot(node, ctx) ||\r\n            parseComponent(node, ctx) ||\r\n            parseDOMNode(node, ctx) ||\r\n            parseTSetNode(node, ctx) ||\r\n            parseTNode(node, ctx));\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // <t /> tag\r\n    // -----------------------------------------------------------------------------\r\n    function parseTNode(node, ctx) {\r\n        if (node.tagName !== \"t\") {\r\n            return null;\r\n        }\r\n        return parseChildNodes(node, ctx);\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Text and Comment Nodes\r\n    // -----------------------------------------------------------------------------\r\n    const lineBreakRE = /[\\r\\n]/;\r\n    function parseTextCommentNode(node, ctx) {\r\n        if (node.nodeType === Node.TEXT_NODE) {\r\n            let value = node.textContent || \"\";\r\n            if (!ctx.inPreTag && lineBreakRE.test(value) && !value.trim()) {\r\n                return null;\r\n            }\r\n            return { type: 0 /* Text */, value };\r\n        }\r\n        else if (node.nodeType === Node.COMMENT_NODE) {\r\n            return { type: 1 /* Comment */, value: node.textContent || \"\" };\r\n        }\r\n        return null;\r\n    }\r\n    function parseTCustom(node, ctx) {\r\n        if (!ctx.customDirectives) {\r\n            return null;\r\n        }\r\n        const nodeAttrsNames = node.getAttributeNames();\r\n        for (let attr of nodeAttrsNames) {\r\n            if (attr === \"t-custom\" || attr === \"t-custom-\") {\r\n                throw new OwlError(\"Missing custom directive name with t-custom directive\");\r\n            }\r\n            if (attr.startsWith(\"t-custom-\")) {\r\n                const directiveName = attr.split(\".\")[0].slice(9);\r\n                const customDirective = ctx.customDirectives[directiveName];\r\n                if (!customDirective) {\r\n                    throw new OwlError(`Custom directive \"${directiveName}\" is not defined`);\r\n                }\r\n                const value = node.getAttribute(attr);\r\n                const modifiers = attr.split(\".\").slice(1);\r\n                node.removeAttribute(attr);\r\n                try {\r\n                    customDirective(node, value, modifiers);\r\n                }\r\n                catch (error) {\r\n                    throw new OwlError(`Custom directive \"${directiveName}\" throw the following error: ${error}`);\r\n                }\r\n                return parseNode(node, ctx);\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // debugging\r\n    // -----------------------------------------------------------------------------\r\n    function parseTDebugLog(node, ctx) {\r\n        if (node.hasAttribute(\"t-debug\")) {\r\n            node.removeAttribute(\"t-debug\");\r\n            return {\r\n                type: 12 /* TDebug */,\r\n                content: parseNode(node, ctx),\r\n            };\r\n        }\r\n        if (node.hasAttribute(\"t-log\")) {\r\n            const expr = node.getAttribute(\"t-log\");\r\n            node.removeAttribute(\"t-log\");\r\n            return {\r\n                type: 13 /* TLog */,\r\n                expr,\r\n                content: parseNode(node, ctx),\r\n            };\r\n        }\r\n        return null;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Regular dom node\r\n    // -----------------------------------------------------------------------------\r\n    const hasDotAtTheEnd = /\\.[\\w_]+\\s*$/;\r\n    const hasBracketsAtTheEnd = /\\[[^\\[]+\\]\\s*$/;\r\n    const ROOT_SVG_TAGS = new Set([\"svg\", \"g\", \"path\"]);\r\n    function parseDOMNode(node, ctx) {\r\n        const { tagName } = node;\r\n        const dynamicTag = node.getAttribute(\"t-tag\");\r\n        node.removeAttribute(\"t-tag\");\r\n        if (tagName === \"t\" && !dynamicTag) {\r\n            return null;\r\n        }\r\n        if (tagName.startsWith(\"block-\")) {\r\n            throw new OwlError(`Invalid tag name: '${tagName}'`);\r\n        }\r\n        ctx = Object.assign({}, ctx);\r\n        if (tagName === \"pre\") {\r\n            ctx.inPreTag = true;\r\n        }\r\n        let ns = !ctx.nameSpace && ROOT_SVG_TAGS.has(tagName) ? \"http://www.w3.org/2000/svg\" : null;\r\n        const ref = node.getAttribute(\"t-ref\");\r\n        node.removeAttribute(\"t-ref\");\r\n        const nodeAttrsNames = node.getAttributeNames();\r\n        let attrs = null;\r\n        let on = null;\r\n        let model = null;\r\n        for (let attr of nodeAttrsNames) {\r\n            const value = node.getAttribute(attr);\r\n            if (attr === \"t-on\" || attr === \"t-on-\") {\r\n                throw new OwlError(\"Missing event name with t-on directive\");\r\n            }\r\n            if (attr.startsWith(\"t-on-\")) {\r\n                on = on || {};\r\n                on[attr.slice(5)] = value;\r\n            }\r\n            else if (attr.startsWith(\"t-model\")) {\r\n                if (![\"input\", \"select\", \"textarea\"].includes(tagName)) {\r\n                    throw new OwlError(\"The t-model directive only works with <input>, <textarea> and <select>\");\r\n                }\r\n                let baseExpr, expr;\r\n                if (hasDotAtTheEnd.test(value)) {\r\n                    const index = value.lastIndexOf(\".\");\r\n                    baseExpr = value.slice(0, index);\r\n                    expr = `'${value.slice(index + 1)}'`;\r\n                }\r\n                else if (hasBracketsAtTheEnd.test(value)) {\r\n                    const index = value.lastIndexOf(\"[\");\r\n                    baseExpr = value.slice(0, index);\r\n                    expr = value.slice(index + 1, -1);\r\n                }\r\n                else {\r\n                    throw new OwlError(`Invalid t-model expression: \"${value}\" (it should be assignable)`);\r\n                }\r\n                const typeAttr = node.getAttribute(\"type\");\r\n                const isInput = tagName === \"input\";\r\n                const isSelect = tagName === \"select\";\r\n                const isCheckboxInput = isInput && typeAttr === \"checkbox\";\r\n                const isRadioInput = isInput && typeAttr === \"radio\";\r\n                const hasTrimMod = attr.includes(\".trim\");\r\n                const hasLazyMod = hasTrimMod || attr.includes(\".lazy\");\r\n                const hasNumberMod = attr.includes(\".number\");\r\n                const eventType = isRadioInput ? \"click\" : isSelect || hasLazyMod ? \"change\" : \"input\";\r\n                model = {\r\n                    baseExpr,\r\n                    expr,\r\n                    targetAttr: isCheckboxInput ? \"checked\" : \"value\",\r\n                    specialInitTargetAttr: isRadioInput ? \"checked\" : null,\r\n                    eventType,\r\n                    hasDynamicChildren: false,\r\n                    shouldTrim: hasTrimMod,\r\n                    shouldNumberize: hasNumberMod,\r\n                };\r\n                if (isSelect) {\r\n                    // don't pollute the original ctx\r\n                    ctx = Object.assign({}, ctx);\r\n                    ctx.tModelInfo = model;\r\n                }\r\n            }\r\n            else if (attr.startsWith(\"block-\")) {\r\n                throw new OwlError(`Invalid attribute: '${attr}'`);\r\n            }\r\n            else if (attr === \"xmlns\") {\r\n                ns = value;\r\n            }\r\n            else if (attr !== \"t-name\") {\r\n                if (attr.startsWith(\"t-\") && !attr.startsWith(\"t-att\")) {\r\n                    throw new OwlError(`Unknown QWeb directive: '${attr}'`);\r\n                }\r\n                const tModel = ctx.tModelInfo;\r\n                if (tModel && [\"t-att-value\", \"t-attf-value\"].includes(attr)) {\r\n                    tModel.hasDynamicChildren = true;\r\n                }\r\n                attrs = attrs || {};\r\n                attrs[attr] = value;\r\n            }\r\n        }\r\n        if (ns) {\r\n            ctx.nameSpace = ns;\r\n        }\r\n        const children = parseChildren(node, ctx);\r\n        return {\r\n            type: 2 /* DomNode */,\r\n            tag: tagName,\r\n            dynamicTag,\r\n            attrs,\r\n            on,\r\n            ref,\r\n            content: children,\r\n            model,\r\n            ns,\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-esc\r\n    // -----------------------------------------------------------------------------\r\n    function parseTEscNode(node, ctx) {\r\n        if (!node.hasAttribute(\"t-esc\")) {\r\n            return null;\r\n        }\r\n        const escValue = node.getAttribute(\"t-esc\");\r\n        node.removeAttribute(\"t-esc\");\r\n        const tesc = {\r\n            type: 4 /* TEsc */,\r\n            expr: escValue,\r\n            defaultValue: node.textContent || \"\",\r\n        };\r\n        let ref = node.getAttribute(\"t-ref\");\r\n        node.removeAttribute(\"t-ref\");\r\n        const ast = parseNode(node, ctx);\r\n        if (!ast) {\r\n            return tesc;\r\n        }\r\n        if (ast.type === 2 /* DomNode */) {\r\n            return {\r\n                ...ast,\r\n                ref,\r\n                content: [tesc],\r\n            };\r\n        }\r\n        return tesc;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-out\r\n    // -----------------------------------------------------------------------------\r\n    function parseTOutNode(node, ctx) {\r\n        if (!node.hasAttribute(\"t-out\") && !node.hasAttribute(\"t-raw\")) {\r\n            return null;\r\n        }\r\n        if (node.hasAttribute(\"t-raw\")) {\r\n            console.warn(`t-raw has been deprecated in favor of t-out. If the value to render is not wrapped by the \"markup\" function, it will be escaped`);\r\n        }\r\n        const expr = (node.getAttribute(\"t-out\") || node.getAttribute(\"t-raw\"));\r\n        node.removeAttribute(\"t-out\");\r\n        node.removeAttribute(\"t-raw\");\r\n        const tOut = { type: 8 /* TOut */, expr, body: null };\r\n        const ref = node.getAttribute(\"t-ref\");\r\n        node.removeAttribute(\"t-ref\");\r\n        const ast = parseNode(node, ctx);\r\n        if (!ast) {\r\n            return tOut;\r\n        }\r\n        if (ast.type === 2 /* DomNode */) {\r\n            tOut.body = ast.content.length ? ast.content : null;\r\n            return {\r\n                ...ast,\r\n                ref,\r\n                content: [tOut],\r\n            };\r\n        }\r\n        return tOut;\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-foreach and t-key\r\n    // -----------------------------------------------------------------------------\r\n    function parseTForEach(node, ctx) {\r\n        if (!node.hasAttribute(\"t-foreach\")) {\r\n            return null;\r\n        }\r\n        const html = node.outerHTML;\r\n        const collection = node.getAttribute(\"t-foreach\");\r\n        node.removeAttribute(\"t-foreach\");\r\n        const elem = node.getAttribute(\"t-as\") || \"\";\r\n        node.removeAttribute(\"t-as\");\r\n        const key = node.getAttribute(\"t-key\");\r\n        if (!key) {\r\n            throw new OwlError(`\"Directive t-foreach should always be used with a t-key!\" (expression: t-foreach=\"${collection}\" t-as=\"${elem}\")`);\r\n        }\r\n        node.removeAttribute(\"t-key\");\r\n        const memo = node.getAttribute(\"t-memo\") || \"\";\r\n        node.removeAttribute(\"t-memo\");\r\n        const body = parseNode(node, ctx);\r\n        if (!body) {\r\n            return null;\r\n        }\r\n        const hasNoTCall = !html.includes(\"t-call\");\r\n        const hasNoFirst = hasNoTCall && !html.includes(`${elem}_first`);\r\n        const hasNoLast = hasNoTCall && !html.includes(`${elem}_last`);\r\n        const hasNoIndex = hasNoTCall && !html.includes(`${elem}_index`);\r\n        const hasNoValue = hasNoTCall && !html.includes(`${elem}_value`);\r\n        return {\r\n            type: 9 /* TForEach */,\r\n            collection,\r\n            elem,\r\n            body,\r\n            memo,\r\n            key,\r\n            hasNoFirst,\r\n            hasNoLast,\r\n            hasNoIndex,\r\n            hasNoValue,\r\n        };\r\n    }\r\n    function parseTKey(node, ctx) {\r\n        if (!node.hasAttribute(\"t-key\")) {\r\n            return null;\r\n        }\r\n        const key = node.getAttribute(\"t-key\");\r\n        node.removeAttribute(\"t-key\");\r\n        const body = parseNode(node, ctx);\r\n        if (!body) {\r\n            return null;\r\n        }\r\n        return { type: 10 /* TKey */, expr: key, content: body };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-call\r\n    // -----------------------------------------------------------------------------\r\n    function parseTCall(node, ctx) {\r\n        if (!node.hasAttribute(\"t-call\")) {\r\n            return null;\r\n        }\r\n        const subTemplate = node.getAttribute(\"t-call\");\r\n        const context = node.getAttribute(\"t-call-context\");\r\n        node.removeAttribute(\"t-call\");\r\n        node.removeAttribute(\"t-call-context\");\r\n        if (node.tagName !== \"t\") {\r\n            const ast = parseNode(node, ctx);\r\n            const tcall = { type: 7 /* TCall */, name: subTemplate, body: null, context };\r\n            if (ast && ast.type === 2 /* DomNode */) {\r\n                ast.content = [tcall];\r\n                return ast;\r\n            }\r\n            if (ast && ast.type === 11 /* TComponent */) {\r\n                return {\r\n                    ...ast,\r\n                    slots: { default: { content: tcall, scope: null, on: null, attrs: null } },\r\n                };\r\n            }\r\n        }\r\n        const body = parseChildren(node, ctx);\r\n        return {\r\n            type: 7 /* TCall */,\r\n            name: subTemplate,\r\n            body: body.length ? body : null,\r\n            context,\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-call-block\r\n    // -----------------------------------------------------------------------------\r\n    function parseTCallBlock(node, ctx) {\r\n        if (!node.hasAttribute(\"t-call-block\")) {\r\n            return null;\r\n        }\r\n        const name = node.getAttribute(\"t-call-block\");\r\n        return {\r\n            type: 15 /* TCallBlock */,\r\n            name,\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-if\r\n    // -----------------------------------------------------------------------------\r\n    function parseTIf(node, ctx) {\r\n        if (!node.hasAttribute(\"t-if\")) {\r\n            return null;\r\n        }\r\n        const condition = node.getAttribute(\"t-if\");\r\n        node.removeAttribute(\"t-if\");\r\n        const content = parseNode(node, ctx) || { type: 0 /* Text */, value: \"\" };\r\n        let nextElement = node.nextElementSibling;\r\n        // t-elifs\r\n        const tElifs = [];\r\n        while (nextElement && nextElement.hasAttribute(\"t-elif\")) {\r\n            const condition = nextElement.getAttribute(\"t-elif\");\r\n            nextElement.removeAttribute(\"t-elif\");\r\n            const tElif = parseNode(nextElement, ctx);\r\n            const next = nextElement.nextElementSibling;\r\n            nextElement.remove();\r\n            nextElement = next;\r\n            if (tElif) {\r\n                tElifs.push({ condition, content: tElif });\r\n            }\r\n        }\r\n        // t-else\r\n        let tElse = null;\r\n        if (nextElement && nextElement.hasAttribute(\"t-else\")) {\r\n            nextElement.removeAttribute(\"t-else\");\r\n            tElse = parseNode(nextElement, ctx);\r\n            nextElement.remove();\r\n        }\r\n        return {\r\n            type: 5 /* TIf */,\r\n            condition,\r\n            content,\r\n            tElif: tElifs.length ? tElifs : null,\r\n            tElse,\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // t-set directive\r\n    // -----------------------------------------------------------------------------\r\n    function parseTSetNode(node, ctx) {\r\n        if (!node.hasAttribute(\"t-set\")) {\r\n            return null;\r\n        }\r\n        const name = node.getAttribute(\"t-set\");\r\n        const value = node.getAttribute(\"t-value\") || null;\r\n        const defaultValue = node.innerHTML === node.textContent ? node.textContent || null : null;\r\n        let body = null;\r\n        if (node.textContent !== node.innerHTML) {\r\n            body = parseChildren(node, ctx);\r\n        }\r\n        return { type: 6 /* TSet */, name, value, defaultValue, body };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Components\r\n    // -----------------------------------------------------------------------------\r\n    // Error messages when trying to use an unsupported directive on a component\r\n    const directiveErrorMap = new Map([\r\n        [\r\n            \"t-ref\",\r\n            \"t-ref is no longer supported on components. Consider exposing only the public part of the component's API through a callback prop.\",\r\n        ],\r\n        [\"t-att\", \"t-att makes no sense on component: props are already treated as expressions\"],\r\n        [\r\n            \"t-attf\",\r\n            \"t-attf is not supported on components: use template strings for string interpolation in props\",\r\n        ],\r\n    ]);\r\n    function parseComponent(node, ctx) {\r\n        let name = node.tagName;\r\n        const firstLetter = name[0];\r\n        let isDynamic = node.hasAttribute(\"t-component\");\r\n        if (isDynamic && name !== \"t\") {\r\n            throw new OwlError(`Directive 't-component' can only be used on <t> nodes (used on a <${name}>)`);\r\n        }\r\n        if (!(firstLetter === firstLetter.toUpperCase() || isDynamic)) {\r\n            return null;\r\n        }\r\n        if (isDynamic) {\r\n            name = node.getAttribute(\"t-component\");\r\n            node.removeAttribute(\"t-component\");\r\n        }\r\n        const dynamicProps = node.getAttribute(\"t-props\");\r\n        node.removeAttribute(\"t-props\");\r\n        const defaultSlotScope = node.getAttribute(\"t-slot-scope\");\r\n        node.removeAttribute(\"t-slot-scope\");\r\n        let on = null;\r\n        let props = null;\r\n        for (let name of node.getAttributeNames()) {\r\n            const value = node.getAttribute(name);\r\n            if (name.startsWith(\"t-\")) {\r\n                if (name.startsWith(\"t-on-\")) {\r\n                    on = on || {};\r\n                    on[name.slice(5)] = value;\r\n                }\r\n                else {\r\n                    const message = directiveErrorMap.get(name.split(\"-\").slice(0, 2).join(\"-\"));\r\n                    throw new OwlError(message || `unsupported directive on Component: ${name}`);\r\n                }\r\n            }\r\n            else {\r\n                props = props || {};\r\n                props[name] = value;\r\n            }\r\n        }\r\n        let slots = null;\r\n        if (node.hasChildNodes()) {\r\n            const clone = node.cloneNode(true);\r\n            // named slots\r\n            const slotNodes = Array.from(clone.querySelectorAll(\"[t-set-slot]\"));\r\n            for (let slotNode of slotNodes) {\r\n                if (slotNode.tagName !== \"t\") {\r\n                    throw new OwlError(`Directive 't-set-slot' can only be used on <t> nodes (used on a <${slotNode.tagName}>)`);\r\n                }\r\n                const name = slotNode.getAttribute(\"t-set-slot\");\r\n                // check if this is defined in a sub component (in which case it should\r\n                // be ignored)\r\n                let el = slotNode.parentElement;\r\n                let isInSubComponent = false;\r\n                while (el && el !== clone) {\r\n                    if (el.hasAttribute(\"t-component\") || el.tagName[0] === el.tagName[0].toUpperCase()) {\r\n                        isInSubComponent = true;\r\n                        break;\r\n                    }\r\n                    el = el.parentElement;\r\n                }\r\n                if (isInSubComponent || !el) {\r\n                    continue;\r\n                }\r\n                slotNode.removeAttribute(\"t-set-slot\");\r\n                slotNode.remove();\r\n                const slotAst = parseNode(slotNode, ctx);\r\n                let on = null;\r\n                let attrs = null;\r\n                let scope = null;\r\n                for (let attributeName of slotNode.getAttributeNames()) {\r\n                    const value = slotNode.getAttribute(attributeName);\r\n                    if (attributeName === \"t-slot-scope\") {\r\n                        scope = value;\r\n                        continue;\r\n                    }\r\n                    else if (attributeName.startsWith(\"t-on-\")) {\r\n                        on = on || {};\r\n                        on[attributeName.slice(5)] = value;\r\n                    }\r\n                    else {\r\n                        attrs = attrs || {};\r\n                        attrs[attributeName] = value;\r\n                    }\r\n                }\r\n                slots = slots || {};\r\n                slots[name] = { content: slotAst, on, attrs, scope };\r\n            }\r\n            // default slot\r\n            const defaultContent = parseChildNodes(clone, ctx);\r\n            slots = slots || {};\r\n            // t-set-slot=\"default\" has priority over content\r\n            if (defaultContent && !slots.default) {\r\n                slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope };\r\n            }\r\n        }\r\n        return { type: 11 /* TComponent */, name, isDynamic, dynamicProps, props, slots, on };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Slots\r\n    // -----------------------------------------------------------------------------\r\n    function parseTSlot(node, ctx) {\r\n        if (!node.hasAttribute(\"t-slot\")) {\r\n            return null;\r\n        }\r\n        const name = node.getAttribute(\"t-slot\");\r\n        node.removeAttribute(\"t-slot\");\r\n        let attrs = null;\r\n        let on = null;\r\n        for (let attributeName of node.getAttributeNames()) {\r\n            const value = node.getAttribute(attributeName);\r\n            if (attributeName.startsWith(\"t-on-\")) {\r\n                on = on || {};\r\n                on[attributeName.slice(5)] = value;\r\n            }\r\n            else {\r\n                attrs = attrs || {};\r\n                attrs[attributeName] = value;\r\n            }\r\n        }\r\n        return {\r\n            type: 14 /* TSlot */,\r\n            name,\r\n            attrs,\r\n            on,\r\n            defaultContent: parseChildNodes(node, ctx),\r\n        };\r\n    }\r\n    function parseTTranslation(node, ctx) {\r\n        if (node.getAttribute(\"t-translation\") !== \"off\") {\r\n            return null;\r\n        }\r\n        node.removeAttribute(\"t-translation\");\r\n        return {\r\n            type: 16 /* TTranslation */,\r\n            content: parseNode(node, ctx),\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // Portal\r\n    // -----------------------------------------------------------------------------\r\n    function parseTPortal(node, ctx) {\r\n        if (!node.hasAttribute(\"t-portal\")) {\r\n            return null;\r\n        }\r\n        const target = node.getAttribute(\"t-portal\");\r\n        node.removeAttribute(\"t-portal\");\r\n        const content = parseNode(node, ctx);\r\n        if (!content) {\r\n            return {\r\n                type: 0 /* Text */,\r\n                value: \"\",\r\n            };\r\n        }\r\n        return {\r\n            type: 17 /* TPortal */,\r\n            target,\r\n            content,\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // helpers\r\n    // -----------------------------------------------------------------------------\r\n    /**\r\n     * Parse all the child nodes of a given node and return a list of ast elements\r\n     */\r\n    function parseChildren(node, ctx) {\r\n        const children = [];\r\n        for (let child of node.childNodes) {\r\n            const childAst = parseNode(child, ctx);\r\n            if (childAst) {\r\n                if (childAst.type === 3 /* Multi */) {\r\n                    children.push(...childAst.content);\r\n                }\r\n                else {\r\n                    children.push(childAst);\r\n                }\r\n            }\r\n        }\r\n        return children;\r\n    }\r\n    /**\r\n     * Parse all the child nodes of a given node and return an ast if possible.\r\n     * In the case there are multiple children, they are wrapped in a astmulti.\r\n     */\r\n    function parseChildNodes(node, ctx) {\r\n        const children = parseChildren(node, ctx);\r\n        switch (children.length) {\r\n            case 0:\r\n                return null;\r\n            case 1:\r\n                return children[0];\r\n            default:\r\n                return { type: 3 /* Multi */, content: children };\r\n        }\r\n    }\r\n    /**\r\n     * Normalizes the content of an Element so that t-if/t-elif/t-else directives\r\n     * immediately follow one another (by removing empty text nodes or comments).\r\n     * Throws an error when a conditional branching statement is malformed. This\r\n     * function modifies the Element in place.\r\n     *\r\n     * @param el the element containing the tree that should be normalized\r\n     */\r\n    function normalizeTIf(el) {\r\n        let tbranch = el.querySelectorAll(\"[t-elif], [t-else]\");\r\n        for (let i = 0, ilen = tbranch.length; i < ilen; i++) {\r\n            let node = tbranch[i];\r\n            let prevElem = node.previousElementSibling;\r\n            let pattr = (name) => prevElem.getAttribute(name);\r\n            let nattr = (name) => +!!node.getAttribute(name);\r\n            if (prevElem && (pattr(\"t-if\") || pattr(\"t-elif\"))) {\r\n                if (pattr(\"t-foreach\")) {\r\n                    throw new OwlError(\"t-if cannot stay at the same level as t-foreach when using t-elif or t-else\");\r\n                }\r\n                if ([\"t-if\", \"t-elif\", \"t-else\"].map(nattr).reduce(function (a, b) {\r\n                    return a + b;\r\n                }) > 1) {\r\n                    throw new OwlError(\"Only one conditional branching directive is allowed per node\");\r\n                }\r\n                // All text (with only spaces) and comment nodes (nodeType 8) between\r\n                // branch nodes are removed\r\n                let textNode;\r\n                while ((textNode = node.previousSibling) !== prevElem) {\r\n                    if (textNode.nodeValue.trim().length && textNode.nodeType !== 8) {\r\n                        throw new OwlError(\"text is not allowed between branching directives\");\r\n                    }\r\n                    textNode.remove();\r\n                }\r\n            }\r\n            else {\r\n                throw new OwlError(\"t-elif and t-else directives must be preceded by a t-if or t-elif directive\");\r\n            }\r\n        }\r\n    }\r\n    /**\r\n     * Normalizes the content of an Element so that t-esc directives on components\r\n     * are removed and instead places a <t t-esc=\"\"> as the default slot of the\r\n     * component. Also throws if the component already has content. This function\r\n     * modifies the Element in place.\r\n     *\r\n     * @param el the element containing the tree that should be normalized\r\n     */\r\n    function normalizeTEscTOut(el) {\r\n        for (const d of [\"t-esc\", \"t-out\"]) {\r\n            const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute(\"t-component\"));\r\n            for (const el of elements) {\r\n                if (el.childNodes.length) {\r\n                    throw new OwlError(`Cannot have ${d} on a component that already has content`);\r\n                }\r\n                const value = el.getAttribute(d);\r\n                el.removeAttribute(d);\r\n                const t = el.ownerDocument.createElement(\"t\");\r\n                if (value != null) {\r\n                    t.setAttribute(d, value);\r\n                }\r\n                el.appendChild(t);\r\n            }\r\n        }\r\n    }\r\n    /**\r\n     * Normalizes the tree inside a given element and do some preliminary validation\r\n     * on it. This function modifies the Element in place.\r\n     *\r\n     * @param el the element containing the tree that should be normalized\r\n     */\r\n    function normalizeXML(el) {\r\n        normalizeTIf(el);\r\n        normalizeTEscTOut(el);\r\n    }\r\n\r\n    function compile(template, options = {\r\n        hasGlobalValues: false,\r\n    }) {\r\n        // parsing\r\n        const ast = parse(template, options.customDirectives);\r\n        // some work\r\n        const hasSafeContext = template instanceof Node\r\n            ? !(template instanceof Element) || template.querySelector(\"[t-set], [t-call]\") === null\r\n            : !template.includes(\"t-set\") && !template.includes(\"t-call\");\r\n        // code generation\r\n        const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });\r\n        const code = codeGenerator.generateCode();\r\n        // template function\r\n        try {\r\n            return new Function(\"app, bdom, helpers\", code);\r\n        }\r\n        catch (originalError) {\r\n            const { name } = options;\r\n            const nameStr = name ? `template \"${name}\"` : \"anonymous template\";\r\n            const err = new OwlError(`Failed to compile ${nameStr}: ${originalError.message}\\n\\ngenerated code:\\nfunction(app, bdom, helpers) {\\n${code}\\n}`);\r\n            err.cause = originalError;\r\n            throw err;\r\n        }\r\n    }\r\n\r\n    // do not modify manually. This file is generated by the release script.\r\n    const version = \"2.5.3\";\r\n\r\n    // -----------------------------------------------------------------------------\r\n    //  Scheduler\r\n    // -----------------------------------------------------------------------------\r\n    class Scheduler {\r\n        constructor() {\r\n            this.tasks = new Set();\r\n            this.frame = 0;\r\n            this.delayedRenders = [];\r\n            this.cancelledNodes = new Set();\r\n            this.processing = false;\r\n            this.requestAnimationFrame = Scheduler.requestAnimationFrame;\r\n        }\r\n        addFiber(fiber) {\r\n            this.tasks.add(fiber.root);\r\n        }\r\n        scheduleDestroy(node) {\r\n            this.cancelledNodes.add(node);\r\n            if (this.frame === 0) {\r\n                this.frame = this.requestAnimationFrame(() => this.processTasks());\r\n            }\r\n        }\r\n        /**\r\n         * Process all current tasks. This only applies to the fibers that are ready.\r\n         * Other tasks are left unchanged.\r\n         */\r\n        flush() {\r\n            if (this.delayedRenders.length) {\r\n                let renders = this.delayedRenders;\r\n                this.delayedRenders = [];\r\n                for (let f of renders) {\r\n                    if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {\r\n                        f.render();\r\n                    }\r\n                }\r\n            }\r\n            if (this.frame === 0) {\r\n                this.frame = this.requestAnimationFrame(() => this.processTasks());\r\n            }\r\n        }\r\n        processTasks() {\r\n            if (this.processing) {\r\n                return;\r\n            }\r\n            this.processing = true;\r\n            this.frame = 0;\r\n            for (let node of this.cancelledNodes) {\r\n                node._destroy();\r\n            }\r\n            this.cancelledNodes.clear();\r\n            for (let task of this.tasks) {\r\n                this.processFiber(task);\r\n            }\r\n            for (let task of this.tasks) {\r\n                if (task.node.status === 3 /* DESTROYED */) {\r\n                    this.tasks.delete(task);\r\n                }\r\n            }\r\n            this.processing = false;\r\n        }\r\n        processFiber(fiber) {\r\n            if (fiber.root !== fiber) {\r\n                this.tasks.delete(fiber);\r\n                return;\r\n            }\r\n            const hasError = fibersInError.has(fiber);\r\n            if (hasError && fiber.counter !== 0) {\r\n                this.tasks.delete(fiber);\r\n                return;\r\n            }\r\n            if (fiber.node.status === 3 /* DESTROYED */) {\r\n                this.tasks.delete(fiber);\r\n                return;\r\n            }\r\n            if (fiber.counter === 0) {\r\n                if (!hasError) {\r\n                    fiber.complete();\r\n                }\r\n                // at this point, the fiber should have been applied to the DOM, so we can\r\n                // remove it from the task list. If it is not the case, it means that there\r\n                // was an error and an error handler triggered a new rendering that recycled\r\n                // the fiber, so in that case, we actually want to keep the fiber around,\r\n                // otherwise it will just be ignored.\r\n                if (fiber.appliedToDom) {\r\n                    this.tasks.delete(fiber);\r\n                }\r\n            }\r\n        }\r\n    }\r\n    // capture the value of requestAnimationFrame as soon as possible, to avoid\r\n    // interactions with other code, such as test frameworks that override them\r\n    Scheduler.requestAnimationFrame = window.requestAnimationFrame.bind(window);\r\n\r\n    let hasBeenLogged = false;\r\n    const DEV_MSG = () => {\r\n        const hash = window.owl ? window.owl.__info__.hash : \"master\";\r\n        return `Owl is running in 'dev' mode.\r\n\r\nThis is not suitable for production use.\r\nSee https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;\r\n    };\r\n    const apps = new Set();\r\n    window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = { apps, Fiber, RootFiber, toRaw, reactive });\r\n    class App extends TemplateSet {\r\n        constructor(Root, config = {}) {\r\n            super(config);\r\n            this.scheduler = new Scheduler();\r\n            this.subRoots = new Set();\r\n            this.root = null;\r\n            this.name = config.name || \"\";\r\n            this.Root = Root;\r\n            apps.add(this);\r\n            if (config.test) {\r\n                this.dev = true;\r\n            }\r\n            this.warnIfNoStaticProps = config.warnIfNoStaticProps || false;\r\n            if (this.dev && !config.test && !hasBeenLogged) {\r\n                console.info(DEV_MSG());\r\n                hasBeenLogged = true;\r\n            }\r\n            const env = config.env || {};\r\n            const descrs = Object.getOwnPropertyDescriptors(env);\r\n            this.env = Object.freeze(Object.create(Object.getPrototypeOf(env), descrs));\r\n            this.props = config.props || {};\r\n        }\r\n        mount(target, options) {\r\n            const root = this.createRoot(this.Root, { props: this.props });\r\n            this.root = root.node;\r\n            this.subRoots.delete(root.node);\r\n            return root.mount(target, options);\r\n        }\r\n        createRoot(Root, config = {}) {\r\n            const props = config.props || {};\r\n            // hack to make sure the sub root get the sub env if necessary. for owl 3,\r\n            // would be nice to rethink the initialization process to make sure that\r\n            // we can create a ComponentNode and give it explicitely the env, instead\r\n            // of looking it up in the app\r\n            const env = this.env;\r\n            if (config.env) {\r\n                this.env = config.env;\r\n            }\r\n            const restore = saveCurrent();\r\n            const node = this.makeNode(Root, props);\r\n            restore();\r\n            if (config.env) {\r\n                this.env = env;\r\n            }\r\n            this.subRoots.add(node);\r\n            return {\r\n                node,\r\n                mount: (target, options) => {\r\n                    App.validateTarget(target);\r\n                    if (this.dev) {\r\n                        validateProps(Root, props, { __owl__: { app: this } });\r\n                    }\r\n                    const prom = this.mountNode(node, target, options);\r\n                    return prom;\r\n                },\r\n                destroy: () => {\r\n                    this.subRoots.delete(node);\r\n                    node.destroy();\r\n                    this.scheduler.processTasks();\r\n                },\r\n            };\r\n        }\r\n        makeNode(Component, props) {\r\n            return new ComponentNode(Component, props, this, null, null);\r\n        }\r\n        mountNode(node, target, options) {\r\n            const promise = new Promise((resolve, reject) => {\r\n                let isResolved = false;\r\n                // manually set a onMounted callback.\r\n                // that way, we are independant from the current node.\r\n                node.mounted.push(() => {\r\n                    resolve(node.component);\r\n                    isResolved = true;\r\n                });\r\n                // Manually add the last resort error handler on the node\r\n                let handlers = nodeErrorHandlers.get(node);\r\n                if (!handlers) {\r\n                    handlers = [];\r\n                    nodeErrorHandlers.set(node, handlers);\r\n                }\r\n                handlers.unshift((e) => {\r\n                    if (!isResolved) {\r\n                        reject(e);\r\n                    }\r\n                    throw e;\r\n                });\r\n            });\r\n            node.mountComponent(target, options);\r\n            return promise;\r\n        }\r\n        destroy() {\r\n            if (this.root) {\r\n                for (let subroot of this.subRoots) {\r\n                    subroot.destroy();\r\n                }\r\n                this.root.destroy();\r\n                this.scheduler.processTasks();\r\n            }\r\n            apps.delete(this);\r\n        }\r\n        createComponent(name, isStatic, hasSlotsProp, hasDynamicPropList, propList) {\r\n            const isDynamic = !isStatic;\r\n            let arePropsDifferent;\r\n            const hasNoProp = propList.length === 0;\r\n            if (hasSlotsProp) {\r\n                arePropsDifferent = (_1, _2) => true;\r\n            }\r\n            else if (hasDynamicPropList) {\r\n                arePropsDifferent = function (props1, props2) {\r\n                    for (let k in props1) {\r\n                        if (props1[k] !== props2[k]) {\r\n                            return true;\r\n                        }\r\n                    }\r\n                    return Object.keys(props1).length !== Object.keys(props2).length;\r\n                };\r\n            }\r\n            else if (hasNoProp) {\r\n                arePropsDifferent = (_1, _2) => false;\r\n            }\r\n            else {\r\n                arePropsDifferent = function (props1, props2) {\r\n                    for (let p of propList) {\r\n                        if (props1[p] !== props2[p]) {\r\n                            return true;\r\n                        }\r\n                    }\r\n                    return false;\r\n                };\r\n            }\r\n            const updateAndRender = ComponentNode.prototype.updateAndRender;\r\n            const initiateRender = ComponentNode.prototype.initiateRender;\r\n            return (props, key, ctx, parent, C) => {\r\n                let children = ctx.children;\r\n                let node = children[key];\r\n                if (isDynamic && node && node.component.constructor !== C) {\r\n                    node = undefined;\r\n                }\r\n                const parentFiber = ctx.fiber;\r\n                if (node) {\r\n                    if (arePropsDifferent(node.props, props) || parentFiber.deep || node.forceNextRender) {\r\n                        node.forceNextRender = false;\r\n                        updateAndRender.call(node, props, parentFiber);\r\n                    }\r\n                }\r\n                else {\r\n                    // new component\r\n                    if (isStatic) {\r\n                        const components = parent.constructor.components;\r\n                        if (!components) {\r\n                            throw new OwlError(`Cannot find the definition of component \"${name}\", missing static components key in parent`);\r\n                        }\r\n                        C = components[name];\r\n                        if (!C) {\r\n                            throw new OwlError(`Cannot find the definition of component \"${name}\"`);\r\n                        }\r\n                        else if (!(C.prototype instanceof Component)) {\r\n                            throw new OwlError(`\"${name}\" is not a Component. It must inherit from the Component class`);\r\n                        }\r\n                    }\r\n                    node = new ComponentNode(C, props, this, ctx, key);\r\n                    children[key] = node;\r\n                    initiateRender.call(node, new Fiber(node, parentFiber));\r\n                }\r\n                parentFiber.childrenMap[key] = node;\r\n                return node;\r\n            };\r\n        }\r\n        handleError(...args) {\r\n            return handleError(...args);\r\n        }\r\n    }\r\n    App.validateTarget = validateTarget;\r\n    App.apps = apps;\r\n    App.version = version;\r\n    async function mount(C, target, config = {}) {\r\n        return new App(C, config).mount(target, config);\r\n    }\r\n\r\n    const mainEventHandler = (data, ev, currentTarget) => {\r\n        const { data: _data, modifiers } = filterOutModifiersFromData(data);\r\n        data = _data;\r\n        let stopped = false;\r\n        if (modifiers.length) {\r\n            let selfMode = false;\r\n            const isSelf = ev.target === currentTarget;\r\n            for (const mod of modifiers) {\r\n                switch (mod) {\r\n                    case \"self\":\r\n                        selfMode = true;\r\n                        if (isSelf) {\r\n                            continue;\r\n                        }\r\n                        else {\r\n                            return stopped;\r\n                        }\r\n                    case \"prevent\":\r\n                        if ((selfMode && isSelf) || !selfMode)\r\n                            ev.preventDefault();\r\n                        continue;\r\n                    case \"stop\":\r\n                        if ((selfMode && isSelf) || !selfMode)\r\n                            ev.stopPropagation();\r\n                        stopped = true;\r\n                        continue;\r\n                }\r\n            }\r\n        }\r\n        // If handler is empty, the array slot 0 will also be empty, and data will not have the property 0\r\n        // We check this rather than data[0] being truthy (or typeof function) so that it crashes\r\n        // as expected when there is a handler expression that evaluates to a falsy value\r\n        if (Object.hasOwnProperty.call(data, 0)) {\r\n            const handler = data[0];\r\n            if (typeof handler !== \"function\") {\r\n                throw new OwlError(`Invalid handler (expected a function, received: '${handler}')`);\r\n            }\r\n            let node = data[1] ? data[1].__owl__ : null;\r\n            if (node ? node.status === 1 /* MOUNTED */ : true) {\r\n                handler.call(node ? node.component : null, ev);\r\n            }\r\n        }\r\n        return stopped;\r\n    };\r\n\r\n    function status(component) {\r\n        switch (component.__owl__.status) {\r\n            case 0 /* NEW */:\r\n                return \"new\";\r\n            case 2 /* CANCELLED */:\r\n                return \"cancelled\";\r\n            case 1 /* MOUNTED */:\r\n                return \"mounted\";\r\n            case 3 /* DESTROYED */:\r\n                return \"destroyed\";\r\n        }\r\n    }\r\n\r\n    // -----------------------------------------------------------------------------\r\n    // useRef\r\n    // -----------------------------------------------------------------------------\r\n    /**\r\n     * The purpose of this hook is to allow components to get a reference to a sub\r\n     * html node or component.\r\n     */\r\n    function useRef(name) {\r\n        const node = getCurrent();\r\n        const refs = node.refs;\r\n        return {\r\n            get el() {\r\n                const el = refs[name];\r\n                return inOwnerDocument(el) ? el : null;\r\n            },\r\n        };\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // useEnv and useSubEnv\r\n    // -----------------------------------------------------------------------------\r\n    /**\r\n     * This hook is useful as a building block for some customized hooks, that may\r\n     * need a reference to the env of the component calling them.\r\n     */\r\n    function useEnv() {\r\n        return getCurrent().component.env;\r\n    }\r\n    function extendEnv(currentEnv, extension) {\r\n        const env = Object.create(currentEnv);\r\n        const descrs = Object.getOwnPropertyDescriptors(extension);\r\n        return Object.freeze(Object.defineProperties(env, descrs));\r\n    }\r\n    /**\r\n     * This hook is a simple way to let components use a sub environment.  Note that\r\n     * like for all hooks, it is important that this is only called in the\r\n     * constructor method.\r\n     */\r\n    function useSubEnv(envExtension) {\r\n        const node = getCurrent();\r\n        node.component.env = extendEnv(node.component.env, envExtension);\r\n        useChildSubEnv(envExtension);\r\n    }\r\n    function useChildSubEnv(envExtension) {\r\n        const node = getCurrent();\r\n        node.childEnv = extendEnv(node.childEnv, envExtension);\r\n    }\r\n    /**\r\n     * This hook will run a callback when a component is mounted and patched, and\r\n     * will run a cleanup function before patching and before unmounting the\r\n     * the component.\r\n     *\r\n     * @template T\r\n     * @param {Effect<T>} effect the effect to run on component mount and/or patch\r\n     * @param {()=>[...T]} [computeDependencies=()=>[NaN]] a callback to compute\r\n     *      dependencies that will decide if the effect needs to be cleaned up and\r\n     *      run again. If the dependencies did not change, the effect will not run\r\n     *      again. The default value returns an array containing only NaN because\r\n     *      NaN !== NaN, which will cause the effect to rerun on every patch.\r\n     */\r\n    function useEffect(effect, computeDependencies = () => [NaN]) {\r\n        let cleanup;\r\n        let dependencies;\r\n        onMounted(() => {\r\n            dependencies = computeDependencies();\r\n            cleanup = effect(...dependencies);\r\n        });\r\n        onPatched(() => {\r\n            const newDeps = computeDependencies();\r\n            const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);\r\n            if (shouldReapply) {\r\n                dependencies = newDeps;\r\n                if (cleanup) {\r\n                    cleanup();\r\n                }\r\n                cleanup = effect(...dependencies);\r\n            }\r\n        });\r\n        onWillUnmount(() => cleanup && cleanup());\r\n    }\r\n    // -----------------------------------------------------------------------------\r\n    // useExternalListener\r\n    // -----------------------------------------------------------------------------\r\n    /**\r\n     * When a component needs to listen to DOM Events on element(s) that are not\r\n     * part of his hierarchy, we can use the `useExternalListener` hook.\r\n     * It will correctly add and remove the event listener, whenever the\r\n     * component is mounted and unmounted.\r\n     *\r\n     * Example:\r\n     *  a menu needs to listen to the click on window to be closed automatically\r\n     *\r\n     * Usage:\r\n     *  in the constructor of the OWL component that needs to be notified,\r\n     *  `useExternalListener(window, 'click', this._doSomething);`\r\n     * */\r\n    function useExternalListener(target, eventName, handler, eventParams) {\r\n        const node = getCurrent();\r\n        const boundHandler = handler.bind(node.component);\r\n        onMounted(() => target.addEventListener(eventName, boundHandler, eventParams));\r\n        onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams));\r\n    }\r\n\r\n    config.shouldNormalizeDom = false;\r\n    config.mainEventHandler = mainEventHandler;\r\n    const blockDom = {\r\n        config,\r\n        // bdom entry points\r\n        mount: mount$1,\r\n        patch,\r\n        remove,\r\n        // bdom block types\r\n        list,\r\n        multi,\r\n        text,\r\n        toggler,\r\n        createBlock,\r\n        html,\r\n        comment,\r\n    };\r\n    const __info__ = {\r\n        version: App.version,\r\n    };\r\n\r\n    TemplateSet.prototype._compileTemplate = function _compileTemplate(name, template) {\r\n        return compile(template, {\r\n            name,\r\n            dev: this.dev,\r\n            translateFn: this.translateFn,\r\n            translatableAttributes: this.translatableAttributes,\r\n            customDirectives: this.customDirectives,\r\n            hasGlobalValues: this.hasGlobalValues,\r\n        });\r\n    };\r\n\r\n    exports.App = App;\r\n    exports.Component = Component;\r\n    exports.EventBus = EventBus;\r\n    exports.OwlError = OwlError;\r\n    exports.__info__ = __info__;\r\n    exports.batched = batched;\r\n    exports.blockDom = blockDom;\r\n    exports.loadFile = loadFile;\r\n    exports.markRaw = markRaw;\r\n    exports.markup = markup;\r\n    exports.mount = mount;\r\n    exports.onError = onError;\r\n    exports.onMounted = onMounted;\r\n    exports.onPatched = onPatched;\r\n    exports.onRendered = onRendered;\r\n    exports.onWillDestroy = onWillDestroy;\r\n    exports.onWillPatch = onWillPatch;\r\n    exports.onWillRender = onWillRender;\r\n    exports.onWillStart = onWillStart;\r\n    exports.onWillUnmount = onWillUnmount;\r\n    exports.onWillUpdateProps = onWillUpdateProps;\r\n    exports.reactive = reactive;\r\n    exports.status = status;\r\n    exports.toRaw = toRaw;\r\n    exports.useChildSubEnv = useChildSubEnv;\r\n    exports.useComponent = useComponent;\r\n    exports.useEffect = useEffect;\r\n    exports.useEnv = useEnv;\r\n    exports.useExternalListener = useExternalListener;\r\n    exports.useRef = useRef;\r\n    exports.useState = useState;\r\n    exports.useSubEnv = useSubEnv;\r\n    exports.validate = validate;\r\n    exports.validateType = validateType;\r\n    exports.whenReady = whenReady;\r\n    exports.xml = xml;\r\n\r\n    Object.defineProperty(exports, '__esModule', { value: true });\r\n\r\n\r\n    __info__.date = '2025-01-10T10:10:53.709Z';\r\n    __info__.hash = 'b31fa81';\r\n    __info__.url = 'https://github.com/odoo/owl';\r\n\r\n\r\n})(this.owl = this.owl || {});\r\n", "odoo.define(\"@odoo/owl\", [], function () {\n    \"use strict\";\n\n    return owl;\n});\n", "/*!\n * jQuery JavaScript Library v3.6.3\n * https://jquery.com/\n *\n * Includes Sizzle.js\n * https://sizzlejs.com/\n *\n * Copyright OpenJS Foundation and other contributors\n * Released under the MIT license\n * https://jquery.org/license\n *\n * Date: 2022-12-20T21:28Z\n */\n( function( global, factory ) {\n\n\t\"use strict\";\n\n\tif ( typeof module === \"object\" && typeof module.exports === \"object\" ) {\n\n\t\t// For CommonJS and CommonJS-like environments where a proper `window`\n\t\t// is present, execute the factory and get jQuery.\n\t\t// For environments that do not have a `window` with a `document`\n\t\t// (such as Node.js), expose a factory as module.exports.\n\t\t// This accentuates the need for the creation of a real `window`.\n\t\t// e.g. var jQuery = require(\"jquery\")(window);\n\t\t// See ticket trac-14549 for more info.\n\t\tmodule.exports = global.document ?\n\t\t\tfactory( global, true ) :\n\t\t\tfunction( w ) {\n\t\t\t\tif ( !w.document ) {\n\t\t\t\t\tthrow new Error( \"jQuery requires a window with a document\" );\n\t\t\t\t}\n\t\t\t\treturn factory( w );\n\t\t\t};\n\t} else {\n\t\tfactory( global );\n\t}\n\n// Pass this if window is not defined yet\n} )( typeof window !== \"undefined\" ? window : this, function( window, noGlobal ) {\n\n// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1\n// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode\n// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common\n// enough that all such attempts are guarded in a try block.\n\"use strict\";\n\nvar arr = [];\n\nvar getProto = Object.getPrototypeOf;\n\nvar slice = arr.slice;\n\nvar flat = arr.flat ? function( array ) {\n\treturn arr.flat.call( array );\n} : function( array ) {\n\treturn arr.concat.apply( [], array );\n};\n\n\nvar push = arr.push;\n\nvar indexOf = arr.indexOf;\n\nvar class2type = {};\n\nvar toString = class2type.toString;\n\nvar hasOwn = class2type.hasOwnProperty;\n\nvar fnToString = hasOwn.toString;\n\nvar ObjectFunctionString = fnToString.call( Object );\n\nvar support = {};\n\nvar isFunction = function isFunction( obj ) {\n\n\t\t// Support: Chrome <=57, Firefox <=52\n\t\t// In some browsers, typeof returns \"function\" for HTML <object> elements\n\t\t// (i.e., `typeof document.createElement( \"object\" ) === \"function\"`).\n\t\t// We don't want to classify *any* DOM node as a function.\n\t\t// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5\n\t\t// Plus for old WebKit, typeof returns \"function\" for HTML collections\n\t\t// (e.g., `typeof document.getElementsByTagName(\"div\") === \"function\"`). (gh-4756)\n\t\treturn typeof obj === \"function\" && typeof obj.nodeType !== \"number\" &&\n\t\t\ttypeof obj.item !== \"function\";\n\t};\n\n\nvar isWindow = function isWindow( obj ) {\n\t\treturn obj != null && obj === obj.window;\n\t};\n\n\nvar document = window.document;\n\n\n\n\tvar preservedScriptAttributes = {\n\t\ttype: true,\n\t\tsrc: true,\n\t\tnonce: true,\n\t\tnoModule: true\n\t};\n\n\tfunction DOMEval( code, node, doc ) {\n\t\tdoc = doc || document;\n\n\t\tvar i, val,\n\t\t\tscript = doc.createElement( \"script\" );\n\n\t\tscript.text = code;\n\t\tif ( node ) {\n\t\t\tfor ( i in preservedScriptAttributes ) {\n\n\t\t\t\t// Support: Firefox 64+, Edge 18+\n\t\t\t\t// Some browsers don't support the \"nonce\" property on scripts.\n\t\t\t\t// On the other hand, just using `getAttribute` is not enough as\n\t\t\t\t// the `nonce` attribute is reset to an empty string whenever it\n\t\t\t\t// becomes browsing-context connected.\n\t\t\t\t// See https://github.com/whatwg/html/issues/2369\n\t\t\t\t// See https://html.spec.whatwg.org/#nonce-attributes\n\t\t\t\t// The `node.getAttribute` check was added for the sake of\n\t\t\t\t// `jQuery.globalEval` so that it can fake a nonce-containing node\n\t\t\t\t// via an object.\n\t\t\t\tval = node[ i ] || node.getAttribute && node.getAttribute( i );\n\t\t\t\tif ( val ) {\n\t\t\t\t\tscript.setAttribute( i, val );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdoc.head.appendChild( script ).parentNode.removeChild( script );\n\t}\n\n\nfunction toType( obj ) {\n\tif ( obj == null ) {\n\t\treturn obj + \"\";\n\t}\n\n\t// Support: Android <=2.3 only (functionish RegExp)\n\treturn typeof obj === \"object\" || typeof obj === \"function\" ?\n\t\tclass2type[ toString.call( obj ) ] || \"object\" :\n\t\ttypeof obj;\n}\n/* global Symbol */\n// Defining this global in .eslintrc.json would create a danger of using the global\n// unguarded in another place, it seems safer to define global only for this module\n\n\n\nvar\n\tversion = \"3.6.3\",\n\n\t// Define a local copy of jQuery\n\tjQuery = function( selector, context ) {\n\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\t// Need init if jQuery is called (just allow error to be thrown if not included)\n\t\treturn new jQuery.fn.init( selector, context );\n\t};\n\njQuery.fn = jQuery.prototype = {\n\n\t// The current version of jQuery being used\n\tjquery: version,\n\n\tconstructor: jQuery,\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\ttoArray: function() {\n\t\treturn slice.call( this );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\n\t\t// Return all the elements in a clean array\n\t\tif ( num == null ) {\n\t\t\treturn slice.call( this );\n\t\t}\n\n\t\t// Return just the one element from the set\n\t\treturn num < 0 ? this[ num + this.length ] : this[ num ];\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems ) {\n\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery.merge( this.constructor(), elems );\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\teach: function( callback ) {\n\t\treturn jQuery.each( this, callback );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map( this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t} ) );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ) );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\teven: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn ( i + 1 ) % 2;\n\t\t} ) );\n\t},\n\n\todd: function() {\n\t\treturn this.pushStack( jQuery.grep( this, function( _elem, i ) {\n\t\t\treturn i % 2;\n\t\t} ) );\n\t},\n\n\teq: function( i ) {\n\t\tvar len = this.length,\n\t\t\tj = +i + ( i < 0 ? len : 0 );\n\t\treturn this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor();\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: arr.sort,\n\tsplice: arr.splice\n};\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[ 0 ] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\n\t\t// Skip the boolean and the target\n\t\ttarget = arguments[ i ] || {};\n\t\ti++;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !isFunction( target ) ) {\n\t\ttarget = {};\n\t}\n\n\t// Extend jQuery itself if only one argument is passed\n\tif ( i === length ) {\n\t\ttarget = this;\n\t\ti--;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\n\t\t// Only deal with non-null/undefined values\n\t\tif ( ( options = arguments[ i ] ) != null ) {\n\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent Object.prototype pollution\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( name === \"__proto__\" || target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject( copy ) ||\n\t\t\t\t\t( copyIsArray = Array.isArray( copy ) ) ) ) {\n\t\t\t\t\tsrc = target[ name ];\n\n\t\t\t\t\t// Ensure proper type for the source value\n\t\t\t\t\tif ( copyIsArray && !Array.isArray( src ) ) {\n\t\t\t\t\t\tclone = [];\n\t\t\t\t\t} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {\n\t\t\t\t\t\tclone = {};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src;\n\t\t\t\t\t}\n\t\t\t\t\tcopyIsArray = false;\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend( {\n\n\t// Unique for each copy of jQuery on the page\n\texpando: \"jQuery\" + ( version + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// Assume jQuery is ready without the ready module\n\tisReady: true,\n\n\terror: function( msg ) {\n\t\tthrow new Error( msg );\n\t},\n\n\tnoop: function() {},\n\n\tisPlainObject: function( obj ) {\n\t\tvar proto, Ctor;\n\n\t\t// Detect obvious negatives\n\t\t// Use toString instead of jQuery.type to catch host objects\n\t\tif ( !obj || toString.call( obj ) !== \"[object Object]\" ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tproto = getProto( obj );\n\n\t\t// Objects with no prototype (e.g., `Object.create( null )`) are plain\n\t\tif ( !proto ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Objects with prototype are plain iff they were constructed by a global Object function\n\t\tCtor = hasOwn.call( proto, \"constructor\" ) && proto.constructor;\n\t\treturn typeof Ctor === \"function\" && fnToString.call( Ctor ) === ObjectFunctionString;\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tvar name;\n\n\t\tfor ( name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\t// Evaluates a script in a provided context; falls back to the global one\n\t// if not specified.\n\tglobalEval: function( code, options, doc ) {\n\t\tDOMEval( code, { nonce: options && options.nonce }, doc );\n\t},\n\n\teach: function( obj, callback ) {\n\t\tvar length, i = 0;\n\n\t\tif ( isArrayLike( obj ) ) {\n\t\t\tlength = obj.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( i in obj ) {\n\t\t\t\tif ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn obj;\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( arr, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( arr != null ) {\n\t\t\tif ( isArrayLike( Object( arr ) ) ) {\n\t\t\t\tjQuery.merge( ret,\n\t\t\t\t\ttypeof arr === \"string\" ?\n\t\t\t\t\t\t[ arr ] : arr\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tpush.call( ret, arr );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, arr, i ) {\n\t\treturn arr == null ? -1 : indexOf.call( arr, elem, i );\n\t},\n\n\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t// push.apply(_, arraylike) throws on ancient WebKit\n\tmerge: function( first, second ) {\n\t\tvar len = +second.length,\n\t\t\tj = 0,\n\t\t\ti = first.length;\n\n\t\tfor ( ; j < len; j++ ) {\n\t\t\tfirst[ i++ ] = second[ j ];\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, invert ) {\n\t\tvar callbackInverse,\n\t\t\tmatches = [],\n\t\t\ti = 0,\n\t\t\tlength = elems.length,\n\t\t\tcallbackExpect = !invert;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( ; i < length; i++ ) {\n\t\t\tcallbackInverse = !callback( elems[ i ], i );\n\t\t\tif ( callbackInverse !== callbackExpect ) {\n\t\t\t\tmatches.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn matches;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar length, value,\n\t\t\ti = 0,\n\t\t\tret = [];\n\n\t\t// Go through the array, translating each of the items to their new values\n\t\tif ( isArrayLike( elems ) ) {\n\t\t\tlength = elems.length;\n\t\t\tfor ( ; i < length; i++ ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Go through every key on the object,\n\t\t} else {\n\t\t\tfor ( i in elems ) {\n\t\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\t\tif ( value != null ) {\n\t\t\t\t\tret.push( value );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn flat( ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\t// jQuery.support is not used in Core but other projects attach their\n\t// properties to it so it needs to exist.\n\tsupport: support\n} );\n\nif ( typeof Symbol === \"function\" ) {\n\tjQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];\n}\n\n// Populate the class2type map\njQuery.each( \"Boolean Number String Function Array Date RegExp Object Error Symbol\".split( \" \" ),\n\tfunction( _i, name ) {\n\t\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n\t} );\n\nfunction isArrayLike( obj ) {\n\n\t// Support: real iOS 8.2 only (not reproducible in simulator)\n\t// `in` check used to prevent JIT error (gh-2145)\n\t// hasOwn isn't used here due to false negatives\n\t// regarding Nodelist length in IE\n\tvar length = !!obj && \"length\" in obj && obj.length,\n\t\ttype = toType( obj );\n\n\tif ( isFunction( obj ) || isWindow( obj ) ) {\n\t\treturn false;\n\t}\n\n\treturn type === \"array\" || length === 0 ||\n\t\ttypeof length === \"number\" && length > 0 && ( length - 1 ) in obj;\n}\nvar Sizzle =\n/*!\n * Sizzle CSS Selector Engine v2.3.9\n * https://sizzlejs.com/\n *\n * Copyright JS Foundation and other contributors\n * Released under the MIT license\n * https://js.foundation/\n *\n * Date: 2022-12-19\n */\n( function( window ) {\nvar i,\n\tsupport,\n\tExpr,\n\tgetText,\n\tisXML,\n\ttokenize,\n\tcompile,\n\tselect,\n\toutermostContext,\n\tsortInput,\n\thasDuplicate,\n\n\t// Local document vars\n\tsetDocument,\n\tdocument,\n\tdocElem,\n\tdocumentIsHTML,\n\trbuggyQSA,\n\trbuggyMatches,\n\tmatches,\n\tcontains,\n\n\t// Instance-specific data\n\texpando = \"sizzle\" + 1 * new Date(),\n\tpreferredDoc = window.document,\n\tdirruns = 0,\n\tdone = 0,\n\tclassCache = createCache(),\n\ttokenCache = createCache(),\n\tcompilerCache = createCache(),\n\tnonnativeSelectorCache = createCache(),\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn 0;\n\t},\n\n\t// Instance methods\n\thasOwn = ( {} ).hasOwnProperty,\n\tarr = [],\n\tpop = arr.pop,\n\tpushNative = arr.push,\n\tpush = arr.push,\n\tslice = arr.slice,\n\n\t// Use a stripped-down indexOf as it's faster than native\n\t// https://jsperf.com/thor-indexof-vs-for/5\n\tindexOf = function( list, elem ) {\n\t\tvar i = 0,\n\t\t\tlen = list.length;\n\t\tfor ( ; i < len; i++ ) {\n\t\t\tif ( list[ i ] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t},\n\n\tbooleans = \"checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|\" +\n\t\t\"ismap|loop|multiple|open|readonly|required|scoped\",\n\n\t// Regular expressions\n\n\t// http://www.w3.org/TR/css3-selectors/#whitespace\n\twhitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\",\n\n\t// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram\n\tidentifier = \"(?:\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace +\n\t\t\"?|\\\\\\\\[^\\\\r\\\\n\\\\f]|[\\\\w-]|[^\\0-\\\\x7f])+\",\n\n\t// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors\n\tattributes = \"\\\\[\" + whitespace + \"*(\" + identifier + \")(?:\" + whitespace +\n\n\t\t// Operator (capture 2)\n\t\t\"*([*^$|!~]?=)\" + whitespace +\n\n\t\t// \"Attribute values must be CSS identifiers [capture 5]\n\t\t// or strings [capture 3 or capture 4]\"\n\t\t\"*(?:'((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\"|(\" + identifier + \"))|)\" +\n\t\twhitespace + \"*\\\\]\",\n\n\tpseudos = \":(\" + identifier + \")(?:\\\\((\" +\n\n\t\t// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:\n\t\t// 1. quoted (capture 3; capture 4 or capture 5)\n\t\t\"('((?:\\\\\\\\.|[^\\\\\\\\'])*)'|\\\"((?:\\\\\\\\.|[^\\\\\\\\\\\"])*)\\\")|\" +\n\n\t\t// 2. simple (capture 6)\n\t\t\"((?:\\\\\\\\.|[^\\\\\\\\()[\\\\]]|\" + attributes + \")*)|\" +\n\n\t\t// 3. anything else (capture 2)\n\t\t\".*\" +\n\t\t\")\\\\)|)\",\n\n\t// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter\n\trwhitespace = new RegExp( whitespace + \"+\", \"g\" ),\n\trtrim = new RegExp( \"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" +\n\t\twhitespace + \"+$\", \"g\" ),\n\n\trcomma = new RegExp( \"^\" + whitespace + \"*,\" + whitespace + \"*\" ),\n\trcombinators = new RegExp( \"^\" + whitespace + \"*([>+~]|\" + whitespace + \")\" + whitespace +\n\t\t\"*\" ),\n\trdescend = new RegExp( whitespace + \"|>\" ),\n\n\trpseudo = new RegExp( pseudos ),\n\tridentifier = new RegExp( \"^\" + identifier + \"$\" ),\n\n\tmatchExpr = {\n\t\t\"ID\": new RegExp( \"^#(\" + identifier + \")\" ),\n\t\t\"CLASS\": new RegExp( \"^\\\\.(\" + identifier + \")\" ),\n\t\t\"TAG\": new RegExp( \"^(\" + identifier + \"|[*])\" ),\n\t\t\"ATTR\": new RegExp( \"^\" + attributes ),\n\t\t\"PSEUDO\": new RegExp( \"^\" + pseudos ),\n\t\t\"CHILD\": new RegExp( \"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\" +\n\t\t\twhitespace + \"*(even|odd|(([+-]|)(\\\\d*)n|)\" + whitespace + \"*(?:([+-]|)\" +\n\t\t\twhitespace + \"*(\\\\d+)|))\" + whitespace + \"*\\\\)|)\", \"i\" ),\n\t\t\"bool\": new RegExp( \"^(?:\" + booleans + \")$\", \"i\" ),\n\n\t\t// For use in libraries implementing .is()\n\t\t// We use this for POS matching in `select`\n\t\t\"needsContext\": new RegExp( \"^\" + whitespace +\n\t\t\t\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\" + whitespace +\n\t\t\t\"*((?:-\\\\d)?\\\\d*)\" + whitespace + \"*\\\\)|)(?=[^-]|$)\", \"i\" )\n\t},\n\n\trhtml = /HTML$/i,\n\trinputs = /^(?:input|select|textarea|button)$/i,\n\trheader = /^h\\d$/i,\n\n\trnative = /^[^{]+\\{\\s*\\[native \\w/,\n\n\t// Easily-parseable/retrievable ID or TAG or CLASS selectors\n\trquickExpr = /^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,\n\n\trsibling = /[+~]/,\n\n\t// CSS escapes\n\t// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters\n\trunescape = new RegExp( \"\\\\\\\\[\\\\da-fA-F]{1,6}\" + whitespace + \"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\", \"g\" ),\n\tfunescape = function( escape, nonHex ) {\n\t\tvar high = \"0x\" + escape.slice( 1 ) - 0x10000;\n\n\t\treturn nonHex ?\n\n\t\t\t// Strip the backslash prefix from a non-hex escape sequence\n\t\t\tnonHex :\n\n\t\t\t// Replace a hexadecimal escape sequence with the encoded Unicode code point\n\t\t\t// Support: IE <=11+\n\t\t\t// For values outside the Basic Multilingual Plane (BMP), manually construct a\n\t\t\t// surrogate pair\n\t\t\thigh < 0 ?\n\t\t\t\tString.fromCharCode( high + 0x10000 ) :\n\t\t\t\tString.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );\n\t},\n\n\t// CSS string/identifier serialization\n\t// https://drafts.csswg.org/cssom/#common-serializing-idioms\n\trcssescape = /([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,\n\tfcssescape = function( ch, asCodePoint ) {\n\t\tif ( asCodePoint ) {\n\n\t\t\t// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER\n\t\t\tif ( ch === \"\\0\" ) {\n\t\t\t\treturn \"\\uFFFD\";\n\t\t\t}\n\n\t\t\t// Control characters and (dependent upon position) numbers get escaped as code points\n\t\t\treturn ch.slice( 0, -1 ) + \"\\\\\" +\n\t\t\t\tch.charCodeAt( ch.length - 1 ).toString( 16 ) + \" \";\n\t\t}\n\n\t\t// Other potentially-special ASCII characters get backslash-escaped\n\t\treturn \"\\\\\" + ch;\n\t},\n\n\t// Used for iframes\n\t// See setDocument()\n\t// Removing the function wrapper causes a \"Permission Denied\"\n\t// error in IE\n\tunloadHandler = function() {\n\t\tsetDocument();\n\t},\n\n\tinDisabledFieldset = addCombinator(\n\t\tfunction( elem ) {\n\t\t\treturn elem.disabled === true && elem.nodeName.toLowerCase() === \"fieldset\";\n\t\t},\n\t\t{ dir: \"parentNode\", next: \"legend\" }\n\t);\n\n// Optimize for push.apply( _, NodeList )\ntry {\n\tpush.apply(\n\t\t( arr = slice.call( preferredDoc.childNodes ) ),\n\t\tpreferredDoc.childNodes\n\t);\n\n\t// Support: Android<4.0\n\t// Detect silently failing push.apply\n\t// eslint-disable-next-line no-unused-expressions\n\tarr[ preferredDoc.childNodes.length ].nodeType;\n} catch ( e ) {\n\tpush = { apply: arr.length ?\n\n\t\t// Leverage slice if possible\n\t\tfunction( target, els ) {\n\t\t\tpushNative.apply( target, slice.call( els ) );\n\t\t} :\n\n\t\t// Support: IE<9\n\t\t// Otherwise append directly\n\t\tfunction( target, els ) {\n\t\t\tvar j = target.length,\n\t\t\t\ti = 0;\n\n\t\t\t// Can't trust NodeList.length\n\t\t\twhile ( ( target[ j++ ] = els[ i++ ] ) ) {}\n\t\t\ttarget.length = j - 1;\n\t\t}\n\t};\n}\n\nfunction Sizzle( selector, context, results, seed ) {\n\tvar m, i, elem, nid, match, groups, newSelector,\n\t\tnewContext = context && context.ownerDocument,\n\n\t\t// nodeType defaults to 9, since context defaults to document\n\t\tnodeType = context ? context.nodeType : 9;\n\n\tresults = results || [];\n\n\t// Return early from calls with invalid selector or context\n\tif ( typeof selector !== \"string\" || !selector ||\n\t\tnodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {\n\n\t\treturn results;\n\t}\n\n\t// Try to shortcut find operations (as opposed to filters) in HTML documents\n\tif ( !seed ) {\n\t\tsetDocument( context );\n\t\tcontext = context || document;\n\n\t\tif ( documentIsHTML ) {\n\n\t\t\t// If the selector is sufficiently simple, try using a \"get*By*\" DOM method\n\t\t\t// (excepting DocumentFragment context, where the methods don't exist)\n\t\t\tif ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {\n\n\t\t\t\t// ID selector\n\t\t\t\tif ( ( m = match[ 1 ] ) ) {\n\n\t\t\t\t\t// Document context\n\t\t\t\t\tif ( nodeType === 9 ) {\n\t\t\t\t\t\tif ( ( elem = context.getElementById( m ) ) ) {\n\n\t\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === m ) {\n\t\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t// Element context\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// Support: IE, Opera, Webkit\n\t\t\t\t\t\t// TODO: identify versions\n\t\t\t\t\t\t// getElementById can match elements by name instead of ID\n\t\t\t\t\t\tif ( newContext && ( elem = newContext.getElementById( m ) ) &&\n\t\t\t\t\t\t\tcontains( context, elem ) &&\n\t\t\t\t\t\t\telem.id === m ) {\n\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\treturn results;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t// Type selector\n\t\t\t\t} else if ( match[ 2 ] ) {\n\t\t\t\t\tpush.apply( results, context.getElementsByTagName( selector ) );\n\t\t\t\t\treturn results;\n\n\t\t\t\t// Class selector\n\t\t\t\t} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&\n\t\t\t\t\tcontext.getElementsByClassName ) {\n\n\t\t\t\t\tpush.apply( results, context.getElementsByClassName( m ) );\n\t\t\t\t\treturn results;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Take advantage of querySelectorAll\n\t\t\tif ( support.qsa &&\n\t\t\t\t!nonnativeSelectorCache[ selector + \" \" ] &&\n\t\t\t\t( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&\n\n\t\t\t\t// Support: IE 8 only\n\t\t\t\t// Exclude object elements\n\t\t\t\t( nodeType !== 1 || context.nodeName.toLowerCase() !== \"object\" ) ) {\n\n\t\t\t\tnewSelector = selector;\n\t\t\t\tnewContext = context;\n\n\t\t\t\t// qSA considers elements outside a scoping root when evaluating child or\n\t\t\t\t// descendant combinators, which is not what we want.\n\t\t\t\t// In such cases, we work around the behavior by prefixing every selector in the\n\t\t\t\t// list with an ID selector referencing the scope context.\n\t\t\t\t// The technique has to be used as well when a leading combinator is used\n\t\t\t\t// as such selectors are not recognized by querySelectorAll.\n\t\t\t\t// Thanks to Andrew Dupont for this technique.\n\t\t\t\tif ( nodeType === 1 &&\n\t\t\t\t\t( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {\n\n\t\t\t\t\t// Expand context for sibling selectors\n\t\t\t\t\tnewContext = rsibling.test( selector ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext;\n\n\t\t\t\t\t// We can use :scope instead of the ID hack if the browser\n\t\t\t\t\t// supports it & if we're not changing the context.\n\t\t\t\t\tif ( newContext !== context || !support.scope ) {\n\n\t\t\t\t\t\t// Capture the context ID, setting it first if necessary\n\t\t\t\t\t\tif ( ( nid = context.getAttribute( \"id\" ) ) ) {\n\t\t\t\t\t\t\tnid = nid.replace( rcssescape, fcssescape );\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcontext.setAttribute( \"id\", ( nid = expando ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prefix every selector in the list\n\t\t\t\t\tgroups = tokenize( selector );\n\t\t\t\t\ti = groups.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tgroups[ i ] = ( nid ? \"#\" + nid : \":scope\" ) + \" \" +\n\t\t\t\t\t\t\ttoSelector( groups[ i ] );\n\t\t\t\t\t}\n\t\t\t\t\tnewSelector = groups.join( \",\" );\n\t\t\t\t}\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// `qSA` may not throw for unrecognized parts using forgiving parsing:\n\t\t\t\t\t// https://drafts.csswg.org/selectors/#forgiving-selector\n\t\t\t\t\t// like the `:has()` pseudo-class:\n\t\t\t\t\t// https://drafts.csswg.org/selectors/#relational\n\t\t\t\t\t// `CSS.supports` is still expected to return `false` then:\n\t\t\t\t\t// https://drafts.csswg.org/css-conditional-4/#typedef-supports-selector-fn\n\t\t\t\t\t// https://drafts.csswg.org/css-conditional-4/#dfn-support-selector\n\t\t\t\t\tif ( support.cssSupportsSelector &&\n\n\t\t\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\t\t\t!CSS.supports( \"selector(:is(\" + newSelector + \"))\" ) ) {\n\n\t\t\t\t\t\t// Support: IE 11+\n\t\t\t\t\t\t// Throw to get to the same code path as an error directly in qSA.\n\t\t\t\t\t\t// Note: once we only support browser supporting\n\t\t\t\t\t\t// `CSS.supports('selector(...)')`, we can most likely drop\n\t\t\t\t\t\t// the `try-catch`. IE doesn't implement the API.\n\t\t\t\t\t\tthrow new Error();\n\t\t\t\t\t}\n\n\t\t\t\t\tpush.apply( results,\n\t\t\t\t\t\tnewContext.querySelectorAll( newSelector )\n\t\t\t\t\t);\n\t\t\t\t\treturn results;\n\t\t\t\t} catch ( qsaError ) {\n\t\t\t\t\tnonnativeSelectorCache( selector, true );\n\t\t\t\t} finally {\n\t\t\t\t\tif ( nid === expando ) {\n\t\t\t\t\t\tcontext.removeAttribute( \"id\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// All others\n\treturn select( selector.replace( rtrim, \"$1\" ), context, results, seed );\n}\n\n/**\n * Create key-value caches of limited size\n * @returns {function(string, object)} Returns the Object data after storing it on itself with\n *\tproperty name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)\n *\tdeleting the oldest entry\n */\nfunction createCache() {\n\tvar keys = [];\n\n\tfunction cache( key, value ) {\n\n\t\t// Use (key + \" \") to avoid collision with native prototype properties (see Issue #157)\n\t\tif ( keys.push( key + \" \" ) > Expr.cacheLength ) {\n\n\t\t\t// Only keep the most recent entries\n\t\t\tdelete cache[ keys.shift() ];\n\t\t}\n\t\treturn ( cache[ key + \" \" ] = value );\n\t}\n\treturn cache;\n}\n\n/**\n * Mark a function for special use by Sizzle\n * @param {Function} fn The function to mark\n */\nfunction markFunction( fn ) {\n\tfn[ expando ] = true;\n\treturn fn;\n}\n\n/**\n * Support testing using an element\n * @param {Function} fn Passed the created element and returns a boolean result\n */\nfunction assert( fn ) {\n\tvar el = document.createElement( \"fieldset\" );\n\n\ttry {\n\t\treturn !!fn( el );\n\t} catch ( e ) {\n\t\treturn false;\n\t} finally {\n\n\t\t// Remove from its parent by default\n\t\tif ( el.parentNode ) {\n\t\t\tel.parentNode.removeChild( el );\n\t\t}\n\n\t\t// release memory in IE\n\t\tel = null;\n\t}\n}\n\n/**\n * Adds the same handler for all of the specified attrs\n * @param {String} attrs Pipe-separated list of attributes\n * @param {Function} handler The method that will be applied\n */\nfunction addHandle( attrs, handler ) {\n\tvar arr = attrs.split( \"|\" ),\n\t\ti = arr.length;\n\n\twhile ( i-- ) {\n\t\tExpr.attrHandle[ arr[ i ] ] = handler;\n\t}\n}\n\n/**\n * Checks document order of two siblings\n * @param {Element} a\n * @param {Element} b\n * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b\n */\nfunction siblingCheck( a, b ) {\n\tvar cur = b && a,\n\t\tdiff = cur && a.nodeType === 1 && b.nodeType === 1 &&\n\t\t\ta.sourceIndex - b.sourceIndex;\n\n\t// Use IE sourceIndex if available on both nodes\n\tif ( diff ) {\n\t\treturn diff;\n\t}\n\n\t// Check if b follows a\n\tif ( cur ) {\n\t\twhile ( ( cur = cur.nextSibling ) ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn a ? 1 : -1;\n}\n\n/**\n * Returns a function to use in pseudos for input types\n * @param {String} type\n */\nfunction createInputPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn name === \"input\" && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for buttons\n * @param {String} type\n */\nfunction createButtonPseudo( type ) {\n\treturn function( elem ) {\n\t\tvar name = elem.nodeName.toLowerCase();\n\t\treturn ( name === \"input\" || name === \"button\" ) && elem.type === type;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for :enabled/:disabled\n * @param {Boolean} disabled true for :disabled; false for :enabled\n */\nfunction createDisabledPseudo( disabled ) {\n\n\t// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable\n\treturn function( elem ) {\n\n\t\t// Only certain elements can match :enabled or :disabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled\n\t\t// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled\n\t\tif ( \"form\" in elem ) {\n\n\t\t\t// Check for inherited disabledness on relevant non-disabled elements:\n\t\t\t// * listed form-associated elements in a disabled fieldset\n\t\t\t//   https://html.spec.whatwg.org/multipage/forms.html#category-listed\n\t\t\t//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled\n\t\t\t// * option elements in a disabled optgroup\n\t\t\t//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled\n\t\t\t// All such elements have a \"form\" property.\n\t\t\tif ( elem.parentNode && elem.disabled === false ) {\n\n\t\t\t\t// Option elements defer to a parent optgroup if present\n\t\t\t\tif ( \"label\" in elem ) {\n\t\t\t\t\tif ( \"label\" in elem.parentNode ) {\n\t\t\t\t\t\treturn elem.parentNode.disabled === disabled;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn elem.disabled === disabled;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Support: IE 6 - 11\n\t\t\t\t// Use the isDisabled shortcut property to check for disabled fieldset ancestors\n\t\t\t\treturn elem.isDisabled === disabled ||\n\n\t\t\t\t\t// Where there is no isDisabled, check manually\n\t\t\t\t\t/* jshint -W018 */\n\t\t\t\t\telem.isDisabled !== !disabled &&\n\t\t\t\t\tinDisabledFieldset( elem ) === disabled;\n\t\t\t}\n\n\t\t\treturn elem.disabled === disabled;\n\n\t\t// Try to winnow out elements that can't be disabled before trusting the disabled property.\n\t\t// Some victims get caught in our net (label, legend, menu, track), but it shouldn't\n\t\t// even exist on them, let alone have a boolean value.\n\t\t} else if ( \"label\" in elem ) {\n\t\t\treturn elem.disabled === disabled;\n\t\t}\n\n\t\t// Remaining elements are neither :enabled nor :disabled\n\t\treturn false;\n\t};\n}\n\n/**\n * Returns a function to use in pseudos for positionals\n * @param {Function} fn\n */\nfunction createPositionalPseudo( fn ) {\n\treturn markFunction( function( argument ) {\n\t\targument = +argument;\n\t\treturn markFunction( function( seed, matches ) {\n\t\t\tvar j,\n\t\t\t\tmatchIndexes = fn( [], seed.length, argument ),\n\t\t\t\ti = matchIndexes.length;\n\n\t\t\t// Match elements found at the specified indexes\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( seed[ ( j = matchIndexes[ i ] ) ] ) {\n\t\t\t\t\tseed[ j ] = !( matches[ j ] = seed[ j ] );\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t} );\n}\n\n/**\n * Checks a node for validity as a Sizzle context\n * @param {Element|Object=} context\n * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value\n */\nfunction testContext( context ) {\n\treturn context && typeof context.getElementsByTagName !== \"undefined\" && context;\n}\n\n// Expose support vars for convenience\nsupport = Sizzle.support = {};\n\n/**\n * Detects XML nodes\n * @param {Element|Object} elem An element or a document\n * @returns {Boolean} True iff elem is a non-HTML XML node\n */\nisXML = Sizzle.isXML = function( elem ) {\n\tvar namespace = elem && elem.namespaceURI,\n\t\tdocElem = elem && ( elem.ownerDocument || elem ).documentElement;\n\n\t// Support: IE <=8\n\t// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes\n\t// https://bugs.jquery.com/ticket/4833\n\treturn !rhtml.test( namespace || docElem && docElem.nodeName || \"HTML\" );\n};\n\n/**\n * Sets document-related variables once based on the current document\n * @param {Element|Object} [doc] An element or document object to use to set the document\n * @returns {Object} Returns the current document\n */\nsetDocument = Sizzle.setDocument = function( node ) {\n\tvar hasCompare, subWindow,\n\t\tdoc = node ? node.ownerDocument || node : preferredDoc;\n\n\t// Return early if doc is invalid or already selected\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {\n\t\treturn document;\n\t}\n\n\t// Update global variables\n\tdocument = doc;\n\tdocElem = document.documentElement;\n\tdocumentIsHTML = !isXML( document );\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+\n\t// Accessing iframe documents after unload throws \"permission denied\" errors (jQuery #13936)\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( preferredDoc != document &&\n\t\t( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {\n\n\t\t// Support: IE 11, Edge\n\t\tif ( subWindow.addEventListener ) {\n\t\t\tsubWindow.addEventListener( \"unload\", unloadHandler, false );\n\n\t\t// Support: IE 9 - 10 only\n\t\t} else if ( subWindow.attachEvent ) {\n\t\t\tsubWindow.attachEvent( \"onunload\", unloadHandler );\n\t\t}\n\t}\n\n\t// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,\n\t// Safari 4 - 5 only, Opera <=11.6 - 12.x only\n\t// IE/Edge & older browsers don't support the :scope pseudo-class.\n\t// Support: Safari 6.0 only\n\t// Safari 6.0 supports :scope but it's an alias of :root there.\n\tsupport.scope = assert( function( el ) {\n\t\tdocElem.appendChild( el ).appendChild( document.createElement( \"div\" ) );\n\t\treturn typeof el.querySelectorAll !== \"undefined\" &&\n\t\t\t!el.querySelectorAll( \":scope fieldset div\" ).length;\n\t} );\n\n\t// Support: Chrome 105+, Firefox 104+, Safari 15.4+\n\t// Make sure forgiving mode is not used in `CSS.supports( \"selector(...)\" )`.\n\t//\n\t// `:is()` uses a forgiving selector list as an argument and is widely\n\t// implemented, so it's a good one to test against.\n\tsupport.cssSupportsSelector = assert( function() {\n\t\t/* eslint-disable no-undef */\n\n\t\treturn CSS.supports( \"selector(*)\" ) &&\n\n\t\t\t// Support: Firefox 78-81 only\n\t\t\t// In old Firefox, `:is()` didn't use forgiving parsing. In that case,\n\t\t\t// fail this test as there's no selector to test against that.\n\t\t\t// `CSS.supports` uses unforgiving parsing\n\t\t\tdocument.querySelectorAll( \":is(:jqfake)\" ) &&\n\n\t\t\t// `*` is needed as Safari & newer Chrome implemented something in between\n\t\t\t// for `:has()` - it throws in `qSA` if it only contains an unsupported\n\t\t\t// argument but multiple ones, one of which is supported, are fine.\n\t\t\t// We want to play safe in case `:is()` gets the same treatment.\n\t\t\t!CSS.supports( \"selector(:is(*,:jqfake))\" );\n\n\t\t/* eslint-enable */\n\t} );\n\n\t/* Attributes\n\t---------------------------------------------------------------------- */\n\n\t// Support: IE<8\n\t// Verify that getAttribute really returns attributes and not properties\n\t// (excepting IE8 booleans)\n\tsupport.attributes = assert( function( el ) {\n\t\tel.className = \"i\";\n\t\treturn !el.getAttribute( \"className\" );\n\t} );\n\n\t/* getElement(s)By*\n\t---------------------------------------------------------------------- */\n\n\t// Check if getElementsByTagName(\"*\") returns only elements\n\tsupport.getElementsByTagName = assert( function( el ) {\n\t\tel.appendChild( document.createComment( \"\" ) );\n\t\treturn !el.getElementsByTagName( \"*\" ).length;\n\t} );\n\n\t// Support: IE<9\n\tsupport.getElementsByClassName = rnative.test( document.getElementsByClassName );\n\n\t// Support: IE<10\n\t// Check if getElementById returns elements by name\n\t// The broken getElementById methods don't pick up programmatically-set names,\n\t// so use a roundabout getElementsByName test\n\tsupport.getById = assert( function( el ) {\n\t\tdocElem.appendChild( el ).id = expando;\n\t\treturn !document.getElementsByName || !document.getElementsByName( expando ).length;\n\t} );\n\n\t// ID filter and find\n\tif ( support.getById ) {\n\t\tExpr.filter[ \"ID\" ] = function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn elem.getAttribute( \"id\" ) === attrId;\n\t\t\t};\n\t\t};\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar elem = context.getElementById( id );\n\t\t\t\treturn elem ? [ elem ] : [];\n\t\t\t}\n\t\t};\n\t} else {\n\t\tExpr.filter[ \"ID\" ] =  function( id ) {\n\t\t\tvar attrId = id.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" &&\n\t\t\t\t\telem.getAttributeNode( \"id\" );\n\t\t\t\treturn node && node.value === attrId;\n\t\t\t};\n\t\t};\n\n\t\t// Support: IE 6 - 7 only\n\t\t// getElementById is not reliable as a find shortcut\n\t\tExpr.find[ \"ID\" ] = function( id, context ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && documentIsHTML ) {\n\t\t\t\tvar node, i, elems,\n\t\t\t\t\telem = context.getElementById( id );\n\n\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t// Verify the id attribute\n\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fall back on getElementsByName\n\t\t\t\t\telems = context.getElementsByName( id );\n\t\t\t\t\ti = 0;\n\t\t\t\t\twhile ( ( elem = elems[ i++ ] ) ) {\n\t\t\t\t\t\tnode = elem.getAttributeNode( \"id\" );\n\t\t\t\t\t\tif ( node && node.value === id ) {\n\t\t\t\t\t\t\treturn [ elem ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn [];\n\t\t\t}\n\t\t};\n\t}\n\n\t// Tag\n\tExpr.find[ \"TAG\" ] = support.getElementsByTagName ?\n\t\tfunction( tag, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( tag );\n\n\t\t\t// DocumentFragment nodes don't have gEBTN\n\t\t\t} else if ( support.qsa ) {\n\t\t\t\treturn context.querySelectorAll( tag );\n\t\t\t}\n\t\t} :\n\n\t\tfunction( tag, context ) {\n\t\t\tvar elem,\n\t\t\t\ttmp = [],\n\t\t\t\ti = 0,\n\n\t\t\t\t// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too\n\t\t\t\tresults = context.getElementsByTagName( tag );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( tag === \"*\" ) {\n\t\t\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn tmp;\n\t\t\t}\n\t\t\treturn results;\n\t\t};\n\n\t// Class\n\tExpr.find[ \"CLASS\" ] = support.getElementsByClassName && function( className, context ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && documentIsHTML ) {\n\t\t\treturn context.getElementsByClassName( className );\n\t\t}\n\t};\n\n\t/* QSA/matchesSelector\n\t---------------------------------------------------------------------- */\n\n\t// QSA and matchesSelector support\n\n\t// matchesSelector(:active) reports false when true (IE9/Opera 11.5)\n\trbuggyMatches = [];\n\n\t// qSa(:focus) reports false when true (Chrome 21)\n\t// We allow this because of a bug in IE8/9 that throws an error\n\t// whenever `document.activeElement` is accessed on an iframe\n\t// So, we allow :focus to pass through QSA all the time to avoid the IE error\n\t// See https://bugs.jquery.com/ticket/13378\n\trbuggyQSA = [];\n\n\tif ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {\n\n\t\t// Build QSA regex\n\t\t// Regex strategy adopted from Diego Perini\n\t\tassert( function( el ) {\n\n\t\t\tvar input;\n\n\t\t\t// Select is set to empty string on purpose\n\t\t\t// This is to test IE's treatment of not explicitly\n\t\t\t// setting a boolean content attribute,\n\t\t\t// since its presence should be enough\n\t\t\t// https://bugs.jquery.com/ticket/12359\n\t\t\tdocElem.appendChild( el ).innerHTML = \"<a id='\" + expando + \"'></a>\" +\n\t\t\t\t\"<select id='\" + expando + \"-\\r\\\\' msallowcapture=''>\" +\n\t\t\t\t\"<option selected=''></option></select>\";\n\n\t\t\t// Support: IE8, Opera 11-12.16\n\t\t\t// Nothing should be selected when empty strings follow ^= or $= or *=\n\t\t\t// The test attribute must be unknown in Opera but \"safe\" for WinRT\n\t\t\t// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section\n\t\t\tif ( el.querySelectorAll( \"[msallowcapture^='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"[*^$]=\" + whitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Support: IE8\n\t\t\t// Boolean attributes and \"value\" are not treated correctly\n\t\t\tif ( !el.querySelectorAll( \"[selected]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*(?:value|\" + booleans + \")\" );\n\t\t\t}\n\n\t\t\t// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+\n\t\t\tif ( !el.querySelectorAll( \"[id~=\" + expando + \"-]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"~=\" );\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 15 - 18+\n\t\t\t// IE 11/Edge don't find elements on a `[name='']` query in some cases.\n\t\t\t// Adding a temporary attribute to the document before the selection works\n\t\t\t// around the issue.\n\t\t\t// Interestingly, IE 10 & older don't seem to have the issue.\n\t\t\tinput = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"name\", \"\" );\n\t\t\tel.appendChild( input );\n\t\t\tif ( !el.querySelectorAll( \"[name='']\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"\\\\[\" + whitespace + \"*name\" + whitespace + \"*=\" +\n\t\t\t\t\twhitespace + \"*(?:''|\\\"\\\")\" );\n\t\t\t}\n\n\t\t\t// Webkit/Opera - :checked should return selected option elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( !el.querySelectorAll( \":checked\" ).length ) {\n\t\t\t\trbuggyQSA.push( \":checked\" );\n\t\t\t}\n\n\t\t\t// Support: Safari 8+, iOS 8+\n\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=136851\n\t\t\t// In-page `selector#id sibling-combinator selector` fails\n\t\t\tif ( !el.querySelectorAll( \"a#\" + expando + \"+*\" ).length ) {\n\t\t\t\trbuggyQSA.push( \".#.+[+~]\" );\n\t\t\t}\n\n\t\t\t// Support: Firefox <=3.6 - 5 only\n\t\t\t// Old Firefox doesn't throw on a badly-escaped identifier.\n\t\t\t// el.querySelectorAll( \"\\\\\\f\" );\n\t\t\t// rbuggyQSA.push( \"[\\\\r\\\\n\\\\f]\" );\n\t\t} );\n\n\t\tassert( function( el ) {\n\t\t\tel.innerHTML = \"<a href='' disabled='disabled'></a>\" +\n\t\t\t\t\"<select disabled='disabled'><option/></select>\";\n\n\t\t\t// Support: Windows 8 Native Apps\n\t\t\t// The type and name attributes are restricted during .innerHTML assignment\n\t\t\tvar input = document.createElement( \"input\" );\n\t\t\tinput.setAttribute( \"type\", \"hidden\" );\n\t\t\tel.appendChild( input ).setAttribute( \"name\", \"D\" );\n\n\t\t\t// Support: IE8\n\t\t\t// Enforce case-sensitivity of name attribute\n\t\t\tif ( el.querySelectorAll( \"[name=d]\" ).length ) {\n\t\t\t\trbuggyQSA.push( \"name\" + whitespace + \"*[*^$|!~]?=\" );\n\t\t\t}\n\n\t\t\t// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)\n\t\t\t// IE8 throws error here and will not see later tests\n\t\t\tif ( el.querySelectorAll( \":enabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: IE9-11+\n\t\t\t// IE's :disabled selector does not pick up the children of disabled fieldsets\n\t\t\tdocElem.appendChild( el ).disabled = true;\n\t\t\tif ( el.querySelectorAll( \":disabled\" ).length !== 2 ) {\n\t\t\t\trbuggyQSA.push( \":enabled\", \":disabled\" );\n\t\t\t}\n\n\t\t\t// Support: Opera 10 - 11 only\n\t\t\t// Opera 10-11 does not throw on post-comma invalid pseudos\n\t\t\t// el.querySelectorAll( \"*,:x\" );\n\t\t\t// rbuggyQSA.push( \",.*:\" );\n\t\t} );\n\t}\n\n\tif ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||\n\t\tdocElem.webkitMatchesSelector ||\n\t\tdocElem.mozMatchesSelector ||\n\t\tdocElem.oMatchesSelector ||\n\t\tdocElem.msMatchesSelector ) ) ) ) {\n\n\t\tassert( function( el ) {\n\n\t\t\t// Check to see if it's possible to do matchesSelector\n\t\t\t// on a disconnected node (IE 9)\n\t\t\tsupport.disconnectedMatch = matches.call( el, \"*\" );\n\n\t\t\t// This should fail with an exception\n\t\t\t// Gecko does not error, returns false instead\n\t\t\t// matches.call( el, \"[s!='']:x\" );\n\t\t\t// rbuggyMatches.push( \"!=\", pseudos );\n\t\t} );\n\t}\n\n\tif ( !support.cssSupportsSelector ) {\n\n\t\t// Support: Chrome 105+, Safari 15.4+\n\t\t// `:has()` uses a forgiving selector list as an argument so our regular\n\t\t// `try-catch` mechanism fails to catch `:has()` with arguments not supported\n\t\t// natively like `:has(:contains(\"Foo\"))`. Where supported & spec-compliant,\n\t\t// we now use `CSS.supports(\"selector(:is(SELECTOR_TO_BE_TESTED))\")`, but\n\t\t// outside that we mark `:has` as buggy.\n\t\trbuggyQSA.push( \":has\" );\n\t}\n\n\trbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( \"|\" ) );\n\trbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( \"|\" ) );\n\n\t/* Contains\n\t---------------------------------------------------------------------- */\n\thasCompare = rnative.test( docElem.compareDocumentPosition );\n\n\t// Element contains another\n\t// Purposefully self-exclusive\n\t// As in, an element does not contain itself\n\tcontains = hasCompare || rnative.test( docElem.contains ) ?\n\t\tfunction( a, b ) {\n\n\t\t\t// Support: IE <9 only\n\t\t\t// IE doesn't have `contains` on `document` so we need to check for\n\t\t\t// `documentElement` presence.\n\t\t\t// We need to fall back to `a` when `documentElement` is missing\n\t\t\t// as `ownerDocument` of elements within `<template/>` may have\n\t\t\t// a null one - a default behavior of all modern browsers.\n\t\t\tvar adown = a.nodeType === 9 && a.documentElement || a,\n\t\t\t\tbup = b && b.parentNode;\n\t\t\treturn a === bup || !!( bup && bup.nodeType === 1 && (\n\t\t\t\tadown.contains ?\n\t\t\t\t\tadown.contains( bup ) :\n\t\t\t\t\ta.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16\n\t\t\t) );\n\t\t} :\n\t\tfunction( a, b ) {\n\t\t\tif ( b ) {\n\t\t\t\twhile ( ( b = b.parentNode ) ) {\n\t\t\t\t\tif ( b === a ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\n\t/* Sorting\n\t---------------------------------------------------------------------- */\n\n\t// Document order sorting\n\tsortOrder = hasCompare ?\n\tfunction( a, b ) {\n\n\t\t// Flag for duplicate removal\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Sort on method existence if only one input has compareDocumentPosition\n\t\tvar compare = !a.compareDocumentPosition - !b.compareDocumentPosition;\n\t\tif ( compare ) {\n\t\t\treturn compare;\n\t\t}\n\n\t\t// Calculate position if both inputs belong to the same document\n\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t// two documents; shallow comparisons work.\n\t\t// eslint-disable-next-line eqeqeq\n\t\tcompare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?\n\t\t\ta.compareDocumentPosition( b ) :\n\n\t\t\t// Otherwise we know they are disconnected\n\t\t\t1;\n\n\t\t// Disconnected nodes\n\t\tif ( compare & 1 ||\n\t\t\t( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {\n\n\t\t\t// Choose the first element that is related to our preferred document\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( a == document || a.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, a ) ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\tif ( b == document || b.ownerDocument == preferredDoc &&\n\t\t\t\tcontains( preferredDoc, b ) ) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\t// Maintain original order\n\t\t\treturn sortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\t\t}\n\n\t\treturn compare & 4 ? -1 : 1;\n\t} :\n\tfunction( a, b ) {\n\n\t\t// Exit early if the nodes are identical\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tap = [ a ],\n\t\t\tbp = [ b ];\n\n\t\t// Parentless nodes are either documents or disconnected\n\t\tif ( !aup || !bup ) {\n\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\treturn a == document ? -1 :\n\t\t\t\tb == document ? 1 :\n\t\t\t\t/* eslint-enable eqeqeq */\n\t\t\t\taup ? -1 :\n\t\t\t\tbup ? 1 :\n\t\t\t\tsortInput ?\n\t\t\t\t( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :\n\t\t\t\t0;\n\n\t\t// If the nodes are siblings, we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\t\t}\n\n\t\t// Otherwise we need full lists of their ancestors for comparison\n\t\tcur = a;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tap.unshift( cur );\n\t\t}\n\t\tcur = b;\n\t\twhile ( ( cur = cur.parentNode ) ) {\n\t\t\tbp.unshift( cur );\n\t\t}\n\n\t\t// Walk down the tree looking for a discrepancy\n\t\twhile ( ap[ i ] === bp[ i ] ) {\n\t\t\ti++;\n\t\t}\n\n\t\treturn i ?\n\n\t\t\t// Do a sibling check if the nodes have a common ancestor\n\t\t\tsiblingCheck( ap[ i ], bp[ i ] ) :\n\n\t\t\t// Otherwise nodes in our document sort first\n\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t// two documents; shallow comparisons work.\n\t\t\t/* eslint-disable eqeqeq */\n\t\t\tap[ i ] == preferredDoc ? -1 :\n\t\t\tbp[ i ] == preferredDoc ? 1 :\n\t\t\t/* eslint-enable eqeqeq */\n\t\t\t0;\n\t};\n\n\treturn document;\n};\n\nSizzle.matches = function( expr, elements ) {\n\treturn Sizzle( expr, null, null, elements );\n};\n\nSizzle.matchesSelector = function( elem, expr ) {\n\tsetDocument( elem );\n\n\tif ( support.matchesSelector && documentIsHTML &&\n\t\t!nonnativeSelectorCache[ expr + \" \" ] &&\n\t\t( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&\n\t\t( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {\n\n\t\ttry {\n\t\t\tvar ret = matches.call( elem, expr );\n\n\t\t\t// IE 9's matchesSelector returns false on disconnected nodes\n\t\t\tif ( ret || support.disconnectedMatch ||\n\n\t\t\t\t// As well, disconnected nodes are said to be in a document\n\t\t\t\t// fragment in IE 9\n\t\t\t\telem.document && elem.document.nodeType !== 11 ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t} catch ( e ) {\n\t\t\tnonnativeSelectorCache( expr, true );\n\t\t}\n\t}\n\n\treturn Sizzle( expr, document, null, [ elem ] ).length > 0;\n};\n\nSizzle.contains = function( context, elem ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( context.ownerDocument || context ) != document ) {\n\t\tsetDocument( context );\n\t}\n\treturn contains( context, elem );\n};\n\nSizzle.attr = function( elem, name ) {\n\n\t// Set document vars if needed\n\t// Support: IE 11+, Edge 17 - 18+\n\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t// two documents; shallow comparisons work.\n\t// eslint-disable-next-line eqeqeq\n\tif ( ( elem.ownerDocument || elem ) != document ) {\n\t\tsetDocument( elem );\n\t}\n\n\tvar fn = Expr.attrHandle[ name.toLowerCase() ],\n\n\t\t// Don't get fooled by Object.prototype properties (jQuery #13807)\n\t\tval = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?\n\t\t\tfn( elem, name, !documentIsHTML ) :\n\t\t\tundefined;\n\n\treturn val !== undefined ?\n\t\tval :\n\t\tsupport.attributes || !documentIsHTML ?\n\t\t\telem.getAttribute( name ) :\n\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\tval.value :\n\t\t\t\tnull;\n};\n\nSizzle.escape = function( sel ) {\n\treturn ( sel + \"\" ).replace( rcssescape, fcssescape );\n};\n\nSizzle.error = function( msg ) {\n\tthrow new Error( \"Syntax error, unrecognized expression: \" + msg );\n};\n\n/**\n * Document sorting and removing duplicates\n * @param {ArrayLike} results\n */\nSizzle.uniqueSort = function( results ) {\n\tvar elem,\n\t\tduplicates = [],\n\t\tj = 0,\n\t\ti = 0;\n\n\t// Unless we *know* we can detect duplicates, assume their presence\n\thasDuplicate = !support.detectDuplicates;\n\tsortInput = !support.sortStable && results.slice( 0 );\n\tresults.sort( sortOrder );\n\n\tif ( hasDuplicate ) {\n\t\twhile ( ( elem = results[ i++ ] ) ) {\n\t\t\tif ( elem === results[ i ] ) {\n\t\t\t\tj = duplicates.push( i );\n\t\t\t}\n\t\t}\n\t\twhile ( j-- ) {\n\t\t\tresults.splice( duplicates[ j ], 1 );\n\t\t}\n\t}\n\n\t// Clear input after sorting to release objects\n\t// See https://github.com/jquery/sizzle/pull/225\n\tsortInput = null;\n\n\treturn results;\n};\n\n/**\n * Utility function for retrieving the text value of an array of DOM nodes\n * @param {Array|Element} elem\n */\ngetText = Sizzle.getText = function( elem ) {\n\tvar node,\n\t\tret = \"\",\n\t\ti = 0,\n\t\tnodeType = elem.nodeType;\n\n\tif ( !nodeType ) {\n\n\t\t// If no nodeType, this is expected to be an array\n\t\twhile ( ( node = elem[ i++ ] ) ) {\n\n\t\t\t// Do not traverse comment nodes\n\t\t\tret += getText( node );\n\t\t}\n\t} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {\n\n\t\t// Use textContent for elements\n\t\t// innerText usage removed for consistency of new lines (jQuery #11153)\n\t\tif ( typeof elem.textContent === \"string\" ) {\n\t\t\treturn elem.textContent;\n\t\t} else {\n\n\t\t\t// Traverse its children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tret += getText( elem );\n\t\t\t}\n\t\t}\n\t} else if ( nodeType === 3 || nodeType === 4 ) {\n\t\treturn elem.nodeValue;\n\t}\n\n\t// Do not include comment or processing instruction nodes\n\n\treturn ret;\n};\n\nExpr = Sizzle.selectors = {\n\n\t// Can be adjusted by the user\n\tcacheLength: 50,\n\n\tcreatePseudo: markFunction,\n\n\tmatch: matchExpr,\n\n\tattrHandle: {},\n\n\tfind: {},\n\n\trelative: {\n\t\t\">\": { dir: \"parentNode\", first: true },\n\t\t\" \": { dir: \"parentNode\" },\n\t\t\"+\": { dir: \"previousSibling\", first: true },\n\t\t\"~\": { dir: \"previousSibling\" }\n\t},\n\n\tpreFilter: {\n\t\t\"ATTR\": function( match ) {\n\t\t\tmatch[ 1 ] = match[ 1 ].replace( runescape, funescape );\n\n\t\t\t// Move the given value to match[3] whether quoted or unquoted\n\t\t\tmatch[ 3 ] = ( match[ 3 ] || match[ 4 ] ||\n\t\t\t\tmatch[ 5 ] || \"\" ).replace( runescape, funescape );\n\n\t\t\tif ( match[ 2 ] === \"~=\" ) {\n\t\t\t\tmatch[ 3 ] = \" \" + match[ 3 ] + \" \";\n\t\t\t}\n\n\t\t\treturn match.slice( 0, 4 );\n\t\t},\n\n\t\t\"CHILD\": function( match ) {\n\n\t\t\t/* matches from matchExpr[\"CHILD\"]\n\t\t\t\t1 type (only|nth|...)\n\t\t\t\t2 what (child|of-type)\n\t\t\t\t3 argument (even|odd|\\d*|\\d*n([+-]\\d+)?|...)\n\t\t\t\t4 xn-component of xn+y argument ([+-]?\\d*n|)\n\t\t\t\t5 sign of xn-component\n\t\t\t\t6 x of xn-component\n\t\t\t\t7 sign of y-component\n\t\t\t\t8 y of y-component\n\t\t\t*/\n\t\t\tmatch[ 1 ] = match[ 1 ].toLowerCase();\n\n\t\t\tif ( match[ 1 ].slice( 0, 3 ) === \"nth\" ) {\n\n\t\t\t\t// nth-* requires argument\n\t\t\t\tif ( !match[ 3 ] ) {\n\t\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t\t}\n\n\t\t\t\t// numeric x and y parameters for Expr.filter.CHILD\n\t\t\t\t// remember that false/true cast respectively to 0/1\n\t\t\t\tmatch[ 4 ] = +( match[ 4 ] ?\n\t\t\t\t\tmatch[ 5 ] + ( match[ 6 ] || 1 ) :\n\t\t\t\t\t2 * ( match[ 3 ] === \"even\" || match[ 3 ] === \"odd\" ) );\n\t\t\t\tmatch[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === \"odd\" );\n\n\t\t\t\t// other types prohibit arguments\n\t\t\t} else if ( match[ 3 ] ) {\n\t\t\t\tSizzle.error( match[ 0 ] );\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\t\"PSEUDO\": function( match ) {\n\t\t\tvar excess,\n\t\t\t\tunquoted = !match[ 6 ] && match[ 2 ];\n\n\t\t\tif ( matchExpr[ \"CHILD\" ].test( match[ 0 ] ) ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Accept quoted arguments as-is\n\t\t\tif ( match[ 3 ] ) {\n\t\t\t\tmatch[ 2 ] = match[ 4 ] || match[ 5 ] || \"\";\n\n\t\t\t// Strip excess characters from unquoted arguments\n\t\t\t} else if ( unquoted && rpseudo.test( unquoted ) &&\n\n\t\t\t\t// Get excess from tokenize (recursively)\n\t\t\t\t( excess = tokenize( unquoted, true ) ) &&\n\n\t\t\t\t// advance to the next closing parenthesis\n\t\t\t\t( excess = unquoted.indexOf( \")\", unquoted.length - excess ) - unquoted.length ) ) {\n\n\t\t\t\t// excess is a negative index\n\t\t\t\tmatch[ 0 ] = match[ 0 ].slice( 0, excess );\n\t\t\t\tmatch[ 2 ] = unquoted.slice( 0, excess );\n\t\t\t}\n\n\t\t\t// Return only captures needed by the pseudo filter method (type and argument)\n\t\t\treturn match.slice( 0, 3 );\n\t\t}\n\t},\n\n\tfilter: {\n\n\t\t\"TAG\": function( nodeNameSelector ) {\n\t\t\tvar nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn nodeNameSelector === \"*\" ?\n\t\t\t\tfunction() {\n\t\t\t\t\treturn true;\n\t\t\t\t} :\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn elem.nodeName && elem.nodeName.toLowerCase() === nodeName;\n\t\t\t\t};\n\t\t},\n\n\t\t\"CLASS\": function( className ) {\n\t\t\tvar pattern = classCache[ className + \" \" ];\n\n\t\t\treturn pattern ||\n\t\t\t\t( pattern = new RegExp( \"(^|\" + whitespace +\n\t\t\t\t\t\")\" + className + \"(\" + whitespace + \"|$)\" ) ) && classCache(\n\t\t\t\t\t\tclassName, function( elem ) {\n\t\t\t\t\t\t\treturn pattern.test(\n\t\t\t\t\t\t\t\ttypeof elem.className === \"string\" && elem.className ||\n\t\t\t\t\t\t\t\ttypeof elem.getAttribute !== \"undefined\" &&\n\t\t\t\t\t\t\t\t\telem.getAttribute( \"class\" ) ||\n\t\t\t\t\t\t\t\t\"\"\n\t\t\t\t\t\t\t);\n\t\t\t\t} );\n\t\t},\n\n\t\t\"ATTR\": function( name, operator, check ) {\n\t\t\treturn function( elem ) {\n\t\t\t\tvar result = Sizzle.attr( elem, name );\n\n\t\t\t\tif ( result == null ) {\n\t\t\t\t\treturn operator === \"!=\";\n\t\t\t\t}\n\t\t\t\tif ( !operator ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tresult += \"\";\n\n\t\t\t\t/* eslint-disable max-len */\n\n\t\t\t\treturn operator === \"=\" ? result === check :\n\t\t\t\t\toperator === \"!=\" ? result !== check :\n\t\t\t\t\toperator === \"^=\" ? check && result.indexOf( check ) === 0 :\n\t\t\t\t\toperator === \"*=\" ? check && result.indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"$=\" ? check && result.slice( -check.length ) === check :\n\t\t\t\t\toperator === \"~=\" ? ( \" \" + result.replace( rwhitespace, \" \" ) + \" \" ).indexOf( check ) > -1 :\n\t\t\t\t\toperator === \"|=\" ? result === check || result.slice( 0, check.length + 1 ) === check + \"-\" :\n\t\t\t\t\tfalse;\n\t\t\t\t/* eslint-enable max-len */\n\n\t\t\t};\n\t\t},\n\n\t\t\"CHILD\": function( type, what, _argument, first, last ) {\n\t\t\tvar simple = type.slice( 0, 3 ) !== \"nth\",\n\t\t\t\tforward = type.slice( -4 ) !== \"last\",\n\t\t\t\tofType = what === \"of-type\";\n\n\t\t\treturn first === 1 && last === 0 ?\n\n\t\t\t\t// Shortcut for :nth-*(n)\n\t\t\t\tfunction( elem ) {\n\t\t\t\t\treturn !!elem.parentNode;\n\t\t\t\t} :\n\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tvar cache, uniqueCache, outerCache, node, nodeIndex, start,\n\t\t\t\t\t\tdir = simple !== forward ? \"nextSibling\" : \"previousSibling\",\n\t\t\t\t\t\tparent = elem.parentNode,\n\t\t\t\t\t\tname = ofType && elem.nodeName.toLowerCase(),\n\t\t\t\t\t\tuseCache = !xml && !ofType,\n\t\t\t\t\t\tdiff = false;\n\n\t\t\t\t\tif ( parent ) {\n\n\t\t\t\t\t\t// :(first|last|only)-(child|of-type)\n\t\t\t\t\t\tif ( simple ) {\n\t\t\t\t\t\t\twhile ( dir ) {\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\twhile ( ( node = node[ dir ] ) ) {\n\t\t\t\t\t\t\t\t\tif ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) {\n\n\t\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Reverse direction for :only-* (if we haven't yet done so)\n\t\t\t\t\t\t\t\tstart = dir = type === \"only\" && !start && \"nextSibling\";\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstart = [ forward ? parent.firstChild : parent.lastChild ];\n\n\t\t\t\t\t\t// non-xml :nth-child(...) stores cache data on `parent`\n\t\t\t\t\t\tif ( forward && useCache ) {\n\n\t\t\t\t\t\t\t// Seek `elem` from a previously-cached index\n\n\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\tnode = parent;\n\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\tdiff = nodeIndex && cache[ 2 ];\n\t\t\t\t\t\t\tnode = nodeIndex && parent.childNodes[ nodeIndex ];\n\n\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\n\t\t\t\t\t\t\t\t// Fallback to seeking `elem` from the start\n\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t// When found, cache indexes on `parent` and break\n\t\t\t\t\t\t\t\tif ( node.nodeType === 1 && ++diff && node === elem ) {\n\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, nodeIndex, diff ];\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Use previously-cached element index if available\n\t\t\t\t\t\t\tif ( useCache ) {\n\n\t\t\t\t\t\t\t\t// ...in a gzip-friendly way\n\t\t\t\t\t\t\t\tnode = elem;\n\t\t\t\t\t\t\t\touterCache = node[ expando ] || ( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\tcache = uniqueCache[ type ] || [];\n\t\t\t\t\t\t\t\tnodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];\n\t\t\t\t\t\t\t\tdiff = nodeIndex;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// xml :nth-child(...)\n\t\t\t\t\t\t\t// or :nth-last-child(...) or :nth(-last)?-of-type(...)\n\t\t\t\t\t\t\tif ( diff === false ) {\n\n\t\t\t\t\t\t\t\t// Use the same loop as above to seek `elem` from the start\n\t\t\t\t\t\t\t\twhile ( ( node = ++nodeIndex && node && node[ dir ] ||\n\t\t\t\t\t\t\t\t\t( diff = nodeIndex = 0 ) || start.pop() ) ) {\n\n\t\t\t\t\t\t\t\t\tif ( ( ofType ?\n\t\t\t\t\t\t\t\t\t\tnode.nodeName.toLowerCase() === name :\n\t\t\t\t\t\t\t\t\t\tnode.nodeType === 1 ) &&\n\t\t\t\t\t\t\t\t\t\t++diff ) {\n\n\t\t\t\t\t\t\t\t\t\t// Cache the index of each encountered element\n\t\t\t\t\t\t\t\t\t\tif ( useCache ) {\n\t\t\t\t\t\t\t\t\t\t\touterCache = node[ expando ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( node[ expando ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache = outerCache[ node.uniqueID ] ||\n\t\t\t\t\t\t\t\t\t\t\t\t( outerCache[ node.uniqueID ] = {} );\n\n\t\t\t\t\t\t\t\t\t\t\tuniqueCache[ type ] = [ dirruns, diff ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\tif ( node === elem ) {\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Incorporate the offset, then check against cycle size\n\t\t\t\t\t\tdiff -= last;\n\t\t\t\t\t\treturn diff === first || ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t},\n\n\t\t\"PSEUDO\": function( pseudo, argument ) {\n\n\t\t\t// pseudo-class names are case-insensitive\n\t\t\t// http://www.w3.org/TR/selectors/#pseudo-classes\n\t\t\t// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters\n\t\t\t// Remember that setFilters inherits from pseudos\n\t\t\tvar args,\n\t\t\t\tfn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||\n\t\t\t\t\tSizzle.error( \"unsupported pseudo: \" + pseudo );\n\n\t\t\t// The user may use createPseudo to indicate that\n\t\t\t// arguments are needed to create the filter function\n\t\t\t// just as Sizzle does\n\t\t\tif ( fn[ expando ] ) {\n\t\t\t\treturn fn( argument );\n\t\t\t}\n\n\t\t\t// But maintain support for old signatures\n\t\t\tif ( fn.length > 1 ) {\n\t\t\t\targs = [ pseudo, pseudo, \"\", argument ];\n\t\t\t\treturn Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?\n\t\t\t\t\tmarkFunction( function( seed, matches ) {\n\t\t\t\t\t\tvar idx,\n\t\t\t\t\t\t\tmatched = fn( seed, argument ),\n\t\t\t\t\t\t\ti = matched.length;\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tidx = indexOf( seed, matched[ i ] );\n\t\t\t\t\t\t\tseed[ idx ] = !( matches[ idx ] = matched[ i ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} ) :\n\t\t\t\t\tfunction( elem ) {\n\t\t\t\t\t\treturn fn( elem, 0, args );\n\t\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn fn;\n\t\t}\n\t},\n\n\tpseudos: {\n\n\t\t// Potentially complex pseudos\n\t\t\"not\": markFunction( function( selector ) {\n\n\t\t\t// Trim the selector passed to compile\n\t\t\t// to avoid treating leading and trailing\n\t\t\t// spaces as combinators\n\t\t\tvar input = [],\n\t\t\t\tresults = [],\n\t\t\t\tmatcher = compile( selector.replace( rtrim, \"$1\" ) );\n\n\t\t\treturn matcher[ expando ] ?\n\t\t\t\tmarkFunction( function( seed, matches, _context, xml ) {\n\t\t\t\t\tvar elem,\n\t\t\t\t\t\tunmatched = matcher( seed, null, xml, [] ),\n\t\t\t\t\t\ti = seed.length;\n\n\t\t\t\t\t// Match elements unmatched by `matcher`\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\t\t\t\t\tseed[ i ] = !( matches[ i ] = elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} ) :\n\t\t\t\tfunction( elem, _context, xml ) {\n\t\t\t\t\tinput[ 0 ] = elem;\n\t\t\t\t\tmatcher( input, null, xml, results );\n\n\t\t\t\t\t// Don't keep the element (issue #299)\n\t\t\t\t\tinput[ 0 ] = null;\n\t\t\t\t\treturn !results.pop();\n\t\t\t\t};\n\t\t} ),\n\n\t\t\"has\": markFunction( function( selector ) {\n\t\t\treturn function( elem ) {\n\t\t\t\treturn Sizzle( selector, elem ).length > 0;\n\t\t\t};\n\t\t} ),\n\n\t\t\"contains\": markFunction( function( text ) {\n\t\t\ttext = text.replace( runescape, funescape );\n\t\t\treturn function( elem ) {\n\t\t\t\treturn ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;\n\t\t\t};\n\t\t} ),\n\n\t\t// \"Whether an element is represented by a :lang() selector\n\t\t// is based solely on the element's language value\n\t\t// being equal to the identifier C,\n\t\t// or beginning with the identifier C immediately followed by \"-\".\n\t\t// The matching of C against the element's language value is performed case-insensitively.\n\t\t// The identifier C does not have to be a valid language name.\"\n\t\t// http://www.w3.org/TR/selectors/#lang-pseudo\n\t\t\"lang\": markFunction( function( lang ) {\n\n\t\t\t// lang value must be a valid identifier\n\t\t\tif ( !ridentifier.test( lang || \"\" ) ) {\n\t\t\t\tSizzle.error( \"unsupported lang: \" + lang );\n\t\t\t}\n\t\t\tlang = lang.replace( runescape, funescape ).toLowerCase();\n\t\t\treturn function( elem ) {\n\t\t\t\tvar elemLang;\n\t\t\t\tdo {\n\t\t\t\t\tif ( ( elemLang = documentIsHTML ?\n\t\t\t\t\t\telem.lang :\n\t\t\t\t\t\telem.getAttribute( \"xml:lang\" ) || elem.getAttribute( \"lang\" ) ) ) {\n\n\t\t\t\t\t\telemLang = elemLang.toLowerCase();\n\t\t\t\t\t\treturn elemLang === lang || elemLang.indexOf( lang + \"-\" ) === 0;\n\t\t\t\t\t}\n\t\t\t\t} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );\n\t\t\t\treturn false;\n\t\t\t};\n\t\t} ),\n\n\t\t// Miscellaneous\n\t\t\"target\": function( elem ) {\n\t\t\tvar hash = window.location && window.location.hash;\n\t\t\treturn hash && hash.slice( 1 ) === elem.id;\n\t\t},\n\n\t\t\"root\": function( elem ) {\n\t\t\treturn elem === docElem;\n\t\t},\n\n\t\t\"focus\": function( elem ) {\n\t\t\treturn elem === document.activeElement &&\n\t\t\t\t( !document.hasFocus || document.hasFocus() ) &&\n\t\t\t\t!!( elem.type || elem.href || ~elem.tabIndex );\n\t\t},\n\n\t\t// Boolean properties\n\t\t\"enabled\": createDisabledPseudo( false ),\n\t\t\"disabled\": createDisabledPseudo( true ),\n\n\t\t\"checked\": function( elem ) {\n\n\t\t\t// In CSS3, :checked should return both checked and selected elements\n\t\t\t// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked\n\t\t\tvar nodeName = elem.nodeName.toLowerCase();\n\t\t\treturn ( nodeName === \"input\" && !!elem.checked ) ||\n\t\t\t\t( nodeName === \"option\" && !!elem.selected );\n\t\t},\n\n\t\t\"selected\": function( elem ) {\n\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\t// eslint-disable-next-line no-unused-expressions\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\t// Contents\n\t\t\"empty\": function( elem ) {\n\n\t\t\t// http://www.w3.org/TR/selectors/#empty-pseudo\n\t\t\t// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),\n\t\t\t//   but not by others (comment: 8; processing instruction: 7; etc.)\n\t\t\t// nodeType < 6 works because attributes (2) do not appear as children\n\t\t\tfor ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {\n\t\t\t\tif ( elem.nodeType < 6 ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t},\n\n\t\t\"parent\": function( elem ) {\n\t\t\treturn !Expr.pseudos[ \"empty\" ]( elem );\n\t\t},\n\n\t\t// Element/input types\n\t\t\"header\": function( elem ) {\n\t\t\treturn rheader.test( elem.nodeName );\n\t\t},\n\n\t\t\"input\": function( elem ) {\n\t\t\treturn rinputs.test( elem.nodeName );\n\t\t},\n\n\t\t\"button\": function( elem ) {\n\t\t\tvar name = elem.nodeName.toLowerCase();\n\t\t\treturn name === \"input\" && elem.type === \"button\" || name === \"button\";\n\t\t},\n\n\t\t\"text\": function( elem ) {\n\t\t\tvar attr;\n\t\t\treturn elem.nodeName.toLowerCase() === \"input\" &&\n\t\t\t\telem.type === \"text\" &&\n\n\t\t\t\t// Support: IE <10 only\n\t\t\t\t// New HTML5 attribute values (e.g., \"search\") appear with elem.type === \"text\"\n\t\t\t\t( ( attr = elem.getAttribute( \"type\" ) ) == null ||\n\t\t\t\t\tattr.toLowerCase() === \"text\" );\n\t\t},\n\n\t\t// Position-in-collection\n\t\t\"first\": createPositionalPseudo( function() {\n\t\t\treturn [ 0 ];\n\t\t} ),\n\n\t\t\"last\": createPositionalPseudo( function( _matchIndexes, length ) {\n\t\t\treturn [ length - 1 ];\n\t\t} ),\n\n\t\t\"eq\": createPositionalPseudo( function( _matchIndexes, length, argument ) {\n\t\t\treturn [ argument < 0 ? argument + length : argument ];\n\t\t} ),\n\n\t\t\"even\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"odd\": createPositionalPseudo( function( matchIndexes, length ) {\n\t\t\tvar i = 1;\n\t\t\tfor ( ; i < length; i += 2 ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"lt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ?\n\t\t\t\targument + length :\n\t\t\t\targument > length ?\n\t\t\t\t\tlength :\n\t\t\t\t\targument;\n\t\t\tfor ( ; --i >= 0; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} ),\n\n\t\t\"gt\": createPositionalPseudo( function( matchIndexes, length, argument ) {\n\t\t\tvar i = argument < 0 ? argument + length : argument;\n\t\t\tfor ( ; ++i < length; ) {\n\t\t\t\tmatchIndexes.push( i );\n\t\t\t}\n\t\t\treturn matchIndexes;\n\t\t} )\n\t}\n};\n\nExpr.pseudos[ \"nth\" ] = Expr.pseudos[ \"eq\" ];\n\n// Add button/input type pseudos\nfor ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {\n\tExpr.pseudos[ i ] = createInputPseudo( i );\n}\nfor ( i in { submit: true, reset: true } ) {\n\tExpr.pseudos[ i ] = createButtonPseudo( i );\n}\n\n// Easy API for creating new setFilters\nfunction setFilters() {}\nsetFilters.prototype = Expr.filters = Expr.pseudos;\nExpr.setFilters = new setFilters();\n\ntokenize = Sizzle.tokenize = function( selector, parseOnly ) {\n\tvar matched, match, tokens, type,\n\t\tsoFar, groups, preFilters,\n\t\tcached = tokenCache[ selector + \" \" ];\n\n\tif ( cached ) {\n\t\treturn parseOnly ? 0 : cached.slice( 0 );\n\t}\n\n\tsoFar = selector;\n\tgroups = [];\n\tpreFilters = Expr.preFilter;\n\n\twhile ( soFar ) {\n\n\t\t// Comma and first run\n\t\tif ( !matched || ( match = rcomma.exec( soFar ) ) ) {\n\t\t\tif ( match ) {\n\n\t\t\t\t// Don't consume trailing commas as valid\n\t\t\t\tsoFar = soFar.slice( match[ 0 ].length ) || soFar;\n\t\t\t}\n\t\t\tgroups.push( ( tokens = [] ) );\n\t\t}\n\n\t\tmatched = false;\n\n\t\t// Combinators\n\t\tif ( ( match = rcombinators.exec( soFar ) ) ) {\n\t\t\tmatched = match.shift();\n\t\t\ttokens.push( {\n\t\t\t\tvalue: matched,\n\n\t\t\t\t// Cast descendant combinators to space\n\t\t\t\ttype: match[ 0 ].replace( rtrim, \" \" )\n\t\t\t} );\n\t\t\tsoFar = soFar.slice( matched.length );\n\t\t}\n\n\t\t// Filters\n\t\tfor ( type in Expr.filter ) {\n\t\t\tif ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||\n\t\t\t\t( match = preFilters[ type ]( match ) ) ) ) {\n\t\t\t\tmatched = match.shift();\n\t\t\t\ttokens.push( {\n\t\t\t\t\tvalue: matched,\n\t\t\t\t\ttype: type,\n\t\t\t\t\tmatches: match\n\t\t\t\t} );\n\t\t\t\tsoFar = soFar.slice( matched.length );\n\t\t\t}\n\t\t}\n\n\t\tif ( !matched ) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// Return the length of the invalid excess\n\t// if we're just parsing\n\t// Otherwise, throw an error or return tokens\n\treturn parseOnly ?\n\t\tsoFar.length :\n\t\tsoFar ?\n\t\t\tSizzle.error( selector ) :\n\n\t\t\t// Cache the tokens\n\t\t\ttokenCache( selector, groups ).slice( 0 );\n};\n\nfunction toSelector( tokens ) {\n\tvar i = 0,\n\t\tlen = tokens.length,\n\t\tselector = \"\";\n\tfor ( ; i < len; i++ ) {\n\t\tselector += tokens[ i ].value;\n\t}\n\treturn selector;\n}\n\nfunction addCombinator( matcher, combinator, base ) {\n\tvar dir = combinator.dir,\n\t\tskip = combinator.next,\n\t\tkey = skip || dir,\n\t\tcheckNonElements = base && key === \"parentNode\",\n\t\tdoneName = done++;\n\n\treturn combinator.first ?\n\n\t\t// Check against closest ancestor/preceding element\n\t\tfunction( elem, context, xml ) {\n\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\treturn matcher( elem, context, xml );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} :\n\n\t\t// Check against all ancestor/preceding elements\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar oldCache, uniqueCache, outerCache,\n\t\t\t\tnewCache = [ dirruns, doneName ];\n\n\t\t\t// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching\n\t\t\tif ( xml ) {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\tif ( matcher( elem, context, xml ) ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twhile ( ( elem = elem[ dir ] ) ) {\n\t\t\t\t\tif ( elem.nodeType === 1 || checkNonElements ) {\n\t\t\t\t\t\touterCache = elem[ expando ] || ( elem[ expando ] = {} );\n\n\t\t\t\t\t\t// Support: IE <9 only\n\t\t\t\t\t\t// Defend against cloned attroperties (jQuery gh-1709)\n\t\t\t\t\t\tuniqueCache = outerCache[ elem.uniqueID ] ||\n\t\t\t\t\t\t\t( outerCache[ elem.uniqueID ] = {} );\n\n\t\t\t\t\t\tif ( skip && skip === elem.nodeName.toLowerCase() ) {\n\t\t\t\t\t\t\telem = elem[ dir ] || elem;\n\t\t\t\t\t\t} else if ( ( oldCache = uniqueCache[ key ] ) &&\n\t\t\t\t\t\t\toldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {\n\n\t\t\t\t\t\t\t// Assign to newCache so results back-propagate to previous elements\n\t\t\t\t\t\t\treturn ( newCache[ 2 ] = oldCache[ 2 ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Reuse newcache so results back-propagate to previous elements\n\t\t\t\t\t\t\tuniqueCache[ key ] = newCache;\n\n\t\t\t\t\t\t\t// A match means we're done; a fail means we have to keep checking\n\t\t\t\t\t\t\tif ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n}\n\nfunction elementMatcher( matchers ) {\n\treturn matchers.length > 1 ?\n\t\tfunction( elem, context, xml ) {\n\t\t\tvar i = matchers.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( !matchers[ i ]( elem, context, xml ) ) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} :\n\t\tmatchers[ 0 ];\n}\n\nfunction multipleContexts( selector, contexts, results ) {\n\tvar i = 0,\n\t\tlen = contexts.length;\n\tfor ( ; i < len; i++ ) {\n\t\tSizzle( selector, contexts[ i ], results );\n\t}\n\treturn results;\n}\n\nfunction condense( unmatched, map, filter, context, xml ) {\n\tvar elem,\n\t\tnewUnmatched = [],\n\t\ti = 0,\n\t\tlen = unmatched.length,\n\t\tmapped = map != null;\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( elem = unmatched[ i ] ) ) {\n\t\t\tif ( !filter || filter( elem, context, xml ) ) {\n\t\t\t\tnewUnmatched.push( elem );\n\t\t\t\tif ( mapped ) {\n\t\t\t\t\tmap.push( i );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newUnmatched;\n}\n\nfunction setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {\n\tif ( postFilter && !postFilter[ expando ] ) {\n\t\tpostFilter = setMatcher( postFilter );\n\t}\n\tif ( postFinder && !postFinder[ expando ] ) {\n\t\tpostFinder = setMatcher( postFinder, postSelector );\n\t}\n\treturn markFunction( function( seed, results, context, xml ) {\n\t\tvar temp, i, elem,\n\t\t\tpreMap = [],\n\t\t\tpostMap = [],\n\t\t\tpreexisting = results.length,\n\n\t\t\t// Get initial elements from seed or context\n\t\t\telems = seed || multipleContexts(\n\t\t\t\tselector || \"*\",\n\t\t\t\tcontext.nodeType ? [ context ] : context,\n\t\t\t\t[]\n\t\t\t),\n\n\t\t\t// Prefilter to get matcher input, preserving a map for seed-results synchronization\n\t\t\tmatcherIn = preFilter && ( seed || !selector ) ?\n\t\t\t\tcondense( elems, preMap, preFilter, context, xml ) :\n\t\t\t\telems,\n\n\t\t\tmatcherOut = matcher ?\n\n\t\t\t\t// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,\n\t\t\t\tpostFinder || ( seed ? preFilter : preexisting || postFilter ) ?\n\n\t\t\t\t\t// ...intermediate processing is necessary\n\t\t\t\t\t[] :\n\n\t\t\t\t\t// ...otherwise use results directly\n\t\t\t\t\tresults :\n\t\t\t\tmatcherIn;\n\n\t\t// Find primary matches\n\t\tif ( matcher ) {\n\t\t\tmatcher( matcherIn, matcherOut, context, xml );\n\t\t}\n\n\t\t// Apply postFilter\n\t\tif ( postFilter ) {\n\t\t\ttemp = condense( matcherOut, postMap );\n\t\t\tpostFilter( temp, [], context, xml );\n\n\t\t\t// Un-match failing elements by moving them back to matcherIn\n\t\t\ti = temp.length;\n\t\t\twhile ( i-- ) {\n\t\t\t\tif ( ( elem = temp[ i ] ) ) {\n\t\t\t\t\tmatcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ( seed ) {\n\t\t\tif ( postFinder || preFilter ) {\n\t\t\t\tif ( postFinder ) {\n\n\t\t\t\t\t// Get the final matcherOut by condensing this intermediate into postFinder contexts\n\t\t\t\t\ttemp = [];\n\t\t\t\t\ti = matcherOut.length;\n\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) ) {\n\n\t\t\t\t\t\t\t// Restore matcherIn since elem is not yet a final match\n\t\t\t\t\t\t\ttemp.push( ( matcherIn[ i ] = elem ) );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpostFinder( null, ( matcherOut = [] ), temp, xml );\n\t\t\t\t}\n\n\t\t\t\t// Move matched elements from seed to results to keep them synchronized\n\t\t\t\ti = matcherOut.length;\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\tif ( ( elem = matcherOut[ i ] ) &&\n\t\t\t\t\t\t( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {\n\n\t\t\t\t\t\tseed[ temp ] = !( results[ temp ] = elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Add elements to results, through postFinder if defined\n\t\t} else {\n\t\t\tmatcherOut = condense(\n\t\t\t\tmatcherOut === results ?\n\t\t\t\t\tmatcherOut.splice( preexisting, matcherOut.length ) :\n\t\t\t\t\tmatcherOut\n\t\t\t);\n\t\t\tif ( postFinder ) {\n\t\t\t\tpostFinder( null, results, matcherOut, xml );\n\t\t\t} else {\n\t\t\t\tpush.apply( results, matcherOut );\n\t\t\t}\n\t\t}\n\t} );\n}\n\nfunction matcherFromTokens( tokens ) {\n\tvar checkContext, matcher, j,\n\t\tlen = tokens.length,\n\t\tleadingRelative = Expr.relative[ tokens[ 0 ].type ],\n\t\timplicitRelative = leadingRelative || Expr.relative[ \" \" ],\n\t\ti = leadingRelative ? 1 : 0,\n\n\t\t// The foundational matcher ensures that elements are reachable from top-level context(s)\n\t\tmatchContext = addCombinator( function( elem ) {\n\t\t\treturn elem === checkContext;\n\t\t}, implicitRelative, true ),\n\t\tmatchAnyContext = addCombinator( function( elem ) {\n\t\t\treturn indexOf( checkContext, elem ) > -1;\n\t\t}, implicitRelative, true ),\n\t\tmatchers = [ function( elem, context, xml ) {\n\t\t\tvar ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (\n\t\t\t\t( checkContext = context ).nodeType ?\n\t\t\t\t\tmatchContext( elem, context, xml ) :\n\t\t\t\t\tmatchAnyContext( elem, context, xml ) );\n\n\t\t\t// Avoid hanging onto element (issue #299)\n\t\t\tcheckContext = null;\n\t\t\treturn ret;\n\t\t} ];\n\n\tfor ( ; i < len; i++ ) {\n\t\tif ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {\n\t\t\tmatchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];\n\t\t} else {\n\t\t\tmatcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );\n\n\t\t\t// Return special upon seeing a positional matcher\n\t\t\tif ( matcher[ expando ] ) {\n\n\t\t\t\t// Find the next relative operator (if any) for proper handling\n\t\t\t\tj = ++i;\n\t\t\t\tfor ( ; j < len; j++ ) {\n\t\t\t\t\tif ( Expr.relative[ tokens[ j ].type ] ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn setMatcher(\n\t\t\t\t\ti > 1 && elementMatcher( matchers ),\n\t\t\t\t\ti > 1 && toSelector(\n\n\t\t\t\t\t// If the preceding token was a descendant combinator, insert an implicit any-element `*`\n\t\t\t\t\ttokens\n\t\t\t\t\t\t.slice( 0, i - 1 )\n\t\t\t\t\t\t.concat( { value: tokens[ i - 2 ].type === \" \" ? \"*\" : \"\" } )\n\t\t\t\t\t).replace( rtrim, \"$1\" ),\n\t\t\t\t\tmatcher,\n\t\t\t\t\ti < j && matcherFromTokens( tokens.slice( i, j ) ),\n\t\t\t\t\tj < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),\n\t\t\t\t\tj < len && toSelector( tokens )\n\t\t\t\t);\n\t\t\t}\n\t\t\tmatchers.push( matcher );\n\t\t}\n\t}\n\n\treturn elementMatcher( matchers );\n}\n\nfunction matcherFromGroupMatchers( elementMatchers, setMatchers ) {\n\tvar bySet = setMatchers.length > 0,\n\t\tbyElement = elementMatchers.length > 0,\n\t\tsuperMatcher = function( seed, context, xml, results, outermost ) {\n\t\t\tvar elem, j, matcher,\n\t\t\t\tmatchedCount = 0,\n\t\t\t\ti = \"0\",\n\t\t\t\tunmatched = seed && [],\n\t\t\t\tsetMatched = [],\n\t\t\t\tcontextBackup = outermostContext,\n\n\t\t\t\t// We must always have either seed elements or outermost context\n\t\t\t\telems = seed || byElement && Expr.find[ \"TAG\" ]( \"*\", outermost ),\n\n\t\t\t\t// Use integer dirruns iff this is the outermost matcher\n\t\t\t\tdirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),\n\t\t\t\tlen = elems.length;\n\n\t\t\tif ( outermost ) {\n\n\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\toutermostContext = context == document || context || outermost;\n\t\t\t}\n\n\t\t\t// Add elements passing elementMatchers directly to results\n\t\t\t// Support: IE<9, Safari\n\t\t\t// Tolerate NodeList properties (IE: \"length\"; Safari: <number>) matching elements by id\n\t\t\tfor ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {\n\t\t\t\tif ( byElement && elem ) {\n\t\t\t\t\tj = 0;\n\n\t\t\t\t\t// Support: IE 11+, Edge 17 - 18+\n\t\t\t\t\t// IE/Edge sometimes throw a \"Permission denied\" error when strict-comparing\n\t\t\t\t\t// two documents; shallow comparisons work.\n\t\t\t\t\t// eslint-disable-next-line eqeqeq\n\t\t\t\t\tif ( !context && elem.ownerDocument != document ) {\n\t\t\t\t\t\tsetDocument( elem );\n\t\t\t\t\t\txml = !documentIsHTML;\n\t\t\t\t\t}\n\t\t\t\t\twhile ( ( matcher = elementMatchers[ j++ ] ) ) {\n\t\t\t\t\t\tif ( matcher( elem, context || document, xml ) ) {\n\t\t\t\t\t\t\tresults.push( elem );\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( outermost ) {\n\t\t\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Track unmatched elements for set filters\n\t\t\t\tif ( bySet ) {\n\n\t\t\t\t\t// They will have gone through all possible matchers\n\t\t\t\t\tif ( ( elem = !matcher && elem ) ) {\n\t\t\t\t\t\tmatchedCount--;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Lengthen the array for every element, matched or not\n\t\t\t\t\tif ( seed ) {\n\t\t\t\t\t\tunmatched.push( elem );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// `i` is now the count of elements visited above, and adding it to `matchedCount`\n\t\t\t// makes the latter nonnegative.\n\t\t\tmatchedCount += i;\n\n\t\t\t// Apply set filters to unmatched elements\n\t\t\t// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`\n\t\t\t// equals `i`), unless we didn't visit _any_ elements in the above loop because we have\n\t\t\t// no element matchers and no seed.\n\t\t\t// Incrementing an initially-string \"0\" `i` allows `i` to remain a string only in that\n\t\t\t// case, which will result in a \"00\" `matchedCount` that differs from `i` but is also\n\t\t\t// numerically zero.\n\t\t\tif ( bySet && i !== matchedCount ) {\n\t\t\t\tj = 0;\n\t\t\t\twhile ( ( matcher = setMatchers[ j++ ] ) ) {\n\t\t\t\t\tmatcher( unmatched, setMatched, context, xml );\n\t\t\t\t}\n\n\t\t\t\tif ( seed ) {\n\n\t\t\t\t\t// Reintegrate element matches to eliminate the need for sorting\n\t\t\t\t\tif ( matchedCount > 0 ) {\n\t\t\t\t\t\twhile ( i-- ) {\n\t\t\t\t\t\t\tif ( !( unmatched[ i ] || setMatched[ i ] ) ) {\n\t\t\t\t\t\t\t\tsetMatched[ i ] = pop.call( results );\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// Discard index placeholder values to get only actual matches\n\t\t\t\t\tsetMatched = condense( setMatched );\n\t\t\t\t}\n\n\t\t\t\t// Add matches to results\n\t\t\t\tpush.apply( results, setMatched );\n\n\t\t\t\t// Seedless set matches succeeding multiple successful matchers stipulate sorting\n\t\t\t\tif ( outermost && !seed && setMatched.length > 0 &&\n\t\t\t\t\t( matchedCount + setMatchers.length ) > 1 ) {\n\n\t\t\t\t\tSizzle.uniqueSort( results );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Override manipulation of globals by nested matchers\n\t\t\tif ( outermost ) {\n\t\t\t\tdirruns = dirrunsUnique;\n\t\t\t\toutermostContext = contextBackup;\n\t\t\t}\n\n\t\t\treturn unmatched;\n\t\t};\n\n\treturn bySet ?\n\t\tmarkFunction( superMatcher ) :\n\t\tsuperMatcher;\n}\n\ncompile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {\n\tvar i,\n\t\tsetMatchers = [],\n\t\telementMatchers = [],\n\t\tcached = compilerCache[ selector + \" \" ];\n\n\tif ( !cached ) {\n\n\t\t// Generate a function of recursive functions that can be used to check each element\n\t\tif ( !match ) {\n\t\t\tmatch = tokenize( selector );\n\t\t}\n\t\ti = match.length;\n\t\twhile ( i-- ) {\n\t\t\tcached = matcherFromTokens( match[ i ] );\n\t\t\tif ( cached[ expando ] ) {\n\t\t\t\tsetMatchers.push( cached );\n\t\t\t} else {\n\t\t\t\telementMatchers.push( cached );\n\t\t\t}\n\t\t}\n\n\t\t// Cache the compiled function\n\t\tcached = compilerCache(\n\t\t\tselector,\n\t\t\tmatcherFromGroupMatchers( elementMatchers, setMatchers )\n\t\t);\n\n\t\t// Save selector and tokenization\n\t\tcached.selector = selector;\n\t}\n\treturn cached;\n};\n\n/**\n * A low-level selection function that works with Sizzle's compiled\n *  selector functions\n * @param {String|Function} selector A selector or a pre-compiled\n *  selector function built with Sizzle.compile\n * @param {Element} context\n * @param {Array} [results]\n * @param {Array} [seed] A set of elements to match against\n */\nselect = Sizzle.select = function( selector, context, results, seed ) {\n\tvar i, tokens, token, type, find,\n\t\tcompiled = typeof selector === \"function\" && selector,\n\t\tmatch = !seed && tokenize( ( selector = compiled.selector || selector ) );\n\n\tresults = results || [];\n\n\t// Try to minimize operations if there is only one selector in the list and no seed\n\t// (the latter of which guarantees us context)\n\tif ( match.length === 1 ) {\n\n\t\t// Reduce context if the leading compound selector is an ID\n\t\ttokens = match[ 0 ] = match[ 0 ].slice( 0 );\n\t\tif ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === \"ID\" &&\n\t\t\tcontext.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {\n\n\t\t\tcontext = ( Expr.find[ \"ID\" ]( token.matches[ 0 ]\n\t\t\t\t.replace( runescape, funescape ), context ) || [] )[ 0 ];\n\t\t\tif ( !context ) {\n\t\t\t\treturn results;\n\n\t\t\t// Precompiled matchers will still verify ancestry, so step up a level\n\t\t\t} else if ( compiled ) {\n\t\t\t\tcontext = context.parentNode;\n\t\t\t}\n\n\t\t\tselector = selector.slice( tokens.shift().value.length );\n\t\t}\n\n\t\t// Fetch a seed set for right-to-left matching\n\t\ti = matchExpr[ \"needsContext\" ].test( selector ) ? 0 : tokens.length;\n\t\twhile ( i-- ) {\n\t\t\ttoken = tokens[ i ];\n\n\t\t\t// Abort if we hit a combinator\n\t\t\tif ( Expr.relative[ ( type = token.type ) ] ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( ( find = Expr.find[ type ] ) ) {\n\n\t\t\t\t// Search, expanding context for leading sibling combinators\n\t\t\t\tif ( ( seed = find(\n\t\t\t\t\ttoken.matches[ 0 ].replace( runescape, funescape ),\n\t\t\t\t\trsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||\n\t\t\t\t\t\tcontext\n\t\t\t\t) ) ) {\n\n\t\t\t\t\t// If seed is empty or no tokens remain, we can return early\n\t\t\t\t\ttokens.splice( i, 1 );\n\t\t\t\t\tselector = seed.length && toSelector( tokens );\n\t\t\t\t\tif ( !selector ) {\n\t\t\t\t\t\tpush.apply( results, seed );\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Compile and execute a filtering function if one is not provided\n\t// Provide `match` to avoid retokenization if we modified the selector above\n\t( compiled || compile( selector, match ) )(\n\t\tseed,\n\t\tcontext,\n\t\t!documentIsHTML,\n\t\tresults,\n\t\t!context || rsibling.test( selector ) && testContext( context.parentNode ) || context\n\t);\n\treturn results;\n};\n\n// One-time assignments\n\n// Sort stability\nsupport.sortStable = expando.split( \"\" ).sort( sortOrder ).join( \"\" ) === expando;\n\n// Support: Chrome 14-35+\n// Always assume duplicates if they aren't passed to the comparison function\nsupport.detectDuplicates = !!hasDuplicate;\n\n// Initialize against the default document\nsetDocument();\n\n// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)\n// Detached nodes confoundingly follow *each other*\nsupport.sortDetached = assert( function( el ) {\n\n\t// Should return 1, but returns 4 (following)\n\treturn el.compareDocumentPosition( document.createElement( \"fieldset\" ) ) & 1;\n} );\n\n// Support: IE<8\n// Prevent attribute/property \"interpolation\"\n// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx\nif ( !assert( function( el ) {\n\tel.innerHTML = \"<a href='#'></a>\";\n\treturn el.firstChild.getAttribute( \"href\" ) === \"#\";\n} ) ) {\n\taddHandle( \"type|href|height|width\", function( elem, name, isXML ) {\n\t\tif ( !isXML ) {\n\t\t\treturn elem.getAttribute( name, name.toLowerCase() === \"type\" ? 1 : 2 );\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use defaultValue in place of getAttribute(\"value\")\nif ( !support.attributes || !assert( function( el ) {\n\tel.innerHTML = \"<input/>\";\n\tel.firstChild.setAttribute( \"value\", \"\" );\n\treturn el.firstChild.getAttribute( \"value\" ) === \"\";\n} ) ) {\n\taddHandle( \"value\", function( elem, _name, isXML ) {\n\t\tif ( !isXML && elem.nodeName.toLowerCase() === \"input\" ) {\n\t\t\treturn elem.defaultValue;\n\t\t}\n\t} );\n}\n\n// Support: IE<9\n// Use getAttributeNode to fetch booleans when getAttribute lies\nif ( !assert( function( el ) {\n\treturn el.getAttribute( \"disabled\" ) == null;\n} ) ) {\n\taddHandle( booleans, function( elem, name, isXML ) {\n\t\tvar val;\n\t\tif ( !isXML ) {\n\t\t\treturn elem[ name ] === true ? name.toLowerCase() :\n\t\t\t\t( val = elem.getAttributeNode( name ) ) && val.specified ?\n\t\t\t\t\tval.value :\n\t\t\t\t\tnull;\n\t\t}\n\t} );\n}\n\nreturn Sizzle;\n\n} )( window );\n\n\n\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\n\n// Deprecated\njQuery.expr[ \":\" ] = jQuery.expr.pseudos;\njQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\njQuery.escapeSelector = Sizzle.escape;\n\n\n\n\nvar dir = function( elem, dir, until ) {\n\tvar matched = [],\n\t\ttruncate = until !== undefined;\n\n\twhile ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\tif ( truncate && jQuery( elem ).is( until ) ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatched.push( elem );\n\t\t}\n\t}\n\treturn matched;\n};\n\n\nvar siblings = function( n, elem ) {\n\tvar matched = [];\n\n\tfor ( ; n; n = n.nextSibling ) {\n\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\tmatched.push( n );\n\t\t}\n\t}\n\n\treturn matched;\n};\n\n\nvar rneedsContext = jQuery.expr.match.needsContext;\n\n\n\nfunction nodeName( elem, name ) {\n\n\treturn elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();\n\n}\nvar rsingleTag = ( /^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i );\n\n\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, not ) {\n\tif ( isFunction( qualifier ) ) {\n\t\treturn jQuery.grep( elements, function( elem, i ) {\n\t\t\treturn !!qualifier.call( elem, i, elem ) !== not;\n\t\t} );\n\t}\n\n\t// Single element\n\tif ( qualifier.nodeType ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( elem === qualifier ) !== not;\n\t\t} );\n\t}\n\n\t// Arraylike of elements (jQuery, arguments, Array)\n\tif ( typeof qualifier !== \"string\" ) {\n\t\treturn jQuery.grep( elements, function( elem ) {\n\t\t\treturn ( indexOf.call( qualifier, elem ) > -1 ) !== not;\n\t\t} );\n\t}\n\n\t// Filtered directly for both simple and complex selectors\n\treturn jQuery.filter( qualifier, elements, not );\n}\n\njQuery.filter = function( expr, elems, not ) {\n\tvar elem = elems[ 0 ];\n\n\tif ( not ) {\n\t\texpr = \":not(\" + expr + \")\";\n\t}\n\n\tif ( elems.length === 1 && elem.nodeType === 1 ) {\n\t\treturn jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];\n\t}\n\n\treturn jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {\n\t\treturn elem.nodeType === 1;\n\t} ) );\n};\n\njQuery.fn.extend( {\n\tfind: function( selector ) {\n\t\tvar i, ret,\n\t\t\tlen = this.length,\n\t\t\tself = this;\n\n\t\tif ( typeof selector !== \"string\" ) {\n\t\t\treturn this.pushStack( jQuery( selector ).filter( function() {\n\t\t\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\t\t\tif ( jQuery.contains( self[ i ], this ) ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tret = this.pushStack( [] );\n\n\t\tfor ( i = 0; i < len; i++ ) {\n\t\t\tjQuery.find( selector, self[ i ], ret );\n\t\t}\n\n\t\treturn len > 1 ? jQuery.uniqueSort( ret ) : ret;\n\t},\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], false ) );\n\t},\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow( this, selector || [], true ) );\n\t},\n\tis: function( selector ) {\n\t\treturn !!winnow(\n\t\t\tthis,\n\n\t\t\t// If this is a positional/relative selector, check membership in the returned set\n\t\t\t// so $(\"p:first\").is(\"p:last\") won't return true for a doc with two \"p\".\n\t\t\ttypeof selector === \"string\" && rneedsContext.test( selector ) ?\n\t\t\t\tjQuery( selector ) :\n\t\t\t\tselector || [],\n\t\t\tfalse\n\t\t).length;\n\t}\n} );\n\n\n// Initialize a jQuery object\n\n\n// A central reference to the root jQuery(document)\nvar rootjQuery,\n\n\t// A simple way to check for HTML strings\n\t// Prioritize #id over <tag> to avoid XSS via location.hash (trac-9521)\n\t// Strict HTML recognition (trac-11290: must start with <)\n\t// Shortcut simple #id case for speed\n\trquickExpr = /^(?:\\s*(<[\\w\\W]+>)[^>]*|#([\\w-]+))$/,\n\n\tinit = jQuery.fn.init = function( selector, context, root ) {\n\t\tvar match, elem;\n\n\t\t// HANDLE: $(\"\"), $(null), $(undefined), $(false)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Method init() accepts an alternate rootjQuery\n\t\t// so migrate can support jQuery.sub (gh-2101)\n\t\troot = root || rootjQuery;\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\tif ( selector[ 0 ] === \"<\" &&\n\t\t\t\tselector[ selector.length - 1 ] === \">\" &&\n\t\t\t\tselector.length >= 3 ) {\n\n\t\t\t\t// Assume that strings that start and end with <> are HTML and skip the regex check\n\t\t\t\tmatch = [ null, selector, null ];\n\n\t\t\t} else {\n\t\t\t\tmatch = rquickExpr.exec( selector );\n\t\t\t}\n\n\t\t\t// Match html or make sure no context is specified for #id\n\t\t\tif ( match && ( match[ 1 ] || !context ) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[ 1 ] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[ 0 ] : context;\n\n\t\t\t\t\t// Option to run scripts is true for back-compat\n\t\t\t\t\t// Intentionally let the error be thrown if parseHTML is not present\n\t\t\t\t\tjQuery.merge( this, jQuery.parseHTML(\n\t\t\t\t\t\tmatch[ 1 ],\n\t\t\t\t\t\tcontext && context.nodeType ? context.ownerDocument || context : document,\n\t\t\t\t\t\ttrue\n\t\t\t\t\t) );\n\n\t\t\t\t\t// HANDLE: $(html, props)\n\t\t\t\t\tif ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\tfor ( match in context ) {\n\n\t\t\t\t\t\t\t// Properties of context are called as methods if possible\n\t\t\t\t\t\t\tif ( isFunction( this[ match ] ) ) {\n\t\t\t\t\t\t\t\tthis[ match ]( context[ match ] );\n\n\t\t\t\t\t\t\t// ...and otherwise set as attributes\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.attr( match, context[ match ] );\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\treturn this;\n\n\t\t\t\t// HANDLE: $(#id)\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[ 2 ] );\n\n\t\t\t\t\tif ( elem ) {\n\n\t\t\t\t\t\t// Inject the element directly into the jQuery object\n\t\t\t\t\t\tthis[ 0 ] = elem;\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn ( context || root ).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(DOMElement)\n\t\t} else if ( selector.nodeType ) {\n\t\t\tthis[ 0 ] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( isFunction( selector ) ) {\n\t\t\treturn root.ready !== undefined ?\n\t\t\t\troot.ready( selector ) :\n\n\t\t\t\t// Execute immediately if ready is not present\n\t\t\t\tselector( jQuery );\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t};\n\n// Give the init function the jQuery prototype for later instantiation\ninit.prototype = jQuery.fn;\n\n// Initialize central reference\nrootjQuery = jQuery( document );\n\n\nvar rparentsprev = /^(?:parents|prev(?:Until|All))/,\n\n\t// Methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend( {\n\thas: function( target ) {\n\t\tvar targets = jQuery( target, this ),\n\t\t\tl = targets.length;\n\n\t\treturn this.filter( function() {\n\t\t\tvar i = 0;\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[ i ] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar cur,\n\t\t\ti = 0,\n\t\t\tl = this.length,\n\t\t\tmatched = [],\n\t\t\ttargets = typeof selectors !== \"string\" && jQuery( selectors );\n\n\t\t// Positional selectors never match, since there's no _selection_ context\n\t\tif ( !rneedsContext.test( selectors ) ) {\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tfor ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {\n\n\t\t\t\t\t// Always skip document fragments\n\t\t\t\t\tif ( cur.nodeType < 11 && ( targets ?\n\t\t\t\t\t\ttargets.index( cur ) > -1 :\n\n\t\t\t\t\t\t// Don't pass non-elements to Sizzle\n\t\t\t\t\t\tcur.nodeType === 1 &&\n\t\t\t\t\t\t\tjQuery.find.matchesSelector( cur, selectors ) ) ) {\n\n\t\t\t\t\t\tmatched.push( cur );\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );\n\t},\n\n\t// Determine the position of an element within the set\n\tindex: function( elem ) {\n\n\t\t// No argument, return index in parent\n\t\tif ( !elem ) {\n\t\t\treturn ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;\n\t\t}\n\n\t\t// Index in selector\n\t\tif ( typeof elem === \"string\" ) {\n\t\t\treturn indexOf.call( jQuery( elem ), this[ 0 ] );\n\t\t}\n\n\t\t// Locate the position of the desired element\n\t\treturn indexOf.call( this,\n\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[ 0 ] : elem\n\t\t);\n\t},\n\n\tadd: function( selector, context ) {\n\t\treturn this.pushStack(\n\t\t\tjQuery.uniqueSort(\n\t\t\t\tjQuery.merge( this.get(), jQuery( selector, context ) )\n\t\t\t)\n\t\t);\n\t},\n\n\taddBack: function( selector ) {\n\t\treturn this.add( selector == null ?\n\t\t\tthis.prevObject : this.prevObject.filter( selector )\n\t\t);\n\t}\n} );\n\nfunction sibling( cur, dir ) {\n\twhile ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}\n\treturn cur;\n}\n\njQuery.each( {\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn sibling( elem, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn sibling( elem, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, _i, until ) {\n\t\treturn dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn siblings( ( elem.parentNode || {} ).firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn siblings( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\tif ( elem.contentDocument != null &&\n\n\t\t\t// Support: IE 11+\n\t\t\t// <object> elements with no `data` attribute has an object\n\t\t\t// `contentDocument` with a `null` prototype.\n\t\t\tgetProto( elem.contentDocument ) ) {\n\n\t\t\treturn elem.contentDocument;\n\t\t}\n\n\t\t// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only\n\t\t// Treat the template element as a regular one in browsers that\n\t\t// don't support it.\n\t\tif ( nodeName( elem, \"template\" ) ) {\n\t\t\telem = elem.content || elem;\n\t\t}\n\n\t\treturn jQuery.merge( [], elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar matched = jQuery.map( this, fn, until );\n\n\t\tif ( name.slice( -5 ) !== \"Until\" ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tmatched = jQuery.filter( selector, matched );\n\t\t}\n\n\t\tif ( this.length > 1 ) {\n\n\t\t\t// Remove duplicates\n\t\t\tif ( !guaranteedUnique[ name ] ) {\n\t\t\t\tjQuery.uniqueSort( matched );\n\t\t\t}\n\n\t\t\t// Reverse order for parents* and prev-derivatives\n\t\t\tif ( rparentsprev.test( name ) ) {\n\t\t\t\tmatched.reverse();\n\t\t\t}\n\t\t}\n\n\t\treturn this.pushStack( matched );\n\t};\n} );\nvar rnothtmlwhite = ( /[^\\x20\\t\\r\\n\\f]+/g );\n\n\n\n// Convert String-formatted options into Object-formatted ones\nfunction createOptions( options ) {\n\tvar object = {};\n\tjQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {\n\t\tobject[ flag ] = true;\n\t} );\n\treturn object;\n}\n\n/*\n * Create a callback list using the following parameters:\n *\n *\toptions: an optional list of space-separated options that will change how\n *\t\t\tthe callback list behaves or a more traditional option object\n *\n * By default a callback list will act like an event callback list and can be\n * \"fired\" multiple times.\n *\n * Possible options:\n *\n *\tonce:\t\t\twill ensure the callback list can only be fired once (like a Deferred)\n *\n *\tmemory:\t\t\twill keep track of previous values and will call any callback added\n *\t\t\t\t\tafter the list has been fired right away with the latest \"memorized\"\n *\t\t\t\t\tvalues (like a Deferred)\n *\n *\tunique:\t\t\twill ensure a callback can only be added once (no duplicate in the list)\n *\n *\tstopOnFalse:\tinterrupt callings when a callback returns false\n *\n */\njQuery.Callbacks = function( options ) {\n\n\t// Convert options from String-formatted to Object-formatted if needed\n\t// (we check in cache first)\n\toptions = typeof options === \"string\" ?\n\t\tcreateOptions( options ) :\n\t\tjQuery.extend( {}, options );\n\n\tvar // Flag to know if list is currently firing\n\t\tfiring,\n\n\t\t// Last fire value for non-forgettable lists\n\t\tmemory,\n\n\t\t// Flag to know if list was already fired\n\t\tfired,\n\n\t\t// Flag to prevent firing\n\t\tlocked,\n\n\t\t// Actual callback list\n\t\tlist = [],\n\n\t\t// Queue of execution data for repeatable lists\n\t\tqueue = [],\n\n\t\t// Index of currently firing callback (modified by add/remove as needed)\n\t\tfiringIndex = -1,\n\n\t\t// Fire callbacks\n\t\tfire = function() {\n\n\t\t\t// Enforce single-firing\n\t\t\tlocked = locked || options.once;\n\n\t\t\t// Execute callbacks for all pending executions,\n\t\t\t// respecting firingIndex overrides and runtime changes\n\t\t\tfired = firing = true;\n\t\t\tfor ( ; queue.length; firingIndex = -1 ) {\n\t\t\t\tmemory = queue.shift();\n\t\t\t\twhile ( ++firingIndex < list.length ) {\n\n\t\t\t\t\t// Run callback and check for early termination\n\t\t\t\t\tif ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&\n\t\t\t\t\t\toptions.stopOnFalse ) {\n\n\t\t\t\t\t\t// Jump to end and forget the data so .add doesn't re-fire\n\t\t\t\t\t\tfiringIndex = list.length;\n\t\t\t\t\t\tmemory = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Forget the data if we're done with it\n\t\t\tif ( !options.memory ) {\n\t\t\t\tmemory = false;\n\t\t\t}\n\n\t\t\tfiring = false;\n\n\t\t\t// Clean up if we're done firing for good\n\t\t\tif ( locked ) {\n\n\t\t\t\t// Keep an empty list if we have data for future add calls\n\t\t\t\tif ( memory ) {\n\t\t\t\t\tlist = [];\n\n\t\t\t\t// Otherwise, this object is spent\n\t\t\t\t} else {\n\t\t\t\t\tlist = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t// Actual Callbacks object\n\t\tself = {\n\n\t\t\t// Add a callback or a collection of callbacks to the list\n\t\t\tadd: function() {\n\t\t\t\tif ( list ) {\n\n\t\t\t\t\t// If we have memory from a past run, we should fire after adding\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfiringIndex = list.length - 1;\n\t\t\t\t\t\tqueue.push( memory );\n\t\t\t\t\t}\n\n\t\t\t\t\t( function add( args ) {\n\t\t\t\t\t\tjQuery.each( args, function( _, arg ) {\n\t\t\t\t\t\t\tif ( isFunction( arg ) ) {\n\t\t\t\t\t\t\t\tif ( !options.unique || !self.has( arg ) ) {\n\t\t\t\t\t\t\t\t\tlist.push( arg );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( arg && arg.length && toType( arg ) !== \"string\" ) {\n\n\t\t\t\t\t\t\t\t// Inspect recursively\n\t\t\t\t\t\t\t\tadd( arg );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} );\n\t\t\t\t\t} )( arguments );\n\n\t\t\t\t\tif ( memory && !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Remove a callback from the list\n\t\t\tremove: function() {\n\t\t\t\tjQuery.each( arguments, function( _, arg ) {\n\t\t\t\t\tvar index;\n\t\t\t\t\twhile ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {\n\t\t\t\t\t\tlist.splice( index, 1 );\n\n\t\t\t\t\t\t// Handle firing indexes\n\t\t\t\t\t\tif ( index <= firingIndex ) {\n\t\t\t\t\t\t\tfiringIndex--;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Check if a given callback is in the list.\n\t\t\t// If no argument is given, return whether or not list has callbacks attached.\n\t\t\thas: function( fn ) {\n\t\t\t\treturn fn ?\n\t\t\t\t\tjQuery.inArray( fn, list ) > -1 :\n\t\t\t\t\tlist.length > 0;\n\t\t\t},\n\n\t\t\t// Remove all callbacks from the list\n\t\t\tempty: function() {\n\t\t\t\tif ( list ) {\n\t\t\t\t\tlist = [];\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Disable .fire and .add\n\t\t\t// Abort any current/pending executions\n\t\t\t// Clear all callbacks and values\n\t\t\tdisable: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tlist = memory = \"\";\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tdisabled: function() {\n\t\t\t\treturn !list;\n\t\t\t},\n\n\t\t\t// Disable .fire\n\t\t\t// Also disable .add unless we have memory (since it would have no effect)\n\t\t\t// Abort any pending executions\n\t\t\tlock: function() {\n\t\t\t\tlocked = queue = [];\n\t\t\t\tif ( !memory && !firing ) {\n\t\t\t\t\tlist = memory = \"\";\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tlocked: function() {\n\t\t\t\treturn !!locked;\n\t\t\t},\n\n\t\t\t// Call all callbacks with the given context and arguments\n\t\t\tfireWith: function( context, args ) {\n\t\t\t\tif ( !locked ) {\n\t\t\t\t\targs = args || [];\n\t\t\t\t\targs = [ context, args.slice ? args.slice() : args ];\n\t\t\t\t\tqueue.push( args );\n\t\t\t\t\tif ( !firing ) {\n\t\t\t\t\t\tfire();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// Call all the callbacks with the given arguments\n\t\t\tfire: function() {\n\t\t\t\tself.fireWith( this, arguments );\n\t\t\t\treturn this;\n\t\t\t},\n\n\t\t\t// To know if the callbacks have already been called at least once\n\t\t\tfired: function() {\n\t\t\t\treturn !!fired;\n\t\t\t}\n\t\t};\n\n\treturn self;\n};\n\n\nfunction Identity( v ) {\n\treturn v;\n}\nfunction Thrower( ex ) {\n\tthrow ex;\n}\n\nfunction adoptValue( value, resolve, reject, noValue ) {\n\tvar method;\n\n\ttry {\n\n\t\t// Check for promise aspect first to privilege synchronous behavior\n\t\tif ( value && isFunction( ( method = value.promise ) ) ) {\n\t\t\tmethod.call( value ).done( resolve ).fail( reject );\n\n\t\t// Other thenables\n\t\t} else if ( value && isFunction( ( method = value.then ) ) ) {\n\t\t\tmethod.call( value, resolve, reject );\n\n\t\t// Other non-thenables\n\t\t} else {\n\n\t\t\t// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:\n\t\t\t// * false: [ value ].slice( 0 ) => resolve( value )\n\t\t\t// * true: [ value ].slice( 1 ) => resolve()\n\t\t\tresolve.apply( undefined, [ value ].slice( noValue ) );\n\t\t}\n\n\t// For Promises/A+, convert exceptions into rejections\n\t// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in\n\t// Deferred#then to conditionally suppress rejection.\n\t} catch ( value ) {\n\n\t\t// Support: Android 4.0 only\n\t\t// Strict mode functions invoked without .call/.apply get global-object context\n\t\treject.apply( undefined, [ value ] );\n\t}\n}\n\njQuery.extend( {\n\n\tDeferred: function( func ) {\n\t\tvar tuples = [\n\n\t\t\t\t// action, add listener, callbacks,\n\t\t\t\t// ... .then handlers, argument index, [final state]\n\t\t\t\t[ \"notify\", \"progress\", jQuery.Callbacks( \"memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"memory\" ), 2 ],\n\t\t\t\t[ \"resolve\", \"done\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 0, \"resolved\" ],\n\t\t\t\t[ \"reject\", \"fail\", jQuery.Callbacks( \"once memory\" ),\n\t\t\t\t\tjQuery.Callbacks( \"once memory\" ), 1, \"rejected\" ]\n\t\t\t],\n\t\t\tstate = \"pending\",\n\t\t\tpromise = {\n\t\t\t\tstate: function() {\n\t\t\t\t\treturn state;\n\t\t\t\t},\n\t\t\t\talways: function() {\n\t\t\t\t\tdeferred.done( arguments ).fail( arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\t\t\t\t\"catch\": function( fn ) {\n\t\t\t\t\treturn promise.then( null, fn );\n\t\t\t\t},\n\n\t\t\t\t// Keep pipe for back-compat\n\t\t\t\tpipe: function( /* fnDone, fnFail, fnProgress */ ) {\n\t\t\t\t\tvar fns = arguments;\n\n\t\t\t\t\treturn jQuery.Deferred( function( newDefer ) {\n\t\t\t\t\t\tjQuery.each( tuples, function( _i, tuple ) {\n\n\t\t\t\t\t\t\t// Map tuples (progress, done, fail) to arguments (done, fail, progress)\n\t\t\t\t\t\t\tvar fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];\n\n\t\t\t\t\t\t\t// deferred.progress(function() { bind to newDefer or newDefer.notify })\n\t\t\t\t\t\t\t// deferred.done(function() { bind to newDefer or newDefer.resolve })\n\t\t\t\t\t\t\t// deferred.fail(function() { bind to newDefer or newDefer.reject })\n\t\t\t\t\t\t\tdeferred[ tuple[ 1 ] ]( function() {\n\t\t\t\t\t\t\t\tvar returned = fn && fn.apply( this, arguments );\n\t\t\t\t\t\t\t\tif ( returned && isFunction( returned.promise ) ) {\n\t\t\t\t\t\t\t\t\treturned.promise()\n\t\t\t\t\t\t\t\t\t\t.progress( newDefer.notify )\n\t\t\t\t\t\t\t\t\t\t.done( newDefer.resolve )\n\t\t\t\t\t\t\t\t\t\t.fail( newDefer.reject );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tnewDefer[ tuple[ 0 ] + \"With\" ](\n\t\t\t\t\t\t\t\t\t\tthis,\n\t\t\t\t\t\t\t\t\t\tfn ? [ returned ] : arguments\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t} );\n\t\t\t\t\t\tfns = null;\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\t\t\t\tthen: function( onFulfilled, onRejected, onProgress ) {\n\t\t\t\t\tvar maxDepth = 0;\n\t\t\t\t\tfunction resolve( depth, deferred, handler, special ) {\n\t\t\t\t\t\treturn function() {\n\t\t\t\t\t\t\tvar that = this,\n\t\t\t\t\t\t\t\targs = arguments,\n\t\t\t\t\t\t\t\tmightThrow = function() {\n\t\t\t\t\t\t\t\t\tvar returned, then;\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.3\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-59\n\t\t\t\t\t\t\t\t\t// Ignore double-resolution attempts\n\t\t\t\t\t\t\t\t\tif ( depth < maxDepth ) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\treturned = handler.apply( that, args );\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.1\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-48\n\t\t\t\t\t\t\t\t\tif ( returned === deferred.promise() ) {\n\t\t\t\t\t\t\t\t\t\tthrow new TypeError( \"Thenable self-resolution\" );\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Support: Promises/A+ sections 2.3.3.1, 3.5\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-54\n\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-75\n\t\t\t\t\t\t\t\t\t// Retrieve `then` only once\n\t\t\t\t\t\t\t\t\tthen = returned &&\n\n\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.4\n\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-64\n\t\t\t\t\t\t\t\t\t\t// Only check objects and functions for thenability\n\t\t\t\t\t\t\t\t\t\t( typeof returned === \"object\" ||\n\t\t\t\t\t\t\t\t\t\t\ttypeof returned === \"function\" ) &&\n\t\t\t\t\t\t\t\t\t\treturned.then;\n\n\t\t\t\t\t\t\t\t\t// Handle a returned thenable\n\t\t\t\t\t\t\t\t\tif ( isFunction( then ) ) {\n\n\t\t\t\t\t\t\t\t\t\t// Special processors (notify) just wait for resolution\n\t\t\t\t\t\t\t\t\t\tif ( special ) {\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special )\n\t\t\t\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\t\t\t// Normal processors (resolve) also hook into progress\n\t\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t\t// ...and disregard older resolution values\n\t\t\t\t\t\t\t\t\t\t\tmaxDepth++;\n\n\t\t\t\t\t\t\t\t\t\t\tthen.call(\n\t\t\t\t\t\t\t\t\t\t\t\treturned,\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Thrower, special ),\n\t\t\t\t\t\t\t\t\t\t\t\tresolve( maxDepth, deferred, Identity,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdeferred.notifyWith )\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Handle all other returned values\n\t\t\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\tif ( handler !== Identity ) {\n\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\targs = [ returned ];\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t// Process the value(s)\n\t\t\t\t\t\t\t\t\t\t// Default process is resolve\n\t\t\t\t\t\t\t\t\t\t( special || deferred.resolveWith )( that, args );\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\n\t\t\t\t\t\t\t\t// Only normal processors (resolve) catch and reject exceptions\n\t\t\t\t\t\t\t\tprocess = special ?\n\t\t\t\t\t\t\t\t\tmightThrow :\n\t\t\t\t\t\t\t\t\tfunction() {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tmightThrow();\n\t\t\t\t\t\t\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t\t\t\t\t\t\tif ( jQuery.Deferred.exceptionHook ) {\n\t\t\t\t\t\t\t\t\t\t\t\tjQuery.Deferred.exceptionHook( e,\n\t\t\t\t\t\t\t\t\t\t\t\t\tprocess.stackTrace );\n\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.4.1\n\t\t\t\t\t\t\t\t\t\t\t// https://promisesaplus.com/#point-61\n\t\t\t\t\t\t\t\t\t\t\t// Ignore post-resolution exceptions\n\t\t\t\t\t\t\t\t\t\t\tif ( depth + 1 >= maxDepth ) {\n\n\t\t\t\t\t\t\t\t\t\t\t\t// Only substitute handlers pass on context\n\t\t\t\t\t\t\t\t\t\t\t\t// and multiple values (non-spec behavior)\n\t\t\t\t\t\t\t\t\t\t\t\tif ( handler !== Thrower ) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tthat = undefined;\n\t\t\t\t\t\t\t\t\t\t\t\t\targs = [ e ];\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\t\t\tdeferred.rejectWith( that, args );\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Support: Promises/A+ section 2.3.3.3.1\n\t\t\t\t\t\t\t// https://promisesaplus.com/#point-57\n\t\t\t\t\t\t\t// Re-resolve promises immediately to dodge false rejection from\n\t\t\t\t\t\t\t// subsequent errors\n\t\t\t\t\t\t\tif ( depth ) {\n\t\t\t\t\t\t\t\tprocess();\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// Call an optional hook to record the stack, in case of exception\n\t\t\t\t\t\t\t\t// since it's otherwise lost when execution goes async\n\t\t\t\t\t\t\t\tif ( jQuery.Deferred.getStackHook ) {\n\t\t\t\t\t\t\t\t\tprocess.stackTrace = jQuery.Deferred.getStackHook();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\twindow.setTimeout( process );\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\treturn jQuery.Deferred( function( newDefer ) {\n\n\t\t\t\t\t\t// progress_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 0 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onProgress ) ?\n\t\t\t\t\t\t\t\t\tonProgress :\n\t\t\t\t\t\t\t\t\tIdentity,\n\t\t\t\t\t\t\t\tnewDefer.notifyWith\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// fulfilled_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 1 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onFulfilled ) ?\n\t\t\t\t\t\t\t\t\tonFulfilled :\n\t\t\t\t\t\t\t\t\tIdentity\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// rejected_handlers.add( ... )\n\t\t\t\t\t\ttuples[ 2 ][ 3 ].add(\n\t\t\t\t\t\t\tresolve(\n\t\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t\tnewDefer,\n\t\t\t\t\t\t\t\tisFunction( onRejected ) ?\n\t\t\t\t\t\t\t\t\tonRejected :\n\t\t\t\t\t\t\t\t\tThrower\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} ).promise();\n\t\t\t\t},\n\n\t\t\t\t// Get a promise for this deferred\n\t\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\t\tpromise: function( obj ) {\n\t\t\t\t\treturn obj != null ? jQuery.extend( obj, promise ) : promise;\n\t\t\t\t}\n\t\t\t},\n\t\t\tdeferred = {};\n\n\t\t// Add list-specific methods\n\t\tjQuery.each( tuples, function( i, tuple ) {\n\t\t\tvar list = tuple[ 2 ],\n\t\t\t\tstateString = tuple[ 5 ];\n\n\t\t\t// promise.progress = list.add\n\t\t\t// promise.done = list.add\n\t\t\t// promise.fail = list.add\n\t\t\tpromise[ tuple[ 1 ] ] = list.add;\n\n\t\t\t// Handle state\n\t\t\tif ( stateString ) {\n\t\t\t\tlist.add(\n\t\t\t\t\tfunction() {\n\n\t\t\t\t\t\t// state = \"resolved\" (i.e., fulfilled)\n\t\t\t\t\t\t// state = \"rejected\"\n\t\t\t\t\t\tstate = stateString;\n\t\t\t\t\t},\n\n\t\t\t\t\t// rejected_callbacks.disable\n\t\t\t\t\t// fulfilled_callbacks.disable\n\t\t\t\t\ttuples[ 3 - i ][ 2 ].disable,\n\n\t\t\t\t\t// rejected_handlers.disable\n\t\t\t\t\t// fulfilled_handlers.disable\n\t\t\t\t\ttuples[ 3 - i ][ 3 ].disable,\n\n\t\t\t\t\t// progress_callbacks.lock\n\t\t\t\t\ttuples[ 0 ][ 2 ].lock,\n\n\t\t\t\t\t// progress_handlers.lock\n\t\t\t\t\ttuples[ 0 ][ 3 ].lock\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// progress_handlers.fire\n\t\t\t// fulfilled_handlers.fire\n\t\t\t// rejected_handlers.fire\n\t\t\tlist.add( tuple[ 3 ].fire );\n\n\t\t\t// deferred.notify = function() { deferred.notifyWith(...) }\n\t\t\t// deferred.resolve = function() { deferred.resolveWith(...) }\n\t\t\t// deferred.reject = function() { deferred.rejectWith(...) }\n\t\t\tdeferred[ tuple[ 0 ] ] = function() {\n\t\t\t\tdeferred[ tuple[ 0 ] + \"With\" ]( this === deferred ? undefined : this, arguments );\n\t\t\t\treturn this;\n\t\t\t};\n\n\t\t\t// deferred.notifyWith = list.fireWith\n\t\t\t// deferred.resolveWith = list.fireWith\n\t\t\t// deferred.rejectWith = list.fireWith\n\t\t\tdeferred[ tuple[ 0 ] + \"With\" ] = list.fireWith;\n\t\t} );\n\n\t\t// Make the deferred a promise\n\t\tpromise.promise( deferred );\n\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\n\t\t// All done!\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( singleValue ) {\n\t\tvar\n\n\t\t\t// count of uncompleted subordinates\n\t\t\tremaining = arguments.length,\n\n\t\t\t// count of unprocessed arguments\n\t\t\ti = remaining,\n\n\t\t\t// subordinate fulfillment data\n\t\t\tresolveContexts = Array( i ),\n\t\t\tresolveValues = slice.call( arguments ),\n\n\t\t\t// the primary Deferred\n\t\t\tprimary = jQuery.Deferred(),\n\n\t\t\t// subordinate callback factory\n\t\t\tupdateFunc = function( i ) {\n\t\t\t\treturn function( value ) {\n\t\t\t\t\tresolveContexts[ i ] = this;\n\t\t\t\t\tresolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;\n\t\t\t\t\tif ( !( --remaining ) ) {\n\t\t\t\t\t\tprimary.resolveWith( resolveContexts, resolveValues );\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t};\n\n\t\t// Single- and empty arguments are adopted like Promise.resolve\n\t\tif ( remaining <= 1 ) {\n\t\t\tadoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,\n\t\t\t\t!remaining );\n\n\t\t\t// Use .then() to unwrap secondary thenables (cf. gh-3000)\n\t\t\tif ( primary.state() === \"pending\" ||\n\t\t\t\tisFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {\n\n\t\t\t\treturn primary.then();\n\t\t\t}\n\t\t}\n\n\t\t// Multiple arguments are aggregated like Promise.all array elements\n\t\twhile ( i-- ) {\n\t\t\tadoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );\n\t\t}\n\n\t\treturn primary.promise();\n\t}\n} );\n\n\n// These usually indicate a programmer mistake during development,\n// warn about them ASAP rather than swallowing them by default.\nvar rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;\n\njQuery.Deferred.exceptionHook = function( error, stack ) {\n\n\t// Support: IE 8 - 9 only\n\t// Console exists when dev tools are open, which can happen at any time\n\tif ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {\n\t\twindow.console.warn( \"jQuery.Deferred exception: \" + error.message, error.stack, stack );\n\t}\n};\n\n\n\n\njQuery.readyException = function( error ) {\n\twindow.setTimeout( function() {\n\t\tthrow error;\n\t} );\n};\n\n\n\n\n// The deferred used on DOM ready\nvar readyList = jQuery.Deferred();\n\njQuery.fn.ready = function( fn ) {\n\n\treadyList\n\t\t.then( fn )\n\n\t\t// Wrap jQuery.readyException in a function so that the lookup\n\t\t// happens at the time of error handling instead of callback\n\t\t// registration.\n\t\t.catch( function( error ) {\n\t\t\tjQuery.readyException( error );\n\t\t} );\n\n\treturn this;\n};\n\njQuery.extend( {\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See trac-6781\n\treadyWait: 1,\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\n\t\t// Abort if there are pending holds or we're already ready\n\t\tif ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remember that the DOM is ready\n\t\tjQuery.isReady = true;\n\n\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If there are functions bound, to execute\n\t\treadyList.resolveWith( document, [ jQuery ] );\n\t}\n} );\n\njQuery.ready.then = readyList.then;\n\n// The ready event handler and self cleanup method\nfunction completed() {\n\tdocument.removeEventListener( \"DOMContentLoaded\", completed );\n\twindow.removeEventListener( \"load\", completed );\n\tjQuery.ready();\n}\n\n// Catch cases where $(document).ready() is called\n// after the browser event has already occurred.\n// Support: IE <=9 - 10 only\n// Older IE sometimes signals \"interactive\" too soon\nif ( document.readyState === \"complete\" ||\n\t( document.readyState !== \"loading\" && !document.documentElement.doScroll ) ) {\n\n\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\twindow.setTimeout( jQuery.ready );\n\n} else {\n\n\t// Use the handy event callback\n\tdocument.addEventListener( \"DOMContentLoaded\", completed );\n\n\t// A fallback to window.onload, that will always work\n\twindow.addEventListener( \"load\", completed );\n}\n\n\n\n\n// Multifunctional method to get and set values of a collection\n// The value/s can optionally be executed if it's a function\nvar access = function( elems, fn, key, value, chainable, emptyGet, raw ) {\n\tvar i = 0,\n\t\tlen = elems.length,\n\t\tbulk = key == null;\n\n\t// Sets many values\n\tif ( toType( key ) === \"object\" ) {\n\t\tchainable = true;\n\t\tfor ( i in key ) {\n\t\t\taccess( elems, fn, i, key[ i ], true, emptyGet, raw );\n\t\t}\n\n\t// Sets one value\n\t} else if ( value !== undefined ) {\n\t\tchainable = true;\n\n\t\tif ( !isFunction( value ) ) {\n\t\t\traw = true;\n\t\t}\n\n\t\tif ( bulk ) {\n\n\t\t\t// Bulk operations run against the entire set\n\t\t\tif ( raw ) {\n\t\t\t\tfn.call( elems, value );\n\t\t\t\tfn = null;\n\n\t\t\t// ...except when executing function values\n\t\t\t} else {\n\t\t\t\tbulk = fn;\n\t\t\t\tfn = function( elem, _key, value ) {\n\t\t\t\t\treturn bulk.call( jQuery( elem ), value );\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\tfn(\n\t\t\t\t\telems[ i ], key, raw ?\n\t\t\t\t\t\tvalue :\n\t\t\t\t\t\tvalue.call( elems[ i ], i, fn( elems[ i ], key ) )\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( chainable ) {\n\t\treturn elems;\n\t}\n\n\t// Gets\n\tif ( bulk ) {\n\t\treturn fn.call( elems );\n\t}\n\n\treturn len ? fn( elems[ 0 ], key ) : emptyGet;\n};\n\n\n// Matches dashed string for camelizing\nvar rmsPrefix = /^-ms-/,\n\trdashAlpha = /-([a-z])/g;\n\n// Used by camelCase as callback to replace()\nfunction fcamelCase( _all, letter ) {\n\treturn letter.toUpperCase();\n}\n\n// Convert dashed to camelCase; used by the css and data modules\n// Support: IE <=9 - 11, Edge 12 - 15\n// Microsoft forgot to hump their vendor prefix (trac-9572)\nfunction camelCase( string ) {\n\treturn string.replace( rmsPrefix, \"ms-\" ).replace( rdashAlpha, fcamelCase );\n}\nvar acceptData = function( owner ) {\n\n\t// Accepts only:\n\t//  - Node\n\t//    - Node.ELEMENT_NODE\n\t//    - Node.DOCUMENT_NODE\n\t//  - Object\n\t//    - Any\n\treturn owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );\n};\n\n\n\n\nfunction Data() {\n\tthis.expando = jQuery.expando + Data.uid++;\n}\n\nData.uid = 1;\n\nData.prototype = {\n\n\tcache: function( owner ) {\n\n\t\t// Check if the owner object already has a cache\n\t\tvar value = owner[ this.expando ];\n\n\t\t// If not, create one\n\t\tif ( !value ) {\n\t\t\tvalue = {};\n\n\t\t\t// We can accept data for non-element nodes in modern browsers,\n\t\t\t// but we should not, see trac-8335.\n\t\t\t// Always return an empty object.\n\t\t\tif ( acceptData( owner ) ) {\n\n\t\t\t\t// If it is a node unlikely to be stringify-ed or looped over\n\t\t\t\t// use plain assignment\n\t\t\t\tif ( owner.nodeType ) {\n\t\t\t\t\towner[ this.expando ] = value;\n\n\t\t\t\t// Otherwise secure it in a non-enumerable property\n\t\t\t\t// configurable must be true to allow the property to be\n\t\t\t\t// deleted when data is removed\n\t\t\t\t} else {\n\t\t\t\t\tObject.defineProperty( owner, this.expando, {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tconfigurable: true\n\t\t\t\t\t} );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn value;\n\t},\n\tset: function( owner, data, value ) {\n\t\tvar prop,\n\t\t\tcache = this.cache( owner );\n\n\t\t// Handle: [ owner, key, value ] args\n\t\t// Always use camelCase key (gh-2257)\n\t\tif ( typeof data === \"string\" ) {\n\t\t\tcache[ camelCase( data ) ] = value;\n\n\t\t// Handle: [ owner, { properties } ] args\n\t\t} else {\n\n\t\t\t// Copy the properties one-by-one to the cache object\n\t\t\tfor ( prop in data ) {\n\t\t\t\tcache[ camelCase( prop ) ] = data[ prop ];\n\t\t\t}\n\t\t}\n\t\treturn cache;\n\t},\n\tget: function( owner, key ) {\n\t\treturn key === undefined ?\n\t\t\tthis.cache( owner ) :\n\n\t\t\t// Always use camelCase key (gh-2257)\n\t\t\towner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];\n\t},\n\taccess: function( owner, key, value ) {\n\n\t\t// In cases where either:\n\t\t//\n\t\t//   1. No key was specified\n\t\t//   2. A string key was specified, but no value provided\n\t\t//\n\t\t// Take the \"read\" path and allow the get method to determine\n\t\t// which value to return, respectively either:\n\t\t//\n\t\t//   1. The entire cache object\n\t\t//   2. The data stored at the key\n\t\t//\n\t\tif ( key === undefined ||\n\t\t\t\t( ( key && typeof key === \"string\" ) && value === undefined ) ) {\n\n\t\t\treturn this.get( owner, key );\n\t\t}\n\n\t\t// When the key is not a string, or both a key and value\n\t\t// are specified, set or extend (existing objects) with either:\n\t\t//\n\t\t//   1. An object of properties\n\t\t//   2. A key and value\n\t\t//\n\t\tthis.set( owner, key, value );\n\n\t\t// Since the \"set\" path can have two possible entry points\n\t\t// return the expected data based on which path was taken[*]\n\t\treturn value !== undefined ? value : key;\n\t},\n\tremove: function( owner, key ) {\n\t\tvar i,\n\t\t\tcache = owner[ this.expando ];\n\n\t\tif ( cache === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( key !== undefined ) {\n\n\t\t\t// Support array or space separated string of keys\n\t\t\tif ( Array.isArray( key ) ) {\n\n\t\t\t\t// If key is an array of keys...\n\t\t\t\t// We always set camelCase keys, so remove that.\n\t\t\t\tkey = key.map( camelCase );\n\t\t\t} else {\n\t\t\t\tkey = camelCase( key );\n\n\t\t\t\t// If a key with the spaces exists, use it.\n\t\t\t\t// Otherwise, create an array by matching non-whitespace\n\t\t\t\tkey = key in cache ?\n\t\t\t\t\t[ key ] :\n\t\t\t\t\t( key.match( rnothtmlwhite ) || [] );\n\t\t\t}\n\n\t\t\ti = key.length;\n\n\t\t\twhile ( i-- ) {\n\t\t\t\tdelete cache[ key[ i ] ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if there's no more data\n\t\tif ( key === undefined || jQuery.isEmptyObject( cache ) ) {\n\n\t\t\t// Support: Chrome <=35 - 45\n\t\t\t// Webkit & Blink performance suffers when deleting properties\n\t\t\t// from DOM nodes, so set to undefined instead\n\t\t\t// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)\n\t\t\tif ( owner.nodeType ) {\n\t\t\t\towner[ this.expando ] = undefined;\n\t\t\t} else {\n\t\t\t\tdelete owner[ this.expando ];\n\t\t\t}\n\t\t}\n\t},\n\thasData: function( owner ) {\n\t\tvar cache = owner[ this.expando ];\n\t\treturn cache !== undefined && !jQuery.isEmptyObject( cache );\n\t}\n};\nvar dataPriv = new Data();\n\nvar dataUser = new Data();\n\n\n\n//\tImplementation Summary\n//\n//\t1. Enforce API surface and semantic compatibility with 1.9.x branch\n//\t2. Improve the module's maintainability by reducing the storage\n//\t\tpaths to a single mechanism.\n//\t3. Use the same single mechanism to support \"private\" and \"user\" data.\n//\t4. _Never_ expose \"private\" data to user code (TODO: Drop _data, _removeData)\n//\t5. Avoid exposing implementation details on user objects (eg. expando properties)\n//\t6. Provide a clear path for implementation upgrade to WeakMap in 2014\n\nvar rbrace = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/,\n\trmultiDash = /[A-Z]/g;\n\nfunction getData( data ) {\n\tif ( data === \"true\" ) {\n\t\treturn true;\n\t}\n\n\tif ( data === \"false\" ) {\n\t\treturn false;\n\t}\n\n\tif ( data === \"null\" ) {\n\t\treturn null;\n\t}\n\n\t// Only convert to a number if it doesn't change the string\n\tif ( data === +data + \"\" ) {\n\t\treturn +data;\n\t}\n\n\tif ( rbrace.test( data ) ) {\n\t\treturn JSON.parse( data );\n\t}\n\n\treturn data;\n}\n\nfunction dataAttr( elem, key, data ) {\n\tvar name;\n\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tname = \"data-\" + key.replace( rmultiDash, \"-$&\" ).toLowerCase();\n\t\tdata = elem.getAttribute( name );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = getData( data );\n\t\t\t} catch ( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tdataUser.set( elem, key, data );\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\treturn data;\n}\n\njQuery.extend( {\n\thasData: function( elem ) {\n\t\treturn dataUser.hasData( elem ) || dataPriv.hasData( elem );\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\treturn dataUser.access( elem, name, data );\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tdataUser.remove( elem, name );\n\t},\n\n\t// TODO: Now that all calls to _data and _removeData have been replaced\n\t// with direct calls to dataPriv methods, these can be deprecated.\n\t_data: function( elem, name, data ) {\n\t\treturn dataPriv.access( elem, name, data );\n\t},\n\n\t_removeData: function( elem, name ) {\n\t\tdataPriv.remove( elem, name );\n\t}\n} );\n\njQuery.fn.extend( {\n\tdata: function( key, value ) {\n\t\tvar i, name, data,\n\t\t\telem = this[ 0 ],\n\t\t\tattrs = elem && elem.attributes;\n\n\t\t// Gets all values\n\t\tif ( key === undefined ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = dataUser.get( elem );\n\n\t\t\t\tif ( elem.nodeType === 1 && !dataPriv.get( elem, \"hasDataAttrs\" ) ) {\n\t\t\t\t\ti = attrs.length;\n\t\t\t\t\twhile ( i-- ) {\n\n\t\t\t\t\t\t// Support: IE 11 only\n\t\t\t\t\t\t// The attrs elements can be null (trac-14894)\n\t\t\t\t\t\tif ( attrs[ i ] ) {\n\t\t\t\t\t\t\tname = attrs[ i ].name;\n\t\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\t\tname = camelCase( name.slice( 5 ) );\n\t\t\t\t\t\t\t\tdataAttr( elem, name, data[ name ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdataPriv.set( elem, \"hasDataAttrs\", true );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\t\t}\n\n\t\t// Sets multiple values\n\t\tif ( typeof key === \"object\" ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tdataUser.set( this, key );\n\t\t\t} );\n\t\t}\n\n\t\treturn access( this, function( value ) {\n\t\t\tvar data;\n\n\t\t\t// The calling jQuery object (element matches) is not empty\n\t\t\t// (and therefore has an element appears at this[ 0 ]) and the\n\t\t\t// `value` parameter was not undefined. An empty jQuery object\n\t\t\t// will result in `undefined` for elem = this[ 0 ] which will\n\t\t\t// throw an exception if an attempt to read a data cache is made.\n\t\t\tif ( elem && value === undefined ) {\n\n\t\t\t\t// Attempt to get data from the cache\n\t\t\t\t// The key will always be camelCased in Data\n\t\t\t\tdata = dataUser.get( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// Attempt to \"discover\" the data in\n\t\t\t\t// HTML5 custom data-* attrs\n\t\t\t\tdata = dataAttr( elem, key );\n\t\t\t\tif ( data !== undefined ) {\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\n\t\t\t\t// We tried really hard, but the data doesn't exist.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the data...\n\t\t\tthis.each( function() {\n\n\t\t\t\t// We always store the camelCased key\n\t\t\t\tdataUser.set( this, key, value );\n\t\t\t} );\n\t\t}, null, value, arguments.length > 1, null, true );\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each( function() {\n\t\t\tdataUser.remove( this, key );\n\t\t} );\n\t}\n} );\n\n\njQuery.extend( {\n\tqueue: function( elem, type, data ) {\n\t\tvar queue;\n\n\t\tif ( elem ) {\n\t\t\ttype = ( type || \"fx\" ) + \"queue\";\n\t\t\tqueue = dataPriv.get( elem, type );\n\n\t\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\t\tif ( data ) {\n\t\t\t\tif ( !queue || Array.isArray( data ) ) {\n\t\t\t\t\tqueue = dataPriv.access( elem, type, jQuery.makeArray( data ) );\n\t\t\t\t} else {\n\t\t\t\t\tqueue.push( data );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queue || [];\n\t\t}\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tstartLength = queue.length,\n\t\t\tfn = queue.shift(),\n\t\t\thooks = jQuery._queueHooks( elem, type ),\n\t\t\tnext = function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t};\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t\tstartLength--;\n\t\t}\n\n\t\tif ( fn ) {\n\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift( \"inprogress\" );\n\t\t\t}\n\n\t\t\t// Clear up the last queue stop function\n\t\t\tdelete hooks.stop;\n\t\t\tfn.call( elem, next, hooks );\n\t\t}\n\n\t\tif ( !startLength && hooks ) {\n\t\t\thooks.empty.fire();\n\t\t}\n\t},\n\n\t// Not public - generate a queueHooks object, or return the current one\n\t_queueHooks: function( elem, type ) {\n\t\tvar key = type + \"queueHooks\";\n\t\treturn dataPriv.get( elem, key ) || dataPriv.access( elem, key, {\n\t\t\tempty: jQuery.Callbacks( \"once memory\" ).add( function() {\n\t\t\t\tdataPriv.remove( elem, [ type + \"queue\", key ] );\n\t\t\t} )\n\t\t} );\n\t}\n} );\n\njQuery.fn.extend( {\n\tqueue: function( type, data ) {\n\t\tvar setter = 2;\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t\tsetter--;\n\t\t}\n\n\t\tif ( arguments.length < setter ) {\n\t\t\treturn jQuery.queue( this[ 0 ], type );\n\t\t}\n\n\t\treturn data === undefined ?\n\t\t\tthis :\n\t\t\tthis.each( function() {\n\t\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\t\t// Ensure a hooks for this queue\n\t\t\t\tjQuery._queueHooks( this, type );\n\n\t\t\t\tif ( type === \"fx\" && queue[ 0 ] !== \"inprogress\" ) {\n\t\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t\t}\n\t\t\t} );\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t} );\n\t},\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t},\n\n\t// Get a promise resolved when queues of a certain type\n\t// are emptied (fx is the type by default)\n\tpromise: function( type, obj ) {\n\t\tvar tmp,\n\t\t\tcount = 1,\n\t\t\tdefer = jQuery.Deferred(),\n\t\t\telements = this,\n\t\t\ti = this.length,\n\t\t\tresolve = function() {\n\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\tdefer.resolveWith( elements, [ elements ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tobj = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\ttype = type || \"fx\";\n\n\t\twhile ( i-- ) {\n\t\t\ttmp = dataPriv.get( elements[ i ], type + \"queueHooks\" );\n\t\t\tif ( tmp && tmp.empty ) {\n\t\t\t\tcount++;\n\t\t\t\ttmp.empty.add( resolve );\n\t\t\t}\n\t\t}\n\t\tresolve();\n\t\treturn defer.promise( obj );\n\t}\n} );\nvar pnum = ( /[+-]?(?:\\d*\\.|)\\d+(?:[eE][+-]?\\d+|)/ ).source;\n\nvar rcssNum = new RegExp( \"^(?:([+-])=|)(\" + pnum + \")([a-z%]*)$\", \"i\" );\n\n\nvar cssExpand = [ \"Top\", \"Right\", \"Bottom\", \"Left\" ];\n\nvar documentElement = document.documentElement;\n\n\n\n\tvar isAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem );\n\t\t},\n\t\tcomposed = { composed: true };\n\n\t// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only\n\t// Check attachment across shadow DOM boundaries when possible (gh-3504)\n\t// Support: iOS 10.0-10.2 only\n\t// Early iOS 10 versions support `attachShadow` but not `getRootNode`,\n\t// leading to errors. We need to check for `getRootNode`.\n\tif ( documentElement.getRootNode ) {\n\t\tisAttached = function( elem ) {\n\t\t\treturn jQuery.contains( elem.ownerDocument, elem ) ||\n\t\t\t\telem.getRootNode( composed ) === elem.ownerDocument;\n\t\t};\n\t}\nvar isHiddenWithinTree = function( elem, el ) {\n\n\t\t// isHiddenWithinTree might be called from jQuery#filter function;\n\t\t// in that case, element will be second argument\n\t\telem = el || elem;\n\n\t\t// Inline style trumps all\n\t\treturn elem.style.display === \"none\" ||\n\t\t\telem.style.display === \"\" &&\n\n\t\t\t// Otherwise, check computed style\n\t\t\t// Support: Firefox <=43 - 45\n\t\t\t// Disconnected elements can have computed display: none, so first confirm that elem is\n\t\t\t// in the document.\n\t\t\tisAttached( elem ) &&\n\n\t\t\tjQuery.css( elem, \"display\" ) === \"none\";\n\t};\n\n\n\nfunction adjustCSS( elem, prop, valueParts, tween ) {\n\tvar adjusted, scale,\n\t\tmaxIterations = 20,\n\t\tcurrentValue = tween ?\n\t\t\tfunction() {\n\t\t\t\treturn tween.cur();\n\t\t\t} :\n\t\t\tfunction() {\n\t\t\t\treturn jQuery.css( elem, prop, \"\" );\n\t\t\t},\n\t\tinitial = currentValue(),\n\t\tunit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" ),\n\n\t\t// Starting value computation is required for potential unit mismatches\n\t\tinitialInUnit = elem.nodeType &&\n\t\t\t( jQuery.cssNumber[ prop ] || unit !== \"px\" && +initial ) &&\n\t\t\trcssNum.exec( jQuery.css( elem, prop ) );\n\n\tif ( initialInUnit && initialInUnit[ 3 ] !== unit ) {\n\n\t\t// Support: Firefox <=54\n\t\t// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)\n\t\tinitial = initial / 2;\n\n\t\t// Trust units reported by jQuery.css\n\t\tunit = unit || initialInUnit[ 3 ];\n\n\t\t// Iteratively approximate from a nonzero starting point\n\t\tinitialInUnit = +initial || 1;\n\n\t\twhile ( maxIterations-- ) {\n\n\t\t\t// Evaluate and update our best guess (doubling guesses that zero out).\n\t\t\t// Finish if the scale equals or crosses 1 (making the old*new product non-positive).\n\t\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\t\t\tif ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {\n\t\t\t\tmaxIterations = 0;\n\t\t\t}\n\t\t\tinitialInUnit = initialInUnit / scale;\n\n\t\t}\n\n\t\tinitialInUnit = initialInUnit * 2;\n\t\tjQuery.style( elem, prop, initialInUnit + unit );\n\n\t\t// Make sure we update the tween properties later on\n\t\tvalueParts = valueParts || [];\n\t}\n\n\tif ( valueParts ) {\n\t\tinitialInUnit = +initialInUnit || +initial || 0;\n\n\t\t// Apply relative offset (+=/-=) if specified\n\t\tadjusted = valueParts[ 1 ] ?\n\t\t\tinitialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :\n\t\t\t+valueParts[ 2 ];\n\t\tif ( tween ) {\n\t\t\ttween.unit = unit;\n\t\t\ttween.start = initialInUnit;\n\t\t\ttween.end = adjusted;\n\t\t}\n\t}\n\treturn adjusted;\n}\n\n\nvar defaultDisplayMap = {};\n\nfunction getDefaultDisplay( elem ) {\n\tvar temp,\n\t\tdoc = elem.ownerDocument,\n\t\tnodeName = elem.nodeName,\n\t\tdisplay = defaultDisplayMap[ nodeName ];\n\n\tif ( display ) {\n\t\treturn display;\n\t}\n\n\ttemp = doc.body.appendChild( doc.createElement( nodeName ) );\n\tdisplay = jQuery.css( temp, \"display\" );\n\n\ttemp.parentNode.removeChild( temp );\n\n\tif ( display === \"none\" ) {\n\t\tdisplay = \"block\";\n\t}\n\tdefaultDisplayMap[ nodeName ] = display;\n\n\treturn display;\n}\n\nfunction showHide( elements, show ) {\n\tvar display, elem,\n\t\tvalues = [],\n\t\tindex = 0,\n\t\tlength = elements.length;\n\n\t// Determine new display value for elements that need to change\n\tfor ( ; index < length; index++ ) {\n\t\telem = elements[ index ];\n\t\tif ( !elem.style ) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tdisplay = elem.style.display;\n\t\tif ( show ) {\n\n\t\t\t// Since we force visibility upon cascade-hidden elements, an immediate (and slow)\n\t\t\t// check is required in this first loop unless we have a nonempty display value (either\n\t\t\t// inline or about-to-be-restored)\n\t\t\tif ( display === \"none\" ) {\n\t\t\t\tvalues[ index ] = dataPriv.get( elem, \"display\" ) || null;\n\t\t\t\tif ( !values[ index ] ) {\n\t\t\t\t\telem.style.display = \"\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( elem.style.display === \"\" && isHiddenWithinTree( elem ) ) {\n\t\t\t\tvalues[ index ] = getDefaultDisplay( elem );\n\t\t\t}\n\t\t} else {\n\t\t\tif ( display !== \"none\" ) {\n\t\t\t\tvalues[ index ] = \"none\";\n\n\t\t\t\t// Remember what we're overwriting\n\t\t\t\tdataPriv.set( elem, \"display\", display );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Set the display of the elements in a second loop to avoid constant reflow\n\tfor ( index = 0; index < length; index++ ) {\n\t\tif ( values[ index ] != null ) {\n\t\t\telements[ index ].style.display = values[ index ];\n\t\t}\n\t}\n\n\treturn elements;\n}\n\njQuery.fn.extend( {\n\tshow: function() {\n\t\treturn showHide( this, true );\n\t},\n\thide: function() {\n\t\treturn showHide( this );\n\t},\n\ttoggle: function( state ) {\n\t\tif ( typeof state === \"boolean\" ) {\n\t\t\treturn state ? this.show() : this.hide();\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tif ( isHiddenWithinTree( this ) ) {\n\t\t\t\tjQuery( this ).show();\n\t\t\t} else {\n\t\t\t\tjQuery( this ).hide();\n\t\t\t}\n\t\t} );\n\t}\n} );\nvar rcheckableType = ( /^(?:checkbox|radio)$/i );\n\nvar rtagName = ( /<([a-z][^\\/\\0>\\x20\\t\\r\\n\\f]*)/i );\n\nvar rscriptType = ( /^$|^module$|\\/(?:java|ecma)script/i );\n\n\n\n( function() {\n\tvar fragment = document.createDocumentFragment(),\n\t\tdiv = fragment.appendChild( document.createElement( \"div\" ) ),\n\t\tinput = document.createElement( \"input\" );\n\n\t// Support: Android 4.0 - 4.3 only\n\t// Check state lost if the name is set (trac-11217)\n\t// Support: Windows Web Apps (WWA)\n\t// `name` and `type` must use .setAttribute for WWA (trac-14901)\n\tinput.setAttribute( \"type\", \"radio\" );\n\tinput.setAttribute( \"checked\", \"checked\" );\n\tinput.setAttribute( \"name\", \"t\" );\n\n\tdiv.appendChild( input );\n\n\t// Support: Android <=4.1 only\n\t// Older WebKit doesn't clone checked state correctly in fragments\n\tsupport.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;\n\n\t// Support: IE <=11 only\n\t// Make sure textarea (and checkbox) defaultValue is properly cloned\n\tdiv.innerHTML = \"<textarea>x</textarea>\";\n\tsupport.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;\n\n\t// Support: IE <=9 only\n\t// IE <=9 replaces <option> tags with their contents when inserted outside of\n\t// the select element.\n\tdiv.innerHTML = \"<option></option>\";\n\tsupport.option = !!div.lastChild;\n} )();\n\n\n// We have to close these tags to support XHTML (trac-13200)\nvar wrapMap = {\n\n\t// XHTML parsers do not magically insert elements in the\n\t// same way that tag soup parsers do. So we cannot shorten\n\t// this by omitting <tbody> or other required elements.\n\tthead: [ 1, \"<table>\", \"</table>\" ],\n\tcol: [ 2, \"<table><colgroup>\", \"</colgroup></table>\" ],\n\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\n\t_default: [ 0, \"\", \"\" ]\n};\n\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// Support: IE <=9 only\nif ( !support.option ) {\n\twrapMap.optgroup = wrapMap.option = [ 1, \"<select multiple='multiple'>\", \"</select>\" ];\n}\n\n\nfunction getAll( context, tag ) {\n\n\t// Support: IE <=9 - 11 only\n\t// Use typeof to avoid zero-argument method invocation on host objects (trac-15151)\n\tvar ret;\n\n\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\tret = context.getElementsByTagName( tag || \"*\" );\n\n\t} else if ( typeof context.querySelectorAll !== \"undefined\" ) {\n\t\tret = context.querySelectorAll( tag || \"*\" );\n\n\t} else {\n\t\tret = [];\n\t}\n\n\tif ( tag === undefined || tag && nodeName( context, tag ) ) {\n\t\treturn jQuery.merge( [ context ], ret );\n\t}\n\n\treturn ret;\n}\n\n\n// Mark scripts as having already been evaluated\nfunction setGlobalEval( elems, refElements ) {\n\tvar i = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\tdataPriv.set(\n\t\t\telems[ i ],\n\t\t\t\"globalEval\",\n\t\t\t!refElements || dataPriv.get( refElements[ i ], \"globalEval\" )\n\t\t);\n\t}\n}\n\n\nvar rhtml = /<|&#?\\w+;/;\n\nfunction buildFragment( elems, context, scripts, selection, ignored ) {\n\tvar elem, tmp, tag, wrap, attached, j,\n\t\tfragment = context.createDocumentFragment(),\n\t\tnodes = [],\n\t\ti = 0,\n\t\tl = elems.length;\n\n\tfor ( ; i < l; i++ ) {\n\t\telem = elems[ i ];\n\n\t\tif ( elem || elem === 0 ) {\n\n\t\t\t// Add nodes directly\n\t\t\tif ( toType( elem ) === \"object\" ) {\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );\n\n\t\t\t// Convert non-html into a text node\n\t\t\t} else if ( !rhtml.test( elem ) ) {\n\t\t\t\tnodes.push( context.createTextNode( elem ) );\n\n\t\t\t// Convert html into DOM nodes\n\t\t\t} else {\n\t\t\t\ttmp = tmp || fragment.appendChild( context.createElement( \"div\" ) );\n\n\t\t\t\t// Deserialize a standard representation\n\t\t\t\ttag = ( rtagName.exec( elem ) || [ \"\", \"\" ] )[ 1 ].toLowerCase();\n\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default;\n\t\t\t\ttmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];\n\n\t\t\t\t// Descend through wrappers to the right content\n\t\t\t\tj = wrap[ 0 ];\n\t\t\t\twhile ( j-- ) {\n\t\t\t\t\ttmp = tmp.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\tjQuery.merge( nodes, tmp.childNodes );\n\n\t\t\t\t// Remember the top-level container\n\t\t\t\ttmp = fragment.firstChild;\n\n\t\t\t\t// Ensure the created nodes are orphaned (trac-12392)\n\t\t\t\ttmp.textContent = \"\";\n\t\t\t}\n\t\t}\n\t}\n\n\t// Remove wrapper from fragment\n\tfragment.textContent = \"\";\n\n\ti = 0;\n\twhile ( ( elem = nodes[ i++ ] ) ) {\n\n\t\t// Skip elements already in the context collection (trac-4087)\n\t\tif ( selection && jQuery.inArray( elem, selection ) > -1 ) {\n\t\t\tif ( ignored ) {\n\t\t\t\tignored.push( elem );\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tattached = isAttached( elem );\n\n\t\t// Append to fragment\n\t\ttmp = getAll( fragment.appendChild( elem ), \"script\" );\n\n\t\t// Preserve script evaluation history\n\t\tif ( attached ) {\n\t\t\tsetGlobalEval( tmp );\n\t\t}\n\n\t\t// Capture executables\n\t\tif ( scripts ) {\n\t\t\tj = 0;\n\t\t\twhile ( ( elem = tmp[ j++ ] ) ) {\n\t\t\t\tif ( rscriptType.test( elem.type || \"\" ) ) {\n\t\t\t\t\tscripts.push( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn fragment;\n}\n\n\nvar rtypenamespace = /^([^.]*)(?:\\.(.+)|)/;\n\nfunction returnTrue() {\n\treturn true;\n}\n\nfunction returnFalse() {\n\treturn false;\n}\n\n// Support: IE <=9 - 11+\n// focus() and blur() are asynchronous, except when they are no-op.\n// So expect focus to be synchronous when the element is already active,\n// and blur to be synchronous when the element is not already active.\n// (focus and blur are always synchronous in other supported browsers,\n// this just defines when we can count on it).\nfunction expectSync( elem, type ) {\n\treturn ( elem === safeActiveElement() ) === ( type === \"focus\" );\n}\n\n// Support: IE <=9 only\n// Accessing document.activeElement can throw unexpectedly\n// https://bugs.jquery.com/ticket/13393\nfunction safeActiveElement() {\n\ttry {\n\t\treturn document.activeElement;\n\t} catch ( err ) { }\n}\n\nfunction on( elem, types, selector, data, fn, one ) {\n\tvar origFn, type;\n\n\t// Types can be a map of types/handlers\n\tif ( typeof types === \"object\" ) {\n\n\t\t// ( types-Object, selector, data )\n\t\tif ( typeof selector !== \"string\" ) {\n\n\t\t\t// ( types-Object, data )\n\t\t\tdata = data || selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tfor ( type in types ) {\n\t\t\ton( elem, type, selector, data, types[ type ], one );\n\t\t}\n\t\treturn elem;\n\t}\n\n\tif ( data == null && fn == null ) {\n\n\t\t// ( types, fn )\n\t\tfn = selector;\n\t\tdata = selector = undefined;\n\t} else if ( fn == null ) {\n\t\tif ( typeof selector === \"string\" ) {\n\n\t\t\t// ( types, selector, fn )\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t} else {\n\n\t\t\t// ( types, data, fn )\n\t\t\tfn = data;\n\t\t\tdata = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t}\n\tif ( fn === false ) {\n\t\tfn = returnFalse;\n\t} else if ( !fn ) {\n\t\treturn elem;\n\t}\n\n\tif ( one === 1 ) {\n\t\torigFn = fn;\n\t\tfn = function( event ) {\n\n\t\t\t// Can use an empty set, since event contains the info\n\t\t\tjQuery().off( event );\n\t\t\treturn origFn.apply( this, arguments );\n\t\t};\n\n\t\t// Use same guid so caller can remove using origFn\n\t\tfn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );\n\t}\n\treturn elem.each( function() {\n\t\tjQuery.event.add( this, types, fn, data, selector );\n\t} );\n}\n\n/*\n * Helper functions for managing events -- not part of the public interface.\n * Props to Dean Edwards' addEvent library for many of the ideas.\n */\njQuery.event = {\n\n\tglobal: {},\n\n\tadd: function( elem, types, handler, data, selector ) {\n\n\t\tvar handleObjIn, eventHandle, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.get( elem );\n\n\t\t// Only attach events to objects that accept data\n\t\tif ( !acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Caller can pass in an object of custom data in lieu of the handler\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t\tselector = handleObjIn.selector;\n\t\t}\n\n\t\t// Ensure that invalid selectors throw exceptions at attach time\n\t\t// Evaluate against documentElement in case elem is a non-element node (e.g., document)\n\t\tif ( selector ) {\n\t\t\tjQuery.find.matchesSelector( documentElement, selector );\n\t\t}\n\n\t\t// Make sure that the handler has a unique ID, used to find/remove it later\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure and main handler, if this is the first\n\t\tif ( !( events = elemData.events ) ) {\n\t\t\tevents = elemData.events = Object.create( null );\n\t\t}\n\t\tif ( !( eventHandle = elemData.handle ) ) {\n\t\t\teventHandle = elemData.handle = function( e ) {\n\n\t\t\t\t// Discard the second event of a jQuery.event.trigger() and\n\t\t\t\t// when an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && jQuery.event.triggered !== e.type ?\n\t\t\t\t\tjQuery.event.dispatch.apply( elem, arguments ) : undefined;\n\t\t\t};\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// There *must* be a type, no attaching namespace-only handlers\n\t\t\tif ( !type ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// If event changes its type, use the special event handlers for the changed type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// If selector defined, determine special event api type, otherwise given type\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\n\t\t\t// Update special based on newly reset type\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// handleObj is passed to all event handlers\n\t\t\thandleObj = jQuery.extend( {\n\t\t\t\ttype: type,\n\t\t\t\torigType: origType,\n\t\t\t\tdata: data,\n\t\t\t\thandler: handler,\n\t\t\t\tguid: handler.guid,\n\t\t\t\tselector: selector,\n\t\t\t\tneedsContext: selector && jQuery.expr.match.needsContext.test( selector ),\n\t\t\t\tnamespace: namespaces.join( \".\" )\n\t\t\t}, handleObjIn );\n\n\t\t\t// Init the event handler queue if we're the first\n\t\t\tif ( !( handlers = events[ type ] ) ) {\n\t\t\t\thandlers = events[ type ] = [];\n\t\t\t\thandlers.delegateCount = 0;\n\n\t\t\t\t// Only use addEventListener if the special events handler returns false\n\t\t\t\tif ( !special.setup ||\n\t\t\t\t\tspecial.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add to the element's handler list, delegates in front\n\t\t\tif ( selector ) {\n\t\t\t\thandlers.splice( handlers.delegateCount++, 0, handleObj );\n\t\t\t} else {\n\t\t\t\thandlers.push( handleObj );\n\t\t\t}\n\n\t\t\t// Keep track of which events have ever been used, for event optimization\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, selector, mappedTypes ) {\n\n\t\tvar j, origCount, tmp,\n\t\t\tevents, t, handleObj,\n\t\t\tspecial, handlers, type, namespaces, origType,\n\t\t\telemData = dataPriv.hasData( elem ) && dataPriv.get( elem );\n\n\t\tif ( !elemData || !( events = elemData.events ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Once for each type.namespace in types; type may be omitted\n\t\ttypes = ( types || \"\" ).match( rnothtmlwhite ) || [ \"\" ];\n\t\tt = types.length;\n\t\twhile ( t-- ) {\n\t\t\ttmp = rtypenamespace.exec( types[ t ] ) || [];\n\t\t\ttype = origType = tmp[ 1 ];\n\t\t\tnamespaces = ( tmp[ 2 ] || \"\" ).split( \".\" ).sort();\n\n\t\t\t// Unbind all events (on this namespace, if provided) for the element\n\t\t\tif ( !type ) {\n\t\t\t\tfor ( type in events ) {\n\t\t\t\t\tjQuery.event.remove( elem, type + types[ t ], handler, selector, true );\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\t\t\ttype = ( selector ? special.delegateType : special.bindType ) || type;\n\t\t\thandlers = events[ type ] || [];\n\t\t\ttmp = tmp[ 2 ] &&\n\t\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" );\n\n\t\t\t// Remove matching events\n\t\t\torigCount = j = handlers.length;\n\t\t\twhile ( j-- ) {\n\t\t\t\thandleObj = handlers[ j ];\n\n\t\t\t\tif ( ( mappedTypes || origType === handleObj.origType ) &&\n\t\t\t\t\t( !handler || handler.guid === handleObj.guid ) &&\n\t\t\t\t\t( !tmp || tmp.test( handleObj.namespace ) ) &&\n\t\t\t\t\t( !selector || selector === handleObj.selector ||\n\t\t\t\t\t\tselector === \"**\" && handleObj.selector ) ) {\n\t\t\t\t\thandlers.splice( j, 1 );\n\n\t\t\t\t\tif ( handleObj.selector ) {\n\t\t\t\t\t\thandlers.delegateCount--;\n\t\t\t\t\t}\n\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove generic event handler if we removed something and no more handlers exist\n\t\t\t// (avoids potential for endless recursion during removal of special event handlers)\n\t\t\tif ( origCount && !handlers.length ) {\n\t\t\t\tif ( !special.teardown ||\n\t\t\t\t\tspecial.teardown.call( elem, namespaces, elemData.handle ) === false ) {\n\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove data and the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tdataPriv.remove( elem, \"handle events\" );\n\t\t}\n\t},\n\n\tdispatch: function( nativeEvent ) {\n\n\t\tvar i, j, ret, matched, handleObj, handlerQueue,\n\t\t\targs = new Array( arguments.length ),\n\n\t\t\t// Make a writable jQuery.Event from the native event object\n\t\t\tevent = jQuery.event.fix( nativeEvent ),\n\n\t\t\thandlers = (\n\t\t\t\tdataPriv.get( this, \"events\" ) || Object.create( null )\n\t\t\t)[ event.type ] || [],\n\t\t\tspecial = jQuery.event.special[ event.type ] || {};\n\n\t\t// Use the fix-ed jQuery.Event rather than the (read-only) native event\n\t\targs[ 0 ] = event;\n\n\t\tfor ( i = 1; i < arguments.length; i++ ) {\n\t\t\targs[ i ] = arguments[ i ];\n\t\t}\n\n\t\tevent.delegateTarget = this;\n\n\t\t// Call the preDispatch hook for the mapped type, and let it bail if desired\n\t\tif ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine handlers\n\t\thandlerQueue = jQuery.event.handlers.call( this, event, handlers );\n\n\t\t// Run delegates first; they may want to stop propagation beneath us\n\t\ti = 0;\n\t\twhile ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tevent.currentTarget = matched.elem;\n\n\t\t\tj = 0;\n\t\t\twhile ( ( handleObj = matched.handlers[ j++ ] ) &&\n\t\t\t\t!event.isImmediatePropagationStopped() ) {\n\n\t\t\t\t// If the event is namespaced, then each handler is only invoked if it is\n\t\t\t\t// specially universal or its namespaces are a superset of the event's.\n\t\t\t\tif ( !event.rnamespace || handleObj.namespace === false ||\n\t\t\t\t\tevent.rnamespace.test( handleObj.namespace ) ) {\n\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\t\t\t\tevent.data = handleObj.data;\n\n\t\t\t\t\tret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||\n\t\t\t\t\t\thandleObj.handler ).apply( matched.elem, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tif ( ( event.result = ret ) === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Call the postDispatch hook for the mapped type\n\t\tif ( special.postDispatch ) {\n\t\t\tspecial.postDispatch.call( this, event );\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\thandlers: function( event, handlers ) {\n\t\tvar i, handleObj, sel, matchedHandlers, matchedSelectors,\n\t\t\thandlerQueue = [],\n\t\t\tdelegateCount = handlers.delegateCount,\n\t\t\tcur = event.target;\n\n\t\t// Find delegate handlers\n\t\tif ( delegateCount &&\n\n\t\t\t// Support: IE <=9\n\t\t\t// Black-hole SVG <use> instance trees (trac-13180)\n\t\t\tcur.nodeType &&\n\n\t\t\t// Support: Firefox <=42\n\t\t\t// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)\n\t\t\t// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click\n\t\t\t// Support: IE 11 only\n\t\t\t// ...but not arrow key \"clicks\" of radio inputs, which can have `button` -1 (gh-2343)\n\t\t\t!( event.type === \"click\" && event.button >= 1 ) ) {\n\n\t\t\tfor ( ; cur !== this; cur = cur.parentNode || this ) {\n\n\t\t\t\t// Don't check non-elements (trac-13208)\n\t\t\t\t// Don't process clicks on disabled elements (trac-6911, trac-8165, trac-11382, trac-11764)\n\t\t\t\tif ( cur.nodeType === 1 && !( event.type === \"click\" && cur.disabled === true ) ) {\n\t\t\t\t\tmatchedHandlers = [];\n\t\t\t\t\tmatchedSelectors = {};\n\t\t\t\t\tfor ( i = 0; i < delegateCount; i++ ) {\n\t\t\t\t\t\thandleObj = handlers[ i ];\n\n\t\t\t\t\t\t// Don't conflict with Object.prototype properties (trac-13203)\n\t\t\t\t\t\tsel = handleObj.selector + \" \";\n\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] === undefined ) {\n\t\t\t\t\t\t\tmatchedSelectors[ sel ] = handleObj.needsContext ?\n\t\t\t\t\t\t\t\tjQuery( sel, this ).index( cur ) > -1 :\n\t\t\t\t\t\t\t\tjQuery.find( sel, this, null, [ cur ] ).length;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( matchedSelectors[ sel ] ) {\n\t\t\t\t\t\t\tmatchedHandlers.push( handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( matchedHandlers.length ) {\n\t\t\t\t\t\thandlerQueue.push( { elem: cur, handlers: matchedHandlers } );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Add the remaining (directly-bound) handlers\n\t\tcur = this;\n\t\tif ( delegateCount < handlers.length ) {\n\t\t\thandlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );\n\t\t}\n\n\t\treturn handlerQueue;\n\t},\n\n\taddProp: function( name, hook ) {\n\t\tObject.defineProperty( jQuery.Event.prototype, name, {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: true,\n\n\t\t\tget: isFunction( hook ) ?\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\treturn hook( this.originalEvent );\n\t\t\t\t\t}\n\t\t\t\t} :\n\t\t\t\tfunction() {\n\t\t\t\t\tif ( this.originalEvent ) {\n\t\t\t\t\t\treturn this.originalEvent[ name ];\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\tset: function( value ) {\n\t\t\t\tObject.defineProperty( this, name, {\n\t\t\t\t\tenumerable: true,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\twritable: true,\n\t\t\t\t\tvalue: value\n\t\t\t\t} );\n\t\t\t}\n\t\t} );\n\t},\n\n\tfix: function( originalEvent ) {\n\t\treturn originalEvent[ jQuery.expando ] ?\n\t\t\toriginalEvent :\n\t\t\tnew jQuery.Event( originalEvent );\n\t},\n\n\tspecial: {\n\t\tload: {\n\n\t\t\t// Prevent triggered image.load events from bubbling to window.load\n\t\t\tnoBubble: true\n\t\t},\n\t\tclick: {\n\n\t\t\t// Utilize native event to ensure correct state for checkable inputs\n\t\t\tsetup: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Claim the first handler\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\t// dataPriv.set( el, \"click\", ... )\n\t\t\t\t\tleverageNative( el, \"click\", returnTrue );\n\t\t\t\t}\n\n\t\t\t\t// Return false to allow normal processing in the caller\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\ttrigger: function( data ) {\n\n\t\t\t\t// For mutual compressibility with _default, replace `this` access with a local var.\n\t\t\t\t// `|| data` is dead code meant only to preserve the variable through minification.\n\t\t\t\tvar el = this || data;\n\n\t\t\t\t// Force setup before triggering a click\n\t\t\t\tif ( rcheckableType.test( el.type ) &&\n\t\t\t\t\tel.click && nodeName( el, \"input\" ) ) {\n\n\t\t\t\t\tleverageNative( el, \"click\" );\n\t\t\t\t}\n\n\t\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\t// For cross-browser consistency, suppress native .click() on links\n\t\t\t// Also prevent it if we're currently inside a leveraged native-event stack\n\t\t\t_default: function( event ) {\n\t\t\t\tvar target = event.target;\n\t\t\t\treturn rcheckableType.test( target.type ) &&\n\t\t\t\t\ttarget.click && nodeName( target, \"input\" ) &&\n\t\t\t\t\tdataPriv.get( target, \"click\" ) ||\n\t\t\t\t\tnodeName( target, \"a\" );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tpostDispatch: function( event ) {\n\n\t\t\t\t// Support: Firefox 20+\n\t\t\t\t// Firefox doesn't alert if the returnValue field is not set.\n\t\t\t\tif ( event.result !== undefined && event.originalEvent ) {\n\t\t\t\t\tevent.originalEvent.returnValue = event.result;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Ensure the presence of an event listener that handles manually-triggered\n// synthetic events by interrupting progress until reinvoked in response to\n// *native* events that it fires directly, ensuring that state changes have\n// already occurred before other listeners are invoked.\nfunction leverageNative( el, type, expectSync ) {\n\n\t// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add\n\tif ( !expectSync ) {\n\t\tif ( dataPriv.get( el, type ) === undefined ) {\n\t\t\tjQuery.event.add( el, type, returnTrue );\n\t\t}\n\t\treturn;\n\t}\n\n\t// Register the controller as a special universal handler for all event namespaces\n\tdataPriv.set( el, type, false );\n\tjQuery.event.add( el, type, {\n\t\tnamespace: false,\n\t\thandler: function( event ) {\n\t\t\tvar notAsync, result,\n\t\t\t\tsaved = dataPriv.get( this, type );\n\n\t\t\tif ( ( event.isTrigger & 1 ) && this[ type ] ) {\n\n\t\t\t\t// Interrupt processing of the outer synthetic .trigger()ed event\n\t\t\t\t// Saved data should be false in such cases, but might be a leftover capture object\n\t\t\t\t// from an async native handler (gh-4350)\n\t\t\t\tif ( !saved.length ) {\n\n\t\t\t\t\t// Store arguments for use when handling the inner native event\n\t\t\t\t\t// There will always be at least one argument (an event object), so this array\n\t\t\t\t\t// will not be confused with a leftover capture object.\n\t\t\t\t\tsaved = slice.call( arguments );\n\t\t\t\t\tdataPriv.set( this, type, saved );\n\n\t\t\t\t\t// Trigger the native event and capture its result\n\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t// focus() and blur() are asynchronous\n\t\t\t\t\tnotAsync = expectSync( this, type );\n\t\t\t\t\tthis[ type ]();\n\t\t\t\t\tresult = dataPriv.get( this, type );\n\t\t\t\t\tif ( saved !== result || notAsync ) {\n\t\t\t\t\t\tdataPriv.set( this, type, false );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = {};\n\t\t\t\t\t}\n\t\t\t\t\tif ( saved !== result ) {\n\n\t\t\t\t\t\t// Cancel the outer synthetic event\n\t\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\t\tevent.preventDefault();\n\n\t\t\t\t\t\t// Support: Chrome 86+\n\t\t\t\t\t\t// In Chrome, if an element having a focusout handler is blurred by\n\t\t\t\t\t\t// clicking outside of it, it invokes the handler synchronously. If\n\t\t\t\t\t\t// that handler calls `.remove()` on the element, the data is cleared,\n\t\t\t\t\t\t// leaving `result` undefined. We need to guard against this.\n\t\t\t\t\t\treturn result && result.value;\n\t\t\t\t\t}\n\n\t\t\t\t// If this is an inner synthetic event for an event with a bubbling surrogate\n\t\t\t\t// (focus or blur), assume that the surrogate already propagated from triggering the\n\t\t\t\t// native event and prevent that from happening again here.\n\t\t\t\t// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the\n\t\t\t\t// bubbling surrogate propagates *after* the non-bubbling base), but that seems\n\t\t\t\t// less bad than duplication.\n\t\t\t\t} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t}\n\n\t\t\t// If this is a native event triggered above, everything is now in order\n\t\t\t// Fire an inner synthetic event with the original arguments\n\t\t\t} else if ( saved.length ) {\n\n\t\t\t\t// ...and capture the result\n\t\t\t\tdataPriv.set( this, type, {\n\t\t\t\t\tvalue: jQuery.event.trigger(\n\n\t\t\t\t\t\t// Support: IE <=9 - 11+\n\t\t\t\t\t\t// Extend with the prototype to reset the above stopImmediatePropagation()\n\t\t\t\t\t\tjQuery.extend( saved[ 0 ], jQuery.Event.prototype ),\n\t\t\t\t\t\tsaved.slice( 1 ),\n\t\t\t\t\t\tthis\n\t\t\t\t\t)\n\t\t\t\t} );\n\n\t\t\t\t// Abort handling of the native event\n\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t}\n\t\t}\n\t} );\n}\n\njQuery.removeEvent = function( elem, type, handle ) {\n\n\t// This \"if\" is needed for plain objects\n\tif ( elem.removeEventListener ) {\n\t\telem.removeEventListener( type, handle );\n\t}\n};\n\njQuery.Event = function( src, props ) {\n\n\t// Allow instantiation without the 'new' keyword\n\tif ( !( this instanceof jQuery.Event ) ) {\n\t\treturn new jQuery.Event( src, props );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = src.defaultPrevented ||\n\t\t\t\tsrc.defaultPrevented === undefined &&\n\n\t\t\t\t// Support: Android <=2.3 only\n\t\t\t\tsrc.returnValue === false ?\n\t\t\treturnTrue :\n\t\t\treturnFalse;\n\n\t\t// Create target properties\n\t\t// Support: Safari <=6 - 7 only\n\t\t// Target should not be a text node (trac-504, trac-13143)\n\t\tthis.target = ( src.target && src.target.nodeType === 3 ) ?\n\t\t\tsrc.target.parentNode :\n\t\t\tsrc.target;\n\n\t\tthis.currentTarget = src.currentTarget;\n\t\tthis.relatedTarget = src.relatedTarget;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// Put explicitly provided properties onto the event object\n\tif ( props ) {\n\t\tjQuery.extend( this, props );\n\t}\n\n\t// Create a timestamp if incoming event doesn't have one\n\tthis.timeStamp = src && src.timeStamp || Date.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tconstructor: jQuery.Event,\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse,\n\tisSimulated: false,\n\n\tpreventDefault: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.preventDefault();\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t},\n\tstopImmediatePropagation: function() {\n\t\tvar e = this.originalEvent;\n\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\n\t\tif ( e && !this.isSimulated ) {\n\t\t\te.stopImmediatePropagation();\n\t\t}\n\n\t\tthis.stopPropagation();\n\t}\n};\n\n// Includes all common event props including KeyEvent and MouseEvent specific props\njQuery.each( {\n\taltKey: true,\n\tbubbles: true,\n\tcancelable: true,\n\tchangedTouches: true,\n\tctrlKey: true,\n\tdetail: true,\n\teventPhase: true,\n\tmetaKey: true,\n\tpageX: true,\n\tpageY: true,\n\tshiftKey: true,\n\tview: true,\n\t\"char\": true,\n\tcode: true,\n\tcharCode: true,\n\tkey: true,\n\tkeyCode: true,\n\tbutton: true,\n\tbuttons: true,\n\tclientX: true,\n\tclientY: true,\n\toffsetX: true,\n\toffsetY: true,\n\tpointerId: true,\n\tpointerType: true,\n\tscreenX: true,\n\tscreenY: true,\n\ttargetTouches: true,\n\ttoElement: true,\n\ttouches: true,\n\twhich: true\n}, jQuery.event.addProp );\n\njQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( type, delegateType ) {\n\tjQuery.event.special[ type ] = {\n\n\t\t// Utilize native event if possible so blur/focus sequence is correct\n\t\tsetup: function() {\n\n\t\t\t// Claim the first handler\n\t\t\t// dataPriv.set( this, \"focus\", ... )\n\t\t\t// dataPriv.set( this, \"blur\", ... )\n\t\t\tleverageNative( this, type, expectSync );\n\n\t\t\t// Return false to allow normal processing in the caller\n\t\t\treturn false;\n\t\t},\n\t\ttrigger: function() {\n\n\t\t\t// Force setup before trigger\n\t\t\tleverageNative( this, type );\n\n\t\t\t// Return non-false to allow normal event-path propagation\n\t\t\treturn true;\n\t\t},\n\n\t\t// Suppress native focus or blur if we're currently inside\n\t\t// a leveraged native-event stack\n\t\t_default: function( event ) {\n\t\t\treturn dataPriv.get( event.target, type );\n\t\t},\n\n\t\tdelegateType: delegateType\n\t};\n} );\n\n// Create mouseenter/leave events using mouseover/out and event-time checks\n// so that event delegation works in jQuery.\n// Do the same for pointerenter/pointerleave and pointerover/pointerout\n//\n// Support: Safari 7 only\n// Safari sends mouseenter too often; see:\n// https://bugs.chromium.org/p/chromium/issues/detail?id=470258\n// for the description of the bug (it existed in older Chrome versions as well).\njQuery.each( {\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\",\n\tpointerenter: \"pointerover\",\n\tpointerleave: \"pointerout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tdelegateType: fix,\n\t\tbindType: fix,\n\n\t\thandle: function( event ) {\n\t\t\tvar ret,\n\t\t\t\ttarget = this,\n\t\t\t\trelated = event.relatedTarget,\n\t\t\t\thandleObj = event.handleObj;\n\n\t\t\t// For mouseenter/leave call the handler if related is outside the target.\n\t\t\t// NB: No relatedTarget if the mouse left/entered the browser window\n\t\t\tif ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {\n\t\t\t\tevent.type = handleObj.origType;\n\t\t\t\tret = handleObj.handler.apply( this, arguments );\n\t\t\t\tevent.type = fix;\n\t\t\t}\n\t\t\treturn ret;\n\t\t}\n\t};\n} );\n\njQuery.fn.extend( {\n\n\ton: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn );\n\t},\n\tone: function( types, selector, data, fn ) {\n\t\treturn on( this, types, selector, data, fn, 1 );\n\t},\n\toff: function( types, selector, fn ) {\n\t\tvar handleObj, type;\n\t\tif ( types && types.preventDefault && types.handleObj ) {\n\n\t\t\t// ( event )  dispatched jQuery.Event\n\t\t\thandleObj = types.handleObj;\n\t\t\tjQuery( types.delegateTarget ).off(\n\t\t\t\thandleObj.namespace ?\n\t\t\t\t\thandleObj.origType + \".\" + handleObj.namespace :\n\t\t\t\t\thandleObj.origType,\n\t\t\t\thandleObj.selector,\n\t\t\t\thandleObj.handler\n\t\t\t);\n\t\t\treturn this;\n\t\t}\n\t\tif ( typeof types === \"object\" ) {\n\n\t\t\t// ( types-object [, selector] )\n\t\t\tfor ( type in types ) {\n\t\t\t\tthis.off( type, selector, types[ type ] );\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\tif ( selector === false || typeof selector === \"function\" ) {\n\n\t\t\t// ( types [, fn] )\n\t\t\tfn = selector;\n\t\t\tselector = undefined;\n\t\t}\n\t\tif ( fn === false ) {\n\t\t\tfn = returnFalse;\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.remove( this, types, fn, selector );\n\t\t} );\n\t}\n} );\n\n\nvar\n\n\t// Support: IE <=10 - 11, Edge 12 - 13 only\n\t// In IE/Edge using regex groups here causes severe slowdowns.\n\t// See https://connect.microsoft.com/IE/feedback/details/1736512/\n\trnoInnerhtml = /<script|<style|<link/i,\n\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\n\trcleanScript = /^\\s*<!\\[CDATA\\[|\\]\\]>\\s*$/g;\n\n// Prefer a tbody over its parent table for containing new rows\nfunction manipulationTarget( elem, content ) {\n\tif ( nodeName( elem, \"table\" ) &&\n\t\tnodeName( content.nodeType !== 11 ? content : content.firstChild, \"tr\" ) ) {\n\n\t\treturn jQuery( elem ).children( \"tbody\" )[ 0 ] || elem;\n\t}\n\n\treturn elem;\n}\n\n// Replace/restore the type attribute of script elements for safe DOM manipulation\nfunction disableScript( elem ) {\n\telem.type = ( elem.getAttribute( \"type\" ) !== null ) + \"/\" + elem.type;\n\treturn elem;\n}\nfunction restoreScript( elem ) {\n\tif ( ( elem.type || \"\" ).slice( 0, 5 ) === \"true/\" ) {\n\t\telem.type = elem.type.slice( 5 );\n\t} else {\n\t\telem.removeAttribute( \"type\" );\n\t}\n\n\treturn elem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\tvar i, l, type, pdataOld, udataOld, udataCur, events;\n\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\t// 1. Copy private data: events, handlers, etc.\n\tif ( dataPriv.hasData( src ) ) {\n\t\tpdataOld = dataPriv.get( src );\n\t\tevents = pdataOld.events;\n\n\t\tif ( events ) {\n\t\t\tdataPriv.remove( dest, \"handle events\" );\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tfor ( i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type, events[ type ][ i ] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Copy user data\n\tif ( dataUser.hasData( src ) ) {\n\t\tudataOld = dataUser.access( src );\n\t\tudataCur = jQuery.extend( {}, udataOld );\n\n\t\tdataUser.set( dest, udataCur );\n\t}\n}\n\n// Fix IE bugs, see support tests\nfunction fixInput( src, dest ) {\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// Fails to persist the checked state of a cloned checkbox or radio button.\n\tif ( nodeName === \"input\" && rcheckableType.test( src.type ) ) {\n\t\tdest.checked = src.checked;\n\n\t// Fails to return the selected option to the default selected state when cloning options\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n}\n\nfunction domManip( collection, args, callback, ignored ) {\n\n\t// Flatten any nested arrays\n\targs = flat( args );\n\n\tvar fragment, first, scripts, hasScripts, node, doc,\n\t\ti = 0,\n\t\tl = collection.length,\n\t\tiNoClone = l - 1,\n\t\tvalue = args[ 0 ],\n\t\tvalueIsFunction = isFunction( value );\n\n\t// We can't cloneNode fragments that contain checked, in WebKit\n\tif ( valueIsFunction ||\n\t\t\t( l > 1 && typeof value === \"string\" &&\n\t\t\t\t!support.checkClone && rchecked.test( value ) ) ) {\n\t\treturn collection.each( function( index ) {\n\t\t\tvar self = collection.eq( index );\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\targs[ 0 ] = value.call( this, index, self.html() );\n\t\t\t}\n\t\t\tdomManip( self, args, callback, ignored );\n\t\t} );\n\t}\n\n\tif ( l ) {\n\t\tfragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );\n\t\tfirst = fragment.firstChild;\n\n\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\tfragment = first;\n\t\t}\n\n\t\t// Require either new content or an interest in ignored elements to invoke the callback\n\t\tif ( first || ignored ) {\n\t\t\tscripts = jQuery.map( getAll( fragment, \"script\" ), disableScript );\n\t\t\thasScripts = scripts.length;\n\n\t\t\t// Use the original fragment for the last item\n\t\t\t// instead of the first because it can end up\n\t\t\t// being emptied incorrectly in certain situations (trac-8070).\n\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\tnode = fragment;\n\n\t\t\t\tif ( i !== iNoClone ) {\n\t\t\t\t\tnode = jQuery.clone( node, true, true );\n\n\t\t\t\t\t// Keep references to cloned scripts for later restoration\n\t\t\t\t\tif ( hasScripts ) {\n\n\t\t\t\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t\t\t\t// push.apply(_, arraylike) throws on ancient WebKit\n\t\t\t\t\t\tjQuery.merge( scripts, getAll( node, \"script\" ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallback.call( collection[ i ], node, i );\n\t\t\t}\n\n\t\t\tif ( hasScripts ) {\n\t\t\t\tdoc = scripts[ scripts.length - 1 ].ownerDocument;\n\n\t\t\t\t// Reenable scripts\n\t\t\t\tjQuery.map( scripts, restoreScript );\n\n\t\t\t\t// Evaluate executable scripts on first document insertion\n\t\t\t\tfor ( i = 0; i < hasScripts; i++ ) {\n\t\t\t\t\tnode = scripts[ i ];\n\t\t\t\t\tif ( rscriptType.test( node.type || \"\" ) &&\n\t\t\t\t\t\t!dataPriv.access( node, \"globalEval\" ) &&\n\t\t\t\t\t\tjQuery.contains( doc, node ) ) {\n\n\t\t\t\t\t\tif ( node.src && ( node.type || \"\" ).toLowerCase()  !== \"module\" ) {\n\n\t\t\t\t\t\t\t// Optional AJAX dependency, but won't run scripts if not present\n\t\t\t\t\t\t\tif ( jQuery._evalUrl && !node.noModule ) {\n\t\t\t\t\t\t\t\tjQuery._evalUrl( node.src, {\n\t\t\t\t\t\t\t\t\tnonce: node.nonce || node.getAttribute( \"nonce\" )\n\t\t\t\t\t\t\t\t}, doc );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Unwrap a CDATA section containing script contents. This shouldn't be\n\t\t\t\t\t\t\t// needed as in XML documents they're already not visible when\n\t\t\t\t\t\t\t// inspecting element contents and in HTML documents they have no\n\t\t\t\t\t\t\t// meaning but we're preserving that logic for backwards compatibility.\n\t\t\t\t\t\t\t// This will be removed completely in 4.0. See gh-4904.\n\t\t\t\t\t\t\tDOMEval( node.textContent.replace( rcleanScript, \"\" ), node, doc );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn collection;\n}\n\nfunction remove( elem, selector, keepData ) {\n\tvar node,\n\t\tnodes = selector ? jQuery.filter( selector, elem ) : elem,\n\t\ti = 0;\n\n\tfor ( ; ( node = nodes[ i ] ) != null; i++ ) {\n\t\tif ( !keepData && node.nodeType === 1 ) {\n\t\t\tjQuery.cleanData( getAll( node ) );\n\t\t}\n\n\t\tif ( node.parentNode ) {\n\t\t\tif ( keepData && isAttached( node ) ) {\n\t\t\t\tsetGlobalEval( getAll( node, \"script\" ) );\n\t\t\t}\n\t\t\tnode.parentNode.removeChild( node );\n\t\t}\n\t}\n\n\treturn elem;\n}\n\njQuery.extend( {\n\thtmlPrefilter: function( html ) {\n\t\treturn html;\n\t},\n\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar i, l, srcElements, destElements,\n\t\t\tclone = elem.cloneNode( true ),\n\t\t\tinPage = isAttached( elem );\n\n\t\t// Fix IE cloning issues\n\t\tif ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&\n\t\t\t\t!jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2\n\t\t\tdestElements = getAll( clone );\n\t\t\tsrcElements = getAll( elem );\n\n\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\tfixInput( srcElements[ i ], destElements[ i ] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = srcElements || getAll( elem );\n\t\t\t\tdestElements = destElements || getAll( clone );\n\n\t\t\t\tfor ( i = 0, l = srcElements.length; i < l; i++ ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[ i ], destElements[ i ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcloneCopyEvent( elem, clone );\n\t\t\t}\n\t\t}\n\n\t\t// Preserve script evaluation history\n\t\tdestElements = getAll( clone, \"script\" );\n\t\tif ( destElements.length > 0 ) {\n\t\t\tsetGlobalEval( destElements, !inPage && getAll( elem, \"script\" ) );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, elem, type,\n\t\t\tspecial = jQuery.event.special,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {\n\t\t\tif ( acceptData( elem ) ) {\n\t\t\t\tif ( ( data = elem[ dataPriv.expando ] ) ) {\n\t\t\t\t\tif ( data.events ) {\n\t\t\t\t\t\tfor ( type in data.events ) {\n\t\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\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// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataPriv.expando ] = undefined;\n\t\t\t\t}\n\t\t\t\tif ( elem[ dataUser.expando ] ) {\n\n\t\t\t\t\t// Support: Chrome <=35 - 45+\n\t\t\t\t\t// Assign undefined instead of using delete, see Data#remove\n\t\t\t\t\telem[ dataUser.expando ] = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} );\n\njQuery.fn.extend( {\n\tdetach: function( selector ) {\n\t\treturn remove( this, selector, true );\n\t},\n\n\tremove: function( selector ) {\n\t\treturn remove( this, selector );\n\t},\n\n\ttext: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\treturn value === undefined ?\n\t\t\t\tjQuery.text( this ) :\n\t\t\t\tthis.empty().each( function() {\n\t\t\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\t\t\tthis.textContent = value;\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t}, null, value, arguments.length );\n\t},\n\n\tappend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.appendChild( elem );\n\t\t\t}\n\t\t} );\n\t},\n\n\tprepend: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {\n\t\t\t\tvar target = manipulationTarget( this, elem );\n\t\t\t\ttarget.insertBefore( elem, target.firstChild );\n\t\t\t}\n\t\t} );\n\t},\n\n\tbefore: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t}\n\t\t} );\n\t},\n\n\tafter: function() {\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tif ( this.parentNode ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t}\n\t\t} );\n\t},\n\n\tempty: function() {\n\t\tvar elem,\n\t\t\ti = 0;\n\n\t\tfor ( ; ( elem = this[ i ] ) != null; i++ ) {\n\t\t\tif ( elem.nodeType === 1 ) {\n\n\t\t\t\t// Prevent memory leaks\n\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\n\t\t\t\t// Remove any remaining nodes\n\t\t\t\telem.textContent = \"\";\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function() {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t} );\n\t},\n\n\thtml: function( value ) {\n\t\treturn access( this, function( value ) {\n\t\t\tvar elem = this[ 0 ] || {},\n\t\t\t\ti = 0,\n\t\t\t\tl = this.length;\n\n\t\t\tif ( value === undefined && elem.nodeType === 1 ) {\n\t\t\t\treturn elem.innerHTML;\n\t\t\t}\n\n\t\t\t// See if we can take a shortcut and just use innerHTML\n\t\t\tif ( typeof value === \"string\" && !rnoInnerhtml.test( value ) &&\n\t\t\t\t!wrapMap[ ( rtagName.exec( value ) || [ \"\", \"\" ] )[ 1 ].toLowerCase() ] ) {\n\n\t\t\t\tvalue = jQuery.htmlPrefilter( value );\n\n\t\t\t\ttry {\n\t\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\t\telem = this[ i ] || {};\n\n\t\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\tjQuery.cleanData( getAll( elem, false ) );\n\t\t\t\t\t\t\telem.innerHTML = value;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\telem = 0;\n\n\t\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t\t} catch ( e ) {}\n\t\t\t}\n\n\t\t\tif ( elem ) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\t\t}, null, value, arguments.length );\n\t},\n\n\treplaceWith: function() {\n\t\tvar ignored = [];\n\n\t\t// Make the changes, replacing each non-ignored context element with the new content\n\t\treturn domManip( this, arguments, function( elem ) {\n\t\t\tvar parent = this.parentNode;\n\n\t\t\tif ( jQuery.inArray( this, ignored ) < 0 ) {\n\t\t\t\tjQuery.cleanData( getAll( this ) );\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.replaceChild( elem, this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Force callback invocation\n\t\t}, ignored );\n\t}\n} );\n\njQuery.each( {\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar elems,\n\t\t\tret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tlast = insert.length - 1,\n\t\t\ti = 0;\n\n\t\tfor ( ; i <= last; i++ ) {\n\t\t\telems = i === last ? this : this.clone( true );\n\t\t\tjQuery( insert[ i ] )[ original ]( elems );\n\n\t\t\t// Support: Android <=4.0 only, PhantomJS 1 only\n\t\t\t// .get() because push.apply(_, arraylike) throws on ancient WebKit\n\t\t\tpush.apply( ret, elems.get() );\n\t\t}\n\n\t\treturn this.pushStack( ret );\n\t};\n} );\nvar rnumnonpx = new RegExp( \"^(\" + pnum + \")(?!px)[a-z%]+$\", \"i\" );\n\nvar rcustomProp = /^--/;\n\n\nvar getStyles = function( elem ) {\n\n\t\t// Support: IE <=11 only, Firefox <=30 (trac-15098, trac-14150)\n\t\t// IE throws on elements created in popups\n\t\t// FF meanwhile throws on frame elements through \"defaultView.getComputedStyle\"\n\t\tvar view = elem.ownerDocument.defaultView;\n\n\t\tif ( !view || !view.opener ) {\n\t\t\tview = window;\n\t\t}\n\n\t\treturn view.getComputedStyle( elem );\n\t};\n\nvar swap = function( elem, options, callback ) {\n\tvar ret, name,\n\t\told = {};\n\n\t// Remember the old values, and insert the new ones\n\tfor ( name in options ) {\n\t\told[ name ] = elem.style[ name ];\n\t\telem.style[ name ] = options[ name ];\n\t}\n\n\tret = callback.call( elem );\n\n\t// Revert the old values\n\tfor ( name in options ) {\n\t\telem.style[ name ] = old[ name ];\n\t}\n\n\treturn ret;\n};\n\n\nvar rboxStyle = new RegExp( cssExpand.join( \"|\" ), \"i\" );\n\nvar whitespace = \"[\\\\x20\\\\t\\\\r\\\\n\\\\f]\";\n\n\nvar rtrimCSS = new RegExp(\n\t\"^\" + whitespace + \"+|((?:^|[^\\\\\\\\])(?:\\\\\\\\.)*)\" + whitespace + \"+$\",\n\t\"g\"\n);\n\n\n\n\n( function() {\n\n\t// Executing both pixelPosition & boxSizingReliable tests require only one layout\n\t// so they're executed at the same time to save the second computation.\n\tfunction computeStyleTests() {\n\n\t\t// This is a singleton, we need to execute it only once\n\t\tif ( !div ) {\n\t\t\treturn;\n\t\t}\n\n\t\tcontainer.style.cssText = \"position:absolute;left:-11111px;width:60px;\" +\n\t\t\t\"margin-top:1px;padding:0;border:0\";\n\t\tdiv.style.cssText =\n\t\t\t\"position:relative;display:block;box-sizing:border-box;overflow:scroll;\" +\n\t\t\t\"margin:auto;border:1px;padding:1px;\" +\n\t\t\t\"width:60%;top:1%\";\n\t\tdocumentElement.appendChild( container ).appendChild( div );\n\n\t\tvar divStyle = window.getComputedStyle( div );\n\t\tpixelPositionVal = divStyle.top !== \"1%\";\n\n\t\t// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44\n\t\treliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;\n\n\t\t// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3\n\t\t// Some styles come back with percentage values, even though they shouldn't\n\t\tdiv.style.right = \"60%\";\n\t\tpixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;\n\n\t\t// Support: IE 9 - 11 only\n\t\t// Detect misreporting of content dimensions for box-sizing:border-box elements\n\t\tboxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;\n\n\t\t// Support: IE 9 only\n\t\t// Detect overflow:scroll screwiness (gh-3699)\n\t\t// Support: Chrome <=64\n\t\t// Don't get tricked when zoom affects offsetWidth (gh-4029)\n\t\tdiv.style.position = \"absolute\";\n\t\tscrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;\n\n\t\tdocumentElement.removeChild( container );\n\n\t\t// Nullify the div so it wouldn't be stored in the memory and\n\t\t// it will also be a sign that checks already performed\n\t\tdiv = null;\n\t}\n\n\tfunction roundPixelMeasures( measure ) {\n\t\treturn Math.round( parseFloat( measure ) );\n\t}\n\n\tvar pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,\n\t\treliableTrDimensionsVal, reliableMarginLeftVal,\n\t\tcontainer = document.createElement( \"div\" ),\n\t\tdiv = document.createElement( \"div\" );\n\n\t// Finish early in limited (non-browser) environments\n\tif ( !div.style ) {\n\t\treturn;\n\t}\n\n\t// Support: IE <=9 - 11 only\n\t// Style of cloned element affects source element cloned (trac-8908)\n\tdiv.style.backgroundClip = \"content-box\";\n\tdiv.cloneNode( true ).style.backgroundClip = \"\";\n\tsupport.clearCloneStyle = div.style.backgroundClip === \"content-box\";\n\n\tjQuery.extend( support, {\n\t\tboxSizingReliable: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn boxSizingReliableVal;\n\t\t},\n\t\tpixelBoxStyles: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelBoxStylesVal;\n\t\t},\n\t\tpixelPosition: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn pixelPositionVal;\n\t\t},\n\t\treliableMarginLeft: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn reliableMarginLeftVal;\n\t\t},\n\t\tscrollboxSize: function() {\n\t\t\tcomputeStyleTests();\n\t\t\treturn scrollboxSizeVal;\n\t\t},\n\n\t\t// Support: IE 9 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Behavior in IE 9 is more subtle than in newer versions & it passes\n\t\t// some versions of this test; make sure not to make it pass there!\n\t\t//\n\t\t// Support: Firefox 70+\n\t\t// Only Firefox includes border widths\n\t\t// in computed dimensions. (gh-4529)\n\t\treliableTrDimensions: function() {\n\t\t\tvar table, tr, trChild, trStyle;\n\t\t\tif ( reliableTrDimensionsVal == null ) {\n\t\t\t\ttable = document.createElement( \"table\" );\n\t\t\t\ttr = document.createElement( \"tr\" );\n\t\t\t\ttrChild = document.createElement( \"div\" );\n\n\t\t\t\ttable.style.cssText = \"position:absolute;left:-11111px;border-collapse:separate\";\n\t\t\t\ttr.style.cssText = \"border:1px solid\";\n\n\t\t\t\t// Support: Chrome 86+\n\t\t\t\t// Height set through cssText does not get applied.\n\t\t\t\t// Computed height then comes back as 0.\n\t\t\t\ttr.style.height = \"1px\";\n\t\t\t\ttrChild.style.height = \"9px\";\n\n\t\t\t\t// Support: Android 8 Chrome 86+\n\t\t\t\t// In our bodyBackground.html iframe,\n\t\t\t\t// display for all div elements is set to \"inline\",\n\t\t\t\t// which causes a problem only in Android 8 Chrome 86.\n\t\t\t\t// Ensuring the div is display: block\n\t\t\t\t// gets around this issue.\n\t\t\t\ttrChild.style.display = \"block\";\n\n\t\t\t\tdocumentElement\n\t\t\t\t\t.appendChild( table )\n\t\t\t\t\t.appendChild( tr )\n\t\t\t\t\t.appendChild( trChild );\n\n\t\t\t\ttrStyle = window.getComputedStyle( tr );\n\t\t\t\treliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +\n\t\t\t\t\tparseInt( trStyle.borderTopWidth, 10 ) +\n\t\t\t\t\tparseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;\n\n\t\t\t\tdocumentElement.removeChild( table );\n\t\t\t}\n\t\t\treturn reliableTrDimensionsVal;\n\t\t}\n\t} );\n} )();\n\n\nfunction curCSS( elem, name, computed ) {\n\tvar width, minWidth, maxWidth, ret,\n\t\tisCustomProp = rcustomProp.test( name ),\n\n\t\t// Support: Firefox 51+\n\t\t// Retrieving style before computed somehow\n\t\t// fixes an issue with getting wrong values\n\t\t// on detached elements\n\t\tstyle = elem.style;\n\n\tcomputed = computed || getStyles( elem );\n\n\t// getPropertyValue is needed for:\n\t//   .css('filter') (IE 9 only, trac-12537)\n\t//   .css('--customProperty) (gh-3144)\n\tif ( computed ) {\n\n\t\t// Support: IE <=9 - 11+\n\t\t// IE only supports `\"float\"` in `getPropertyValue`; in computed styles\n\t\t// it's only available as `\"cssFloat\"`. We no longer modify properties\n\t\t// sent to `.css()` apart from camelCasing, so we need to check both.\n\t\t// Normally, this would create difference in behavior: if\n\t\t// `getPropertyValue` returns an empty string, the value returned\n\t\t// by `.css()` would be `undefined`. This is usually the case for\n\t\t// disconnected elements. However, in IE even disconnected elements\n\t\t// with no styles return `\"none\"` for `getPropertyValue( \"float\" )`\n\t\tret = computed.getPropertyValue( name ) || computed[ name ];\n\n\t\tif ( isCustomProp && ret ) {\n\n\t\t\t// Support: Firefox 105+, Chrome <=105+\n\t\t\t// Spec requires trimming whitespace for custom properties (gh-4926).\n\t\t\t// Firefox only trims leading whitespace. Chrome just collapses\n\t\t\t// both leading & trailing whitespace to a single space.\n\t\t\t//\n\t\t\t// Fall back to `undefined` if empty string returned.\n\t\t\t// This collapses a missing definition with property defined\n\t\t\t// and set to an empty string but there's no standard API\n\t\t\t// allowing us to differentiate them without a performance penalty\n\t\t\t// and returning `undefined` aligns with older jQuery.\n\t\t\t//\n\t\t\t// rtrimCSS treats U+000D CARRIAGE RETURN and U+000C FORM FEED\n\t\t\t// as whitespace while CSS does not, but this is not a problem\n\t\t\t// because CSS preprocessing replaces them with U+000A LINE FEED\n\t\t\t// (which *is* CSS whitespace)\n\t\t\t// https://www.w3.org/TR/css-syntax-3/#input-preprocessing\n\t\t\tret = ret.replace( rtrimCSS, \"$1\" ) || undefined;\n\t\t}\n\n\t\tif ( ret === \"\" && !isAttached( elem ) ) {\n\t\t\tret = jQuery.style( elem, name );\n\t\t}\n\n\t\t// A tribute to the \"awesome hack by Dean Edwards\"\n\t\t// Android Browser returns percentage for some values,\n\t\t// but width seems to be reliably pixels.\n\t\t// This is against the CSSOM draft spec:\n\t\t// https://drafts.csswg.org/cssom/#resolved-values\n\t\tif ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {\n\n\t\t\t// Remember the original values\n\t\t\twidth = style.width;\n\t\t\tminWidth = style.minWidth;\n\t\t\tmaxWidth = style.maxWidth;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tstyle.minWidth = style.maxWidth = style.width = ret;\n\t\t\tret = computed.width;\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.width = width;\n\t\t\tstyle.minWidth = minWidth;\n\t\t\tstyle.maxWidth = maxWidth;\n\t\t}\n\t}\n\n\treturn ret !== undefined ?\n\n\t\t// Support: IE <=9 - 11 only\n\t\t// IE returns zIndex value as an integer.\n\t\tret + \"\" :\n\t\tret;\n}\n\n\nfunction addGetHookIf( conditionFn, hookFn ) {\n\n\t// Define the hook, we'll check on the first run if it's really needed.\n\treturn {\n\t\tget: function() {\n\t\t\tif ( conditionFn() ) {\n\n\t\t\t\t// Hook not needed (or it's not possible to use it due\n\t\t\t\t// to missing dependency), remove it.\n\t\t\t\tdelete this.get;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Hook needed; redefine it so that the support test is not executed again.\n\t\t\treturn ( this.get = hookFn ).apply( this, arguments );\n\t\t}\n\t};\n}\n\n\nvar cssPrefixes = [ \"Webkit\", \"Moz\", \"ms\" ],\n\temptyStyle = document.createElement( \"div\" ).style,\n\tvendorProps = {};\n\n// Return a vendor-prefixed property or undefined\nfunction vendorPropName( name ) {\n\n\t// Check for vendor prefixed names\n\tvar capName = name[ 0 ].toUpperCase() + name.slice( 1 ),\n\t\ti = cssPrefixes.length;\n\n\twhile ( i-- ) {\n\t\tname = cssPrefixes[ i ] + capName;\n\t\tif ( name in emptyStyle ) {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n\n// Return a potentially-mapped jQuery.cssProps or vendor prefixed property\nfunction finalPropName( name ) {\n\tvar final = jQuery.cssProps[ name ] || vendorProps[ name ];\n\n\tif ( final ) {\n\t\treturn final;\n\t}\n\tif ( name in emptyStyle ) {\n\t\treturn name;\n\t}\n\treturn vendorProps[ name ] = vendorPropName( name ) || name;\n}\n\n\nvar\n\n\t// Swappable if display is none or starts with table\n\t// except \"table\", \"table-cell\", or \"table-caption\"\n\t// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display\n\trdisplayswap = /^(none|table(?!-c[ea]).+)/,\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssNormalTransform = {\n\t\tletterSpacing: \"0\",\n\t\tfontWeight: \"400\"\n\t};\n\nfunction setPositiveNumber( _elem, value, subtract ) {\n\n\t// Any relative (+/-) values have already been\n\t// normalized at this point\n\tvar matches = rcssNum.exec( value );\n\treturn matches ?\n\n\t\t// Guard against undefined \"subtract\", e.g., when used as in cssHooks\n\t\tMath.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || \"px\" ) :\n\t\tvalue;\n}\n\nfunction boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {\n\tvar i = dimension === \"width\" ? 1 : 0,\n\t\textra = 0,\n\t\tdelta = 0;\n\n\t// Adjustment may not be necessary\n\tif ( box === ( isBorderBox ? \"border\" : \"content\" ) ) {\n\t\treturn 0;\n\t}\n\n\tfor ( ; i < 4; i += 2 ) {\n\n\t\t// Both box models exclude margin\n\t\tif ( box === \"margin\" ) {\n\t\t\tdelta += jQuery.css( elem, box + cssExpand[ i ], true, styles );\n\t\t}\n\n\t\t// If we get here with a content-box, we're seeking \"padding\" or \"border\" or \"margin\"\n\t\tif ( !isBorderBox ) {\n\n\t\t\t// Add padding\n\t\t\tdelta += jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\n\t\t\t// For \"border\" or \"margin\", add border\n\t\t\tif ( box !== \"padding\" ) {\n\t\t\t\tdelta += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\n\t\t\t// But still keep track of it otherwise\n\t\t\t} else {\n\t\t\t\textra += jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\n\t\t// If we get here with a border-box (content + padding + border), we're seeking \"content\" or\n\t\t// \"padding\" or \"margin\"\n\t\t} else {\n\n\t\t\t// For \"content\", subtract padding\n\t\t\tif ( box === \"content\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"padding\" + cssExpand[ i ], true, styles );\n\t\t\t}\n\n\t\t\t// For \"content\" or \"padding\", subtract border\n\t\t\tif ( box !== \"margin\" ) {\n\t\t\t\tdelta -= jQuery.css( elem, \"border\" + cssExpand[ i ] + \"Width\", true, styles );\n\t\t\t}\n\t\t}\n\t}\n\n\t// Account for positive content-box scroll gutter when requested by providing computedVal\n\tif ( !isBorderBox && computedVal >= 0 ) {\n\n\t\t// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border\n\t\t// Assuming integer scroll gutter, subtract the rest and round down\n\t\tdelta += Math.max( 0, Math.ceil(\n\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\tcomputedVal -\n\t\t\tdelta -\n\t\t\textra -\n\t\t\t0.5\n\n\t\t// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter\n\t\t// Use an explicit zero to avoid NaN (gh-3964)\n\t\t) ) || 0;\n\t}\n\n\treturn delta;\n}\n\nfunction getWidthOrHeight( elem, dimension, extra ) {\n\n\t// Start with computed style\n\tvar styles = getStyles( elem ),\n\n\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).\n\t\t// Fake content-box until we know it's needed to know the true value.\n\t\tboxSizingNeeded = !support.boxSizingReliable() || extra,\n\t\tisBorderBox = boxSizingNeeded &&\n\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\tvalueIsBorderBox = isBorderBox,\n\n\t\tval = curCSS( elem, dimension, styles ),\n\t\toffsetProp = \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );\n\n\t// Support: Firefox <=54\n\t// Return a confounding non-pixel value or feign ignorance, as appropriate.\n\tif ( rnumnonpx.test( val ) ) {\n\t\tif ( !extra ) {\n\t\t\treturn val;\n\t\t}\n\t\tval = \"auto\";\n\t}\n\n\n\t// Support: IE 9 - 11 only\n\t// Use offsetWidth/offsetHeight for when box sizing is unreliable.\n\t// In those cases, the computed value can be trusted to be border-box.\n\tif ( ( !support.boxSizingReliable() && isBorderBox ||\n\n\t\t// Support: IE 10 - 11+, Edge 15 - 18+\n\t\t// IE/Edge misreport `getComputedStyle` of table rows with width/height\n\t\t// set in CSS while `offset*` properties report correct values.\n\t\t// Interestingly, in some cases IE 9 doesn't suffer from this issue.\n\t\t!support.reliableTrDimensions() && nodeName( elem, \"tr\" ) ||\n\n\t\t// Fall back to offsetWidth/offsetHeight when value is \"auto\"\n\t\t// This happens for inline elements with no explicit setting (gh-3571)\n\t\tval === \"auto\" ||\n\n\t\t// Support: Android <=4.1 - 4.3 only\n\t\t// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)\n\t\t!parseFloat( val ) && jQuery.css( elem, \"display\", false, styles ) === \"inline\" ) &&\n\n\t\t// Make sure the element is visible & connected\n\t\telem.getClientRects().length ) {\n\n\t\tisBorderBox = jQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\";\n\n\t\t// Where available, offsetWidth/offsetHeight approximate border box dimensions.\n\t\t// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the\n\t\t// retrieved value as a content box dimension.\n\t\tvalueIsBorderBox = offsetProp in elem;\n\t\tif ( valueIsBorderBox ) {\n\t\t\tval = elem[ offsetProp ];\n\t\t}\n\t}\n\n\t// Normalize \"\" and auto\n\tval = parseFloat( val ) || 0;\n\n\t// Adjust for the element's box model\n\treturn ( val +\n\t\tboxModelAdjustment(\n\t\t\telem,\n\t\t\tdimension,\n\t\t\textra || ( isBorderBox ? \"border\" : \"content\" ),\n\t\t\tvalueIsBorderBox,\n\t\t\tstyles,\n\n\t\t\t// Provide the current computed size to request scroll gutter calculation (gh-3589)\n\t\t\tval\n\t\t)\n\t) + \"px\";\n}\n\njQuery.extend( {\n\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Don't automatically add \"px\" to these possibly-unitless properties\n\tcssNumber: {\n\t\t\"animationIterationCount\": true,\n\t\t\"columnCount\": true,\n\t\t\"fillOpacity\": true,\n\t\t\"flexGrow\": true,\n\t\t\"flexShrink\": true,\n\t\t\"fontWeight\": true,\n\t\t\"gridArea\": true,\n\t\t\"gridColumn\": true,\n\t\t\"gridColumnEnd\": true,\n\t\t\"gridColumnStart\": true,\n\t\t\"gridRow\": true,\n\t\t\"gridRowEnd\": true,\n\t\t\"gridRowStart\": true,\n\t\t\"lineHeight\": true,\n\t\t\"opacity\": true,\n\t\t\"order\": true,\n\t\t\"orphans\": true,\n\t\t\"widows\": true,\n\t\t\"zIndex\": true,\n\t\t\"zoom\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, type, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name ),\n\t\t\tstyle = elem.style;\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to query the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Gets hook for the prefixed version, then unprefixed version\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\ttype = typeof value;\n\n\t\t\t// Convert \"+=\" or \"-=\" to relative numbers (trac-7345)\n\t\t\tif ( type === \"string\" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {\n\t\t\t\tvalue = adjustCSS( elem, name, ret );\n\n\t\t\t\t// Fixes bug trac-9237\n\t\t\t\ttype = \"number\";\n\t\t\t}\n\n\t\t\t// Make sure that null and NaN values aren't set (trac-7116)\n\t\t\tif ( value == null || value !== value ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add the unit (except for certain CSS properties)\n\t\t\t// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append\n\t\t\t// \"px\" to a few hardcoded values.\n\t\t\tif ( type === \"number\" && !isCustomProp ) {\n\t\t\t\tvalue += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? \"\" : \"px\" );\n\t\t\t}\n\n\t\t\t// background-* props affect original clone's values\n\t\t\tif ( !support.clearCloneStyle && value === \"\" && name.indexOf( \"background\" ) === 0 ) {\n\t\t\t\tstyle[ name ] = \"inherit\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !( \"set\" in hooks ) ||\n\t\t\t\t( value = hooks.set( elem, value, extra ) ) !== undefined ) {\n\n\t\t\t\tif ( isCustomProp ) {\n\t\t\t\t\tstyle.setProperty( name, value );\n\t\t\t\t} else {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks &&\n\t\t\t\t( ret = hooks.get( elem, false, extra ) ) !== undefined ) {\n\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra, styles ) {\n\t\tvar val, num, hooks,\n\t\t\torigName = camelCase( name ),\n\t\t\tisCustomProp = rcustomProp.test( name );\n\n\t\t// Make sure that we're working with the right name. We don't\n\t\t// want to modify the value if it is a CSS custom property\n\t\t// since they are user-defined.\n\t\tif ( !isCustomProp ) {\n\t\t\tname = finalPropName( origName );\n\t\t}\n\n\t\t// Try prefixed name followed by the unprefixed name\n\t\thooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks ) {\n\t\t\tval = hooks.get( elem, true, extra );\n\t\t}\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\tif ( val === undefined ) {\n\t\t\tval = curCSS( elem, name, styles );\n\t\t}\n\n\t\t// Convert \"normal\" to computed value\n\t\tif ( val === \"normal\" && name in cssNormalTransform ) {\n\t\t\tval = cssNormalTransform[ name ];\n\t\t}\n\n\t\t// Make numeric if forced or a qualifier was provided and val looks numeric\n\t\tif ( extra === \"\" || extra ) {\n\t\t\tnum = parseFloat( val );\n\t\t\treturn extra === true || isFinite( num ) ? num || 0 : val;\n\t\t}\n\n\t\treturn val;\n\t}\n} );\n\njQuery.each( [ \"height\", \"width\" ], function( _i, dimension ) {\n\tjQuery.cssHooks[ dimension ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tif ( computed ) {\n\n\t\t\t\t// Certain elements can have dimension info if we invisibly show them\n\t\t\t\t// but it must have a current display style that would benefit\n\t\t\t\treturn rdisplayswap.test( jQuery.css( elem, \"display\" ) ) &&\n\n\t\t\t\t\t// Support: Safari 8+\n\t\t\t\t\t// Table columns in Safari have non-zero offsetWidth & zero\n\t\t\t\t\t// getBoundingClientRect().width unless display is changed.\n\t\t\t\t\t// Support: IE <=11 only\n\t\t\t\t\t// Running getBoundingClientRect on a disconnected node\n\t\t\t\t\t// in IE throws an error.\n\t\t\t\t\t( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?\n\t\t\t\t\tswap( elem, cssShow, function() {\n\t\t\t\t\t\treturn getWidthOrHeight( elem, dimension, extra );\n\t\t\t\t\t} ) :\n\t\t\t\t\tgetWidthOrHeight( elem, dimension, extra );\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value, extra ) {\n\t\t\tvar matches,\n\t\t\t\tstyles = getStyles( elem ),\n\n\t\t\t\t// Only read styles.position if the test has a chance to fail\n\t\t\t\t// to avoid forcing a reflow.\n\t\t\t\tscrollboxSizeBuggy = !support.scrollboxSize() &&\n\t\t\t\t\tstyles.position === \"absolute\",\n\n\t\t\t\t// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)\n\t\t\t\tboxSizingNeeded = scrollboxSizeBuggy || extra,\n\t\t\t\tisBorderBox = boxSizingNeeded &&\n\t\t\t\t\tjQuery.css( elem, \"boxSizing\", false, styles ) === \"border-box\",\n\t\t\t\tsubtract = extra ?\n\t\t\t\t\tboxModelAdjustment(\n\t\t\t\t\t\telem,\n\t\t\t\t\t\tdimension,\n\t\t\t\t\t\textra,\n\t\t\t\t\t\tisBorderBox,\n\t\t\t\t\t\tstyles\n\t\t\t\t\t) :\n\t\t\t\t\t0;\n\n\t\t\t// Account for unreliable border-box dimensions by comparing offset* to computed and\n\t\t\t// faking a content-box to get border and padding (gh-3699)\n\t\t\tif ( isBorderBox && scrollboxSizeBuggy ) {\n\t\t\t\tsubtract -= Math.ceil(\n\t\t\t\t\telem[ \"offset\" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -\n\t\t\t\t\tparseFloat( styles[ dimension ] ) -\n\t\t\t\t\tboxModelAdjustment( elem, dimension, \"border\", false, styles ) -\n\t\t\t\t\t0.5\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert to pixels if value adjustment is needed\n\t\t\tif ( subtract && ( matches = rcssNum.exec( value ) ) &&\n\t\t\t\t( matches[ 3 ] || \"px\" ) !== \"px\" ) {\n\n\t\t\t\telem.style[ dimension ] = value;\n\t\t\t\tvalue = jQuery.css( elem, dimension );\n\t\t\t}\n\n\t\t\treturn setPositiveNumber( elem, value, subtract );\n\t\t}\n\t};\n} );\n\njQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,\n\tfunction( elem, computed ) {\n\t\tif ( computed ) {\n\t\t\treturn ( parseFloat( curCSS( elem, \"marginLeft\" ) ) ||\n\t\t\t\telem.getBoundingClientRect().left -\n\t\t\t\t\tswap( elem, { marginLeft: 0 }, function() {\n\t\t\t\t\t\treturn elem.getBoundingClientRect().left;\n\t\t\t\t\t} )\n\t\t\t) + \"px\";\n\t\t}\n\t}\n);\n\n// These hooks are used by animate to expand properties\njQuery.each( {\n\tmargin: \"\",\n\tpadding: \"\",\n\tborder: \"Width\"\n}, function( prefix, suffix ) {\n\tjQuery.cssHooks[ prefix + suffix ] = {\n\t\texpand: function( value ) {\n\t\t\tvar i = 0,\n\t\t\t\texpanded = {},\n\n\t\t\t\t// Assumes a single number if not a string\n\t\t\t\tparts = typeof value === \"string\" ? value.split( \" \" ) : [ value ];\n\n\t\t\tfor ( ; i < 4; i++ ) {\n\t\t\t\texpanded[ prefix + cssExpand[ i ] + suffix ] =\n\t\t\t\t\tparts[ i ] || parts[ i - 2 ] || parts[ 0 ];\n\t\t\t}\n\n\t\t\treturn expanded;\n\t\t}\n\t};\n\n\tif ( prefix !== \"margin\" ) {\n\t\tjQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;\n\t}\n} );\n\njQuery.fn.extend( {\n\tcss: function( name, value ) {\n\t\treturn access( this, function( elem, name, value ) {\n\t\t\tvar styles, len,\n\t\t\t\tmap = {},\n\t\t\t\ti = 0;\n\n\t\t\tif ( Array.isArray( name ) ) {\n\t\t\t\tstyles = getStyles( elem );\n\t\t\t\tlen = name.length;\n\n\t\t\t\tfor ( ; i < len; i++ ) {\n\t\t\t\t\tmap[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );\n\t\t\t\t}\n\n\t\t\t\treturn map;\n\t\t\t}\n\n\t\t\treturn value !== undefined ?\n\t\t\t\tjQuery.style( elem, name, value ) :\n\t\t\t\tjQuery.css( elem, name );\n\t\t}, name, value, arguments.length > 1 );\n\t}\n} );\n\n\nfunction Tween( elem, options, prop, end, easing ) {\n\treturn new Tween.prototype.init( elem, options, prop, end, easing );\n}\njQuery.Tween = Tween;\n\nTween.prototype = {\n\tconstructor: Tween,\n\tinit: function( elem, options, prop, end, easing, unit ) {\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\t\tthis.easing = easing || jQuery.easing._default;\n\t\tthis.options = options;\n\t\tthis.start = this.now = this.cur();\n\t\tthis.end = end;\n\t\tthis.unit = unit || ( jQuery.cssNumber[ prop ] ? \"\" : \"px\" );\n\t},\n\tcur: function() {\n\t\tvar hooks = Tween.propHooks[ this.prop ];\n\n\t\treturn hooks && hooks.get ?\n\t\t\thooks.get( this ) :\n\t\t\tTween.propHooks._default.get( this );\n\t},\n\trun: function( percent ) {\n\t\tvar eased,\n\t\t\thooks = Tween.propHooks[ this.prop ];\n\n\t\tif ( this.options.duration ) {\n\t\t\tthis.pos = eased = jQuery.easing[ this.easing ](\n\t\t\t\tpercent, this.options.duration * percent, 0, 1, this.options.duration\n\t\t\t);\n\t\t} else {\n\t\t\tthis.pos = eased = percent;\n\t\t}\n\t\tthis.now = ( this.end - this.start ) * eased + this.start;\n\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\tif ( hooks && hooks.set ) {\n\t\t\thooks.set( this );\n\t\t} else {\n\t\t\tTween.propHooks._default.set( this );\n\t\t}\n\t\treturn this;\n\t}\n};\n\nTween.prototype.init.prototype = Tween.prototype;\n\nTween.propHooks = {\n\t_default: {\n\t\tget: function( tween ) {\n\t\t\tvar result;\n\n\t\t\t// Use a property on the element directly when it is not a DOM element,\n\t\t\t// or when there is no matching style property that exists.\n\t\t\tif ( tween.elem.nodeType !== 1 ||\n\t\t\t\ttween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {\n\t\t\t\treturn tween.elem[ tween.prop ];\n\t\t\t}\n\n\t\t\t// Passing an empty string as a 3rd parameter to .css will automatically\n\t\t\t// attempt a parseFloat and fallback to a string if the parse fails.\n\t\t\t// Simple values such as \"10px\" are parsed to Float;\n\t\t\t// complex values such as \"rotate(1rad)\" are returned as-is.\n\t\t\tresult = jQuery.css( tween.elem, tween.prop, \"\" );\n\n\t\t\t// Empty strings, null, undefined and \"auto\" are converted to 0.\n\t\t\treturn !result || result === \"auto\" ? 0 : result;\n\t\t},\n\t\tset: function( tween ) {\n\n\t\t\t// Use step hook for back compat.\n\t\t\t// Use cssHook if its there.\n\t\t\t// Use .style if available and use plain properties where available.\n\t\t\tif ( jQuery.fx.step[ tween.prop ] ) {\n\t\t\t\tjQuery.fx.step[ tween.prop ]( tween );\n\t\t\t} else if ( tween.elem.nodeType === 1 && (\n\t\t\t\tjQuery.cssHooks[ tween.prop ] ||\n\t\t\t\t\ttween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {\n\t\t\t\tjQuery.style( tween.elem, tween.prop, tween.now + tween.unit );\n\t\t\t} else {\n\t\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t\t}\n\t\t}\n\t}\n};\n\n// Support: IE <=9 only\n// Panic based approach to setting things on disconnected nodes\nTween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {\n\tset: function( tween ) {\n\t\tif ( tween.elem.nodeType && tween.elem.parentNode ) {\n\t\t\ttween.elem[ tween.prop ] = tween.now;\n\t\t}\n\t}\n};\n\njQuery.easing = {\n\tlinear: function( p ) {\n\t\treturn p;\n\t},\n\tswing: function( p ) {\n\t\treturn 0.5 - Math.cos( p * Math.PI ) / 2;\n\t},\n\t_default: \"swing\"\n};\n\njQuery.fx = Tween.prototype.init;\n\n// Back compat <1.8 extension point\njQuery.fx.step = {};\n\n\n\n\nvar\n\tfxNow, inProgress,\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trrun = /queueHooks$/;\n\nfunction schedule() {\n\tif ( inProgress ) {\n\t\tif ( document.hidden === false && window.requestAnimationFrame ) {\n\t\t\twindow.requestAnimationFrame( schedule );\n\t\t} else {\n\t\t\twindow.setTimeout( schedule, jQuery.fx.interval );\n\t\t}\n\n\t\tjQuery.fx.tick();\n\t}\n}\n\n// Animations created synchronously will run synchronously\nfunction createFxNow() {\n\twindow.setTimeout( function() {\n\t\tfxNow = undefined;\n\t} );\n\treturn ( fxNow = Date.now() );\n}\n\n// Generate parameters to create a standard animation\nfunction genFx( type, includeWidth ) {\n\tvar which,\n\t\ti = 0,\n\t\tattrs = { height: type };\n\n\t// If we include width, step value is 1 to do all cssExpand values,\n\t// otherwise step value is 2 to skip over Left and Right\n\tincludeWidth = includeWidth ? 1 : 0;\n\tfor ( ; i < 4; i += 2 - includeWidth ) {\n\t\twhich = cssExpand[ i ];\n\t\tattrs[ \"margin\" + which ] = attrs[ \"padding\" + which ] = type;\n\t}\n\n\tif ( includeWidth ) {\n\t\tattrs.opacity = attrs.width = type;\n\t}\n\n\treturn attrs;\n}\n\nfunction createTween( value, prop, animation ) {\n\tvar tween,\n\t\tcollection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ \"*\" ] ),\n\t\tindex = 0,\n\t\tlength = collection.length;\n\tfor ( ; index < length; index++ ) {\n\t\tif ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {\n\n\t\t\t// We're done with this property\n\t\t\treturn tween;\n\t\t}\n\t}\n}\n\nfunction defaultPrefilter( elem, props, opts ) {\n\tvar prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,\n\t\tisBox = \"width\" in props || \"height\" in props,\n\t\tanim = this,\n\t\torig = {},\n\t\tstyle = elem.style,\n\t\thidden = elem.nodeType && isHiddenWithinTree( elem ),\n\t\tdataShow = dataPriv.get( elem, \"fxshow\" );\n\n\t// Queue-skipping animations hijack the fx hooks\n\tif ( !opts.queue ) {\n\t\thooks = jQuery._queueHooks( elem, \"fx\" );\n\t\tif ( hooks.unqueued == null ) {\n\t\t\thooks.unqueued = 0;\n\t\t\toldfire = hooks.empty.fire;\n\t\t\thooks.empty.fire = function() {\n\t\t\t\tif ( !hooks.unqueued ) {\n\t\t\t\t\toldfire();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\thooks.unqueued++;\n\n\t\tanim.always( function() {\n\n\t\t\t// Ensure the complete handler is called before this completes\n\t\t\tanim.always( function() {\n\t\t\t\thooks.unqueued--;\n\t\t\t\tif ( !jQuery.queue( elem, \"fx\" ).length ) {\n\t\t\t\t\thooks.empty.fire();\n\t\t\t\t}\n\t\t\t} );\n\t\t} );\n\t}\n\n\t// Detect show/hide animations\n\tfor ( prop in props ) {\n\t\tvalue = props[ prop ];\n\t\tif ( rfxtypes.test( value ) ) {\n\t\t\tdelete props[ prop ];\n\t\t\ttoggle = toggle || value === \"toggle\";\n\t\t\tif ( value === ( hidden ? \"hide\" : \"show\" ) ) {\n\n\t\t\t\t// Pretend to be hidden if this is a \"show\" and\n\t\t\t\t// there is still data from a stopped show/hide\n\t\t\t\tif ( value === \"show\" && dataShow && dataShow[ prop ] !== undefined ) {\n\t\t\t\t\thidden = true;\n\n\t\t\t\t// Ignore all other no-op show/hide data\n\t\t\t\t} else {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\torig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );\n\t\t}\n\t}\n\n\t// Bail out if this is a no-op like .hide().hide()\n\tpropTween = !jQuery.isEmptyObject( props );\n\tif ( !propTween && jQuery.isEmptyObject( orig ) ) {\n\t\treturn;\n\t}\n\n\t// Restrict \"overflow\" and \"display\" styles during box animations\n\tif ( isBox && elem.nodeType === 1 ) {\n\n\t\t// Support: IE <=9 - 11, Edge 12 - 15\n\t\t// Record all 3 overflow attributes because IE does not infer the shorthand\n\t\t// from identically-valued overflowX and overflowY and Edge just mirrors\n\t\t// the overflowX value there.\n\t\topts.overflow = [ style.overflow, style.overflowX, style.overflowY ];\n\n\t\t// Identify a display type, preferring old show/hide data over the CSS cascade\n\t\trestoreDisplay = dataShow && dataShow.display;\n\t\tif ( restoreDisplay == null ) {\n\t\t\trestoreDisplay = dataPriv.get( elem, \"display\" );\n\t\t}\n\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\tif ( display === \"none\" ) {\n\t\t\tif ( restoreDisplay ) {\n\t\t\t\tdisplay = restoreDisplay;\n\t\t\t} else {\n\n\t\t\t\t// Get nonempty value(s) by temporarily forcing visibility\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t\trestoreDisplay = elem.style.display || restoreDisplay;\n\t\t\t\tdisplay = jQuery.css( elem, \"display\" );\n\t\t\t\tshowHide( [ elem ] );\n\t\t\t}\n\t\t}\n\n\t\t// Animate inline elements as inline-block\n\t\tif ( display === \"inline\" || display === \"inline-block\" && restoreDisplay != null ) {\n\t\t\tif ( jQuery.css( elem, \"float\" ) === \"none\" ) {\n\n\t\t\t\t// Restore the original display value at the end of pure show/hide animations\n\t\t\t\tif ( !propTween ) {\n\t\t\t\t\tanim.done( function() {\n\t\t\t\t\t\tstyle.display = restoreDisplay;\n\t\t\t\t\t} );\n\t\t\t\t\tif ( restoreDisplay == null ) {\n\t\t\t\t\t\tdisplay = style.display;\n\t\t\t\t\t\trestoreDisplay = display === \"none\" ? \"\" : display;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstyle.display = \"inline-block\";\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( opts.overflow ) {\n\t\tstyle.overflow = \"hidden\";\n\t\tanim.always( function() {\n\t\t\tstyle.overflow = opts.overflow[ 0 ];\n\t\t\tstyle.overflowX = opts.overflow[ 1 ];\n\t\t\tstyle.overflowY = opts.overflow[ 2 ];\n\t\t} );\n\t}\n\n\t// Implement show/hide animations\n\tpropTween = false;\n\tfor ( prop in orig ) {\n\n\t\t// General show/hide setup for this element animation\n\t\tif ( !propTween ) {\n\t\t\tif ( dataShow ) {\n\t\t\t\tif ( \"hidden\" in dataShow ) {\n\t\t\t\t\thidden = dataShow.hidden;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdataShow = dataPriv.access( elem, \"fxshow\", { display: restoreDisplay } );\n\t\t\t}\n\n\t\t\t// Store hidden/visible for toggle so `.stop().toggle()` \"reverses\"\n\t\t\tif ( toggle ) {\n\t\t\t\tdataShow.hidden = !hidden;\n\t\t\t}\n\n\t\t\t// Show elements before animating them\n\t\t\tif ( hidden ) {\n\t\t\t\tshowHide( [ elem ], true );\n\t\t\t}\n\n\t\t\t/* eslint-disable no-loop-func */\n\n\t\t\tanim.done( function() {\n\n\t\t\t\t/* eslint-enable no-loop-func */\n\n\t\t\t\t// The final step of a \"hide\" animation is actually hiding the element\n\t\t\t\tif ( !hidden ) {\n\t\t\t\t\tshowHide( [ elem ] );\n\t\t\t\t}\n\t\t\t\tdataPriv.remove( elem, \"fxshow\" );\n\t\t\t\tfor ( prop in orig ) {\n\t\t\t\t\tjQuery.style( elem, prop, orig[ prop ] );\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\t// Per-property setup\n\t\tpropTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );\n\t\tif ( !( prop in dataShow ) ) {\n\t\t\tdataShow[ prop ] = propTween.start;\n\t\t\tif ( hidden ) {\n\t\t\t\tpropTween.end = propTween.start;\n\t\t\t\tpropTween.start = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction propFilter( props, specialEasing ) {\n\tvar index, name, easing, value, hooks;\n\n\t// camelCase, specialEasing and expand cssHook pass\n\tfor ( index in props ) {\n\t\tname = camelCase( index );\n\t\teasing = specialEasing[ name ];\n\t\tvalue = props[ index ];\n\t\tif ( Array.isArray( value ) ) {\n\t\t\teasing = value[ 1 ];\n\t\t\tvalue = props[ index ] = value[ 0 ];\n\t\t}\n\n\t\tif ( index !== name ) {\n\t\t\tprops[ name ] = value;\n\t\t\tdelete props[ index ];\n\t\t}\n\n\t\thooks = jQuery.cssHooks[ name ];\n\t\tif ( hooks && \"expand\" in hooks ) {\n\t\t\tvalue = hooks.expand( value );\n\t\t\tdelete props[ name ];\n\n\t\t\t// Not quite $.extend, this won't overwrite existing keys.\n\t\t\t// Reusing 'index' because we have the correct \"name\"\n\t\t\tfor ( index in value ) {\n\t\t\t\tif ( !( index in props ) ) {\n\t\t\t\t\tprops[ index ] = value[ index ];\n\t\t\t\t\tspecialEasing[ index ] = easing;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tspecialEasing[ name ] = easing;\n\t\t}\n\t}\n}\n\nfunction Animation( elem, properties, options ) {\n\tvar result,\n\t\tstopped,\n\t\tindex = 0,\n\t\tlength = Animation.prefilters.length,\n\t\tdeferred = jQuery.Deferred().always( function() {\n\n\t\t\t// Don't match elem in the :animated selector\n\t\t\tdelete tick.elem;\n\t\t} ),\n\t\ttick = function() {\n\t\t\tif ( stopped ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tvar currentTime = fxNow || createFxNow(),\n\t\t\t\tremaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),\n\n\t\t\t\t// Support: Android 2.3 only\n\t\t\t\t// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (trac-12497)\n\t\t\t\ttemp = remaining / animation.duration || 0,\n\t\t\t\tpercent = 1 - temp,\n\t\t\t\tindex = 0,\n\t\t\t\tlength = animation.tweens.length;\n\n\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\tanimation.tweens[ index ].run( percent );\n\t\t\t}\n\n\t\t\tdeferred.notifyWith( elem, [ animation, percent, remaining ] );\n\n\t\t\t// If there's more to do, yield\n\t\t\tif ( percent < 1 && length ) {\n\t\t\t\treturn remaining;\n\t\t\t}\n\n\t\t\t// If this was an empty animation, synthesize a final progress notification\n\t\t\tif ( !length ) {\n\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t}\n\n\t\t\t// Resolve the animation and report its conclusion\n\t\t\tdeferred.resolveWith( elem, [ animation ] );\n\t\t\treturn false;\n\t\t},\n\t\tanimation = deferred.promise( {\n\t\t\telem: elem,\n\t\t\tprops: jQuery.extend( {}, properties ),\n\t\t\topts: jQuery.extend( true, {\n\t\t\t\tspecialEasing: {},\n\t\t\t\teasing: jQuery.easing._default\n\t\t\t}, options ),\n\t\t\toriginalProperties: properties,\n\t\t\toriginalOptions: options,\n\t\t\tstartTime: fxNow || createFxNow(),\n\t\t\tduration: options.duration,\n\t\t\ttweens: [],\n\t\t\tcreateTween: function( prop, end ) {\n\t\t\t\tvar tween = jQuery.Tween( elem, animation.opts, prop, end,\n\t\t\t\t\tanimation.opts.specialEasing[ prop ] || animation.opts.easing );\n\t\t\t\tanimation.tweens.push( tween );\n\t\t\t\treturn tween;\n\t\t\t},\n\t\t\tstop: function( gotoEnd ) {\n\t\t\t\tvar index = 0,\n\n\t\t\t\t\t// If we are going to the end, we want to run all the tweens\n\t\t\t\t\t// otherwise we skip this part\n\t\t\t\t\tlength = gotoEnd ? animation.tweens.length : 0;\n\t\t\t\tif ( stopped ) {\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t\tstopped = true;\n\t\t\t\tfor ( ; index < length; index++ ) {\n\t\t\t\t\tanimation.tweens[ index ].run( 1 );\n\t\t\t\t}\n\n\t\t\t\t// Resolve when we played the last frame; otherwise, reject\n\t\t\t\tif ( gotoEnd ) {\n\t\t\t\t\tdeferred.notifyWith( elem, [ animation, 1, 0 ] );\n\t\t\t\t\tdeferred.resolveWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t} else {\n\t\t\t\t\tdeferred.rejectWith( elem, [ animation, gotoEnd ] );\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\t\t} ),\n\t\tprops = animation.props;\n\n\tpropFilter( props, animation.opts.specialEasing );\n\n\tfor ( ; index < length; index++ ) {\n\t\tresult = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );\n\t\tif ( result ) {\n\t\t\tif ( isFunction( result.stop ) ) {\n\t\t\t\tjQuery._queueHooks( animation.elem, animation.opts.queue ).stop =\n\t\t\t\t\tresult.stop.bind( result );\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tjQuery.map( props, createTween, animation );\n\n\tif ( isFunction( animation.opts.start ) ) {\n\t\tanimation.opts.start.call( elem, animation );\n\t}\n\n\t// Attach callbacks from options\n\tanimation\n\t\t.progress( animation.opts.progress )\n\t\t.done( animation.opts.done, animation.opts.complete )\n\t\t.fail( animation.opts.fail )\n\t\t.always( animation.opts.always );\n\n\tjQuery.fx.timer(\n\t\tjQuery.extend( tick, {\n\t\t\telem: elem,\n\t\t\tanim: animation,\n\t\t\tqueue: animation.opts.queue\n\t\t} )\n\t);\n\n\treturn animation;\n}\n\njQuery.Animation = jQuery.extend( Animation, {\n\n\ttweeners: {\n\t\t\"*\": [ function( prop, value ) {\n\t\t\tvar tween = this.createTween( prop, value );\n\t\t\tadjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );\n\t\t\treturn tween;\n\t\t} ]\n\t},\n\n\ttweener: function( props, callback ) {\n\t\tif ( isFunction( props ) ) {\n\t\t\tcallback = props;\n\t\t\tprops = [ \"*\" ];\n\t\t} else {\n\t\t\tprops = props.match( rnothtmlwhite );\n\t\t}\n\n\t\tvar prop,\n\t\t\tindex = 0,\n\t\t\tlength = props.length;\n\n\t\tfor ( ; index < length; index++ ) {\n\t\t\tprop = props[ index ];\n\t\t\tAnimation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];\n\t\t\tAnimation.tweeners[ prop ].unshift( callback );\n\t\t}\n\t},\n\n\tprefilters: [ defaultPrefilter ],\n\n\tprefilter: function( callback, prepend ) {\n\t\tif ( prepend ) {\n\t\t\tAnimation.prefilters.unshift( callback );\n\t\t} else {\n\t\t\tAnimation.prefilters.push( callback );\n\t\t}\n\t}\n} );\n\njQuery.speed = function( speed, easing, fn ) {\n\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend( {}, speed ) : {\n\t\tcomplete: fn || !fn && easing ||\n\t\t\tisFunction( speed ) && speed,\n\t\tduration: speed,\n\t\teasing: fn && easing || easing && !isFunction( easing ) && easing\n\t};\n\n\t// Go to the end state if fx are off\n\tif ( jQuery.fx.off ) {\n\t\topt.duration = 0;\n\n\t} else {\n\t\tif ( typeof opt.duration !== \"number\" ) {\n\t\t\tif ( opt.duration in jQuery.fx.speeds ) {\n\t\t\t\topt.duration = jQuery.fx.speeds[ opt.duration ];\n\n\t\t\t} else {\n\t\t\t\topt.duration = jQuery.fx.speeds._default;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Normalize opt.queue - true/undefined/null -> \"fx\"\n\tif ( opt.queue == null || opt.queue === true ) {\n\t\topt.queue = \"fx\";\n\t}\n\n\t// Queueing\n\topt.old = opt.complete;\n\n\topt.complete = function() {\n\t\tif ( isFunction( opt.old ) ) {\n\t\t\topt.old.call( this );\n\t\t}\n\n\t\tif ( opt.queue ) {\n\t\t\tjQuery.dequeue( this, opt.queue );\n\t\t}\n\t};\n\n\treturn opt;\n};\n\njQuery.fn.extend( {\n\tfadeTo: function( speed, to, easing, callback ) {\n\n\t\t// Show any hidden elements after setting opacity to 0\n\t\treturn this.filter( isHiddenWithinTree ).css( \"opacity\", 0 ).show()\n\n\t\t\t// Animate to the value specified\n\t\t\t.end().animate( { opacity: to }, speed, easing, callback );\n\t},\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar empty = jQuery.isEmptyObject( prop ),\n\t\t\toptall = jQuery.speed( speed, easing, callback ),\n\t\t\tdoAnimation = function() {\n\n\t\t\t\t// Operate on a copy of prop so per-property easing won't be lost\n\t\t\t\tvar anim = Animation( this, jQuery.extend( {}, prop ), optall );\n\n\t\t\t\t// Empty animations, or finishing resolves immediately\n\t\t\t\tif ( empty || dataPriv.get( this, \"finish\" ) ) {\n\t\t\t\t\tanim.stop( true );\n\t\t\t\t}\n\t\t\t};\n\n\t\tdoAnimation.finish = doAnimation;\n\n\t\treturn empty || optall.queue === false ?\n\t\t\tthis.each( doAnimation ) :\n\t\t\tthis.queue( optall.queue, doAnimation );\n\t},\n\tstop: function( type, clearQueue, gotoEnd ) {\n\t\tvar stopQueue = function( hooks ) {\n\t\t\tvar stop = hooks.stop;\n\t\t\tdelete hooks.stop;\n\t\t\tstop( gotoEnd );\n\t\t};\n\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tgotoEnd = clearQueue;\n\t\t\tclearQueue = type;\n\t\t\ttype = undefined;\n\t\t}\n\t\tif ( clearQueue ) {\n\t\t\tthis.queue( type || \"fx\", [] );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar dequeue = true,\n\t\t\t\tindex = type != null && type + \"queueHooks\",\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tdata = dataPriv.get( this );\n\n\t\t\tif ( index ) {\n\t\t\t\tif ( data[ index ] && data[ index ].stop ) {\n\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( index in data ) {\n\t\t\t\t\tif ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {\n\t\t\t\t\t\tstopQueue( data[ index ] );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this &&\n\t\t\t\t\t( type == null || timers[ index ].queue === type ) ) {\n\n\t\t\t\t\ttimers[ index ].anim.stop( gotoEnd );\n\t\t\t\t\tdequeue = false;\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start the next in the queue if the last step wasn't forced.\n\t\t\t// Timers currently will call their complete callbacks, which\n\t\t\t// will dequeue but only if they were gotoEnd.\n\t\t\tif ( dequeue || !gotoEnd ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t} );\n\t},\n\tfinish: function( type ) {\n\t\tif ( type !== false ) {\n\t\t\ttype = type || \"fx\";\n\t\t}\n\t\treturn this.each( function() {\n\t\t\tvar index,\n\t\t\t\tdata = dataPriv.get( this ),\n\t\t\t\tqueue = data[ type + \"queue\" ],\n\t\t\t\thooks = data[ type + \"queueHooks\" ],\n\t\t\t\ttimers = jQuery.timers,\n\t\t\t\tlength = queue ? queue.length : 0;\n\n\t\t\t// Enable finishing flag on private data\n\t\t\tdata.finish = true;\n\n\t\t\t// Empty the queue first\n\t\t\tjQuery.queue( this, type, [] );\n\n\t\t\tif ( hooks && hooks.stop ) {\n\t\t\t\thooks.stop.call( this, true );\n\t\t\t}\n\n\t\t\t// Look for any active animations, and finish them\n\t\t\tfor ( index = timers.length; index--; ) {\n\t\t\t\tif ( timers[ index ].elem === this && timers[ index ].queue === type ) {\n\t\t\t\t\ttimers[ index ].anim.stop( true );\n\t\t\t\t\ttimers.splice( index, 1 );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Look for any animations in the old queue and finish them\n\t\t\tfor ( index = 0; index < length; index++ ) {\n\t\t\t\tif ( queue[ index ] && queue[ index ].finish ) {\n\t\t\t\t\tqueue[ index ].finish.call( this );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Turn off finishing flag\n\t\t\tdelete data.finish;\n\t\t} );\n\t}\n} );\n\njQuery.each( [ \"toggle\", \"show\", \"hide\" ], function( _i, name ) {\n\tvar cssFn = jQuery.fn[ name ];\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn speed == null || typeof speed === \"boolean\" ?\n\t\t\tcssFn.apply( this, arguments ) :\n\t\t\tthis.animate( genFx( name, true ), speed, easing, callback );\n\t};\n} );\n\n// Generate shortcuts for custom animations\njQuery.each( {\n\tslideDown: genFx( \"show\" ),\n\tslideUp: genFx( \"hide\" ),\n\tslideToggle: genFx( \"toggle\" ),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n} );\n\njQuery.timers = [];\njQuery.fx.tick = function() {\n\tvar timer,\n\t\ti = 0,\n\t\ttimers = jQuery.timers;\n\n\tfxNow = Date.now();\n\n\tfor ( ; i < timers.length; i++ ) {\n\t\ttimer = timers[ i ];\n\n\t\t// Run the timer and safely remove it when done (allowing for external removal)\n\t\tif ( !timer() && timers[ i ] === timer ) {\n\t\t\ttimers.splice( i--, 1 );\n\t\t}\n\t}\n\n\tif ( !timers.length ) {\n\t\tjQuery.fx.stop();\n\t}\n\tfxNow = undefined;\n};\n\njQuery.fx.timer = function( timer ) {\n\tjQuery.timers.push( timer );\n\tjQuery.fx.start();\n};\n\njQuery.fx.interval = 13;\njQuery.fx.start = function() {\n\tif ( inProgress ) {\n\t\treturn;\n\t}\n\n\tinProgress = true;\n\tschedule();\n};\n\njQuery.fx.stop = function() {\n\tinProgress = null;\n};\n\njQuery.fx.speeds = {\n\tslow: 600,\n\tfast: 200,\n\n\t// Default speed\n\t_default: 400\n};\n\n\n// Based off of the plugin by Clint Helfers, with permission.\njQuery.fn.delay = function( time, type ) {\n\ttime = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;\n\ttype = type || \"fx\";\n\n\treturn this.queue( type, function( next, hooks ) {\n\t\tvar timeout = window.setTimeout( next, time );\n\t\thooks.stop = function() {\n\t\t\twindow.clearTimeout( timeout );\n\t\t};\n\t} );\n};\n\n\n( function() {\n\tvar input = document.createElement( \"input\" ),\n\t\tselect = document.createElement( \"select\" ),\n\t\topt = select.appendChild( document.createElement( \"option\" ) );\n\n\tinput.type = \"checkbox\";\n\n\t// Support: Android <=4.3 only\n\t// Default value for a checkbox should be \"on\"\n\tsupport.checkOn = input.value !== \"\";\n\n\t// Support: IE <=11 only\n\t// Must access selectedIndex to make default options select\n\tsupport.optSelected = opt.selected;\n\n\t// Support: IE <=11 only\n\t// An input loses its value after becoming a radio\n\tinput = document.createElement( \"input\" );\n\tinput.value = \"t\";\n\tinput.type = \"radio\";\n\tsupport.radioValue = input.value === \"t\";\n} )();\n\n\nvar boolHook,\n\tattrHandle = jQuery.expr.attrHandle;\n\njQuery.fn.extend( {\n\tattr: function( name, value ) {\n\t\treturn access( this, jQuery.attr, name, value, arguments.length > 1 );\n\t},\n\n\tremoveAttr: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.removeAttr( this, name );\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tattr: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set attributes on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Fallback to prop when attributes are not supported\n\t\tif ( typeof elem.getAttribute === \"undefined\" ) {\n\t\t\treturn jQuery.prop( elem, name, value );\n\t\t}\n\n\t\t// Attribute hooks are determined by the lowercase version\n\t\t// Grab necessary hook if one is defined\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\t\t\thooks = jQuery.attrHooks[ name.toLowerCase() ] ||\n\t\t\t\t( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( value === null ) {\n\t\t\t\tjQuery.removeAttr( elem, name );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\telem.setAttribute( name, value + \"\" );\n\t\t\treturn value;\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tret = jQuery.find.attr( elem, name );\n\n\t\t// Non-existent attributes return null, we normalize to undefined\n\t\treturn ret == null ? undefined : ret;\n\t},\n\n\tattrHooks: {\n\t\ttype: {\n\t\t\tset: function( elem, value ) {\n\t\t\t\tif ( !support.radioValue && value === \"radio\" &&\n\t\t\t\t\tnodeName( elem, \"input\" ) ) {\n\t\t\t\t\tvar val = elem.value;\n\t\t\t\t\telem.setAttribute( \"type\", value );\n\t\t\t\t\tif ( val ) {\n\t\t\t\t\t\telem.value = val;\n\t\t\t\t\t}\n\t\t\t\t\treturn value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\tremoveAttr: function( elem, value ) {\n\t\tvar name,\n\t\t\ti = 0,\n\n\t\t\t// Attribute names can contain non-HTML whitespace characters\n\t\t\t// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2\n\t\t\tattrNames = value && value.match( rnothtmlwhite );\n\n\t\tif ( attrNames && elem.nodeType === 1 ) {\n\t\t\twhile ( ( name = attrNames[ i++ ] ) ) {\n\t\t\t\telem.removeAttribute( name );\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Hooks for boolean attributes\nboolHook = {\n\tset: function( elem, value, name ) {\n\t\tif ( value === false ) {\n\n\t\t\t// Remove boolean attributes when set to false\n\t\t\tjQuery.removeAttr( elem, name );\n\t\t} else {\n\t\t\telem.setAttribute( name, name );\n\t\t}\n\t\treturn name;\n\t}\n};\n\njQuery.each( jQuery.expr.match.bool.source.match( /\\w+/g ), function( _i, name ) {\n\tvar getter = attrHandle[ name ] || jQuery.find.attr;\n\n\tattrHandle[ name ] = function( elem, name, isXML ) {\n\t\tvar ret, handle,\n\t\t\tlowercaseName = name.toLowerCase();\n\n\t\tif ( !isXML ) {\n\n\t\t\t// Avoid an infinite loop by temporarily removing this function from the getter\n\t\t\thandle = attrHandle[ lowercaseName ];\n\t\t\tattrHandle[ lowercaseName ] = ret;\n\t\t\tret = getter( elem, name, isXML ) != null ?\n\t\t\t\tlowercaseName :\n\t\t\t\tnull;\n\t\t\tattrHandle[ lowercaseName ] = handle;\n\t\t}\n\t\treturn ret;\n\t};\n} );\n\n\n\n\nvar rfocusable = /^(?:input|select|textarea|button)$/i,\n\trclickable = /^(?:a|area)$/i;\n\njQuery.fn.extend( {\n\tprop: function( name, value ) {\n\t\treturn access( this, jQuery.prop, name, value, arguments.length > 1 );\n\t},\n\n\tremoveProp: function( name ) {\n\t\treturn this.each( function() {\n\t\t\tdelete this[ jQuery.propFix[ name ] || name ];\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tprop: function( elem, name, value ) {\n\t\tvar ret, hooks,\n\t\t\tnType = elem.nodeType;\n\n\t\t// Don't get/set properties on text, comment and attribute nodes\n\t\tif ( nType === 3 || nType === 8 || nType === 2 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {\n\n\t\t\t// Fix name and attach hooks\n\t\t\tname = jQuery.propFix[ name ] || name;\n\t\t\thooks = jQuery.propHooks[ name ];\n\t\t}\n\n\t\tif ( value !== undefined ) {\n\t\t\tif ( hooks && \"set\" in hooks &&\n\t\t\t\t( ret = hooks.set( elem, value, name ) ) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\treturn ( elem[ name ] = value );\n\t\t}\n\n\t\tif ( hooks && \"get\" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\treturn elem[ name ];\n\t},\n\n\tpropHooks: {\n\t\ttabIndex: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\t// Support: IE <=9 - 11 only\n\t\t\t\t// elem.tabIndex doesn't always return the\n\t\t\t\t// correct value when it hasn't been explicitly set\n\t\t\t\t// Use proper attribute retrieval (trac-12072)\n\t\t\t\tvar tabindex = jQuery.find.attr( elem, \"tabindex\" );\n\n\t\t\t\tif ( tabindex ) {\n\t\t\t\t\treturn parseInt( tabindex, 10 );\n\t\t\t\t}\n\n\t\t\t\tif (\n\t\t\t\t\trfocusable.test( elem.nodeName ) ||\n\t\t\t\t\trclickable.test( elem.nodeName ) &&\n\t\t\t\t\telem.href\n\t\t\t\t) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t},\n\n\tpropFix: {\n\t\t\"for\": \"htmlFor\",\n\t\t\"class\": \"className\"\n\t}\n} );\n\n// Support: IE <=11 only\n// Accessing the selectedIndex property\n// forces the browser to respect setting selected\n// on the option\n// The getter ensures a default option is selected\n// when in an optgroup\n// eslint rule \"no-unused-expressions\" is disabled for this code\n// since it considers such accessions noop\nif ( !support.optSelected ) {\n\tjQuery.propHooks.selected = {\n\t\tget: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent && parent.parentNode ) {\n\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t}\n\t\t\treturn null;\n\t\t},\n\t\tset: function( elem ) {\n\n\t\t\t/* eslint no-unused-expressions: \"off\" */\n\n\t\t\tvar parent = elem.parentNode;\n\t\t\tif ( parent ) {\n\t\t\t\tparent.selectedIndex;\n\n\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\njQuery.each( [\n\t\"tabIndex\",\n\t\"readOnly\",\n\t\"maxLength\",\n\t\"cellSpacing\",\n\t\"cellPadding\",\n\t\"rowSpan\",\n\t\"colSpan\",\n\t\"useMap\",\n\t\"frameBorder\",\n\t\"contentEditable\"\n], function() {\n\tjQuery.propFix[ this.toLowerCase() ] = this;\n} );\n\n\n\n\n\t// Strip and collapse whitespace according to HTML spec\n\t// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace\n\tfunction stripAndCollapse( value ) {\n\t\tvar tokens = value.match( rnothtmlwhite ) || [];\n\t\treturn tokens.join( \" \" );\n\t}\n\n\nfunction getClass( elem ) {\n\treturn elem.getAttribute && elem.getAttribute( \"class\" ) || \"\";\n}\n\nfunction classesToArray( value ) {\n\tif ( Array.isArray( value ) ) {\n\t\treturn value;\n\t}\n\tif ( typeof value === \"string\" ) {\n\t\treturn value.match( rnothtmlwhite ) || [];\n\t}\n\treturn [];\n}\n\njQuery.fn.extend( {\n\taddClass: function( value ) {\n\t\tvar classNames, cur, curValue, className, i, finalValue;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).addClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tclassNames = classesToArray( value );\n\n\t\tif ( classNames.length ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tcurValue = getClass( this );\n\t\t\t\tcur = this.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tfor ( i = 0; i < classNames.length; i++ ) {\n\t\t\t\t\t\tclassName = classNames[ i ];\n\t\t\t\t\t\tif ( cur.indexOf( \" \" + className + \" \" ) < 0 ) {\n\t\t\t\t\t\t\tcur += className + \" \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\tthis.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tvar classNames, cur, curValue, className, i, finalValue;\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( j ) {\n\t\t\t\tjQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );\n\t\t\t} );\n\t\t}\n\n\t\tif ( !arguments.length ) {\n\t\t\treturn this.attr( \"class\", \"\" );\n\t\t}\n\n\t\tclassNames = classesToArray( value );\n\n\t\tif ( classNames.length ) {\n\t\t\treturn this.each( function() {\n\t\t\t\tcurValue = getClass( this );\n\n\t\t\t\t// This expression is here for better compressibility (see addClass)\n\t\t\t\tcur = this.nodeType === 1 && ( \" \" + stripAndCollapse( curValue ) + \" \" );\n\n\t\t\t\tif ( cur ) {\n\t\t\t\t\tfor ( i = 0; i < classNames.length; i++ ) {\n\t\t\t\t\t\tclassName = classNames[ i ];\n\n\t\t\t\t\t\t// Remove *all* instances\n\t\t\t\t\t\twhile ( cur.indexOf( \" \" + className + \" \" ) > -1 ) {\n\t\t\t\t\t\t\tcur = cur.replace( \" \" + className + \" \", \" \" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Only assign if different to avoid unneeded rendering.\n\t\t\t\t\tfinalValue = stripAndCollapse( cur );\n\t\t\t\t\tif ( curValue !== finalValue ) {\n\t\t\t\t\t\tthis.setAttribute( \"class\", finalValue );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar classNames, className, i, self,\n\t\t\ttype = typeof value,\n\t\t\tisValidValue = type === \"string\" || Array.isArray( value );\n\n\t\tif ( isFunction( value ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).toggleClass(\n\t\t\t\t\tvalue.call( this, i, getClass( this ), stateVal ),\n\t\t\t\t\tstateVal\n\t\t\t\t);\n\t\t\t} );\n\t\t}\n\n\t\tif ( typeof stateVal === \"boolean\" && isValidValue ) {\n\t\t\treturn stateVal ? this.addClass( value ) : this.removeClass( value );\n\t\t}\n\n\t\tclassNames = classesToArray( value );\n\n\t\treturn this.each( function() {\n\t\t\tif ( isValidValue ) {\n\n\t\t\t\t// Toggle individual class names\n\t\t\t\tself = jQuery( this );\n\n\t\t\t\tfor ( i = 0; i < classNames.length; i++ ) {\n\t\t\t\t\tclassName = classNames[ i ];\n\n\t\t\t\t\t// Check each className given, space separated list\n\t\t\t\t\tif ( self.hasClass( className ) ) {\n\t\t\t\t\t\tself.removeClass( className );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.addClass( className );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// Toggle whole class name\n\t\t\t} else if ( value === undefined || type === \"boolean\" ) {\n\t\t\t\tclassName = getClass( this );\n\t\t\t\tif ( className ) {\n\n\t\t\t\t\t// Store className if set\n\t\t\t\t\tdataPriv.set( this, \"__className__\", className );\n\t\t\t\t}\n\n\t\t\t\t// If the element has a class name or if we're passed `false`,\n\t\t\t\t// then remove the whole classname (if there was one, the above saved it).\n\t\t\t\t// Otherwise bring back whatever was previously saved (if anything),\n\t\t\t\t// falling back to the empty string if nothing was stored.\n\t\t\t\tif ( this.setAttribute ) {\n\t\t\t\t\tthis.setAttribute( \"class\",\n\t\t\t\t\t\tclassName || value === false ?\n\t\t\t\t\t\t\t\"\" :\n\t\t\t\t\t\t\tdataPriv.get( this, \"__className__\" ) || \"\"\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t} );\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className, elem,\n\t\t\ti = 0;\n\n\t\tclassName = \" \" + selector + \" \";\n\t\twhile ( ( elem = this[ i++ ] ) ) {\n\t\t\tif ( elem.nodeType === 1 &&\n\t\t\t\t( \" \" + stripAndCollapse( getClass( elem ) ) + \" \" ).indexOf( className ) > -1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n} );\n\n\n\n\nvar rreturn = /\\r/g;\n\njQuery.fn.extend( {\n\tval: function( value ) {\n\t\tvar hooks, ret, valueIsFunction,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !arguments.length ) {\n\t\t\tif ( elem ) {\n\t\t\t\thooks = jQuery.valHooks[ elem.type ] ||\n\t\t\t\t\tjQuery.valHooks[ elem.nodeName.toLowerCase() ];\n\n\t\t\t\tif ( hooks &&\n\t\t\t\t\t\"get\" in hooks &&\n\t\t\t\t\t( ret = hooks.get( elem, \"value\" ) ) !== undefined\n\t\t\t\t) {\n\t\t\t\t\treturn ret;\n\t\t\t\t}\n\n\t\t\t\tret = elem.value;\n\n\t\t\t\t// Handle most common string cases\n\t\t\t\tif ( typeof ret === \"string\" ) {\n\t\t\t\t\treturn ret.replace( rreturn, \"\" );\n\t\t\t\t}\n\n\t\t\t\t// Handle cases where value is null/undef or number\n\t\t\t\treturn ret == null ? \"\" : ret;\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tvalueIsFunction = isFunction( value );\n\n\t\treturn this.each( function( i ) {\n\t\t\tvar val;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( valueIsFunction ) {\n\t\t\t\tval = value.call( this, i, jQuery( this ).val() );\n\t\t\t} else {\n\t\t\t\tval = value;\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\n\t\t\t} else if ( Array.isArray( val ) ) {\n\t\t\t\tval = jQuery.map( val, function( value ) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\thooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];\n\n\t\t\t// If set returns undefined, fall back to normal setting\n\t\t\tif ( !hooks || !( \"set\" in hooks ) || hooks.set( this, val, \"value\" ) === undefined ) {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t} );\n\t}\n} );\n\njQuery.extend( {\n\tvalHooks: {\n\t\toption: {\n\t\t\tget: function( elem ) {\n\n\t\t\t\tvar val = jQuery.find.attr( elem, \"value\" );\n\t\t\t\treturn val != null ?\n\t\t\t\t\tval :\n\n\t\t\t\t\t// Support: IE <=10 - 11 only\n\t\t\t\t\t// option.text throws exceptions (trac-14686, trac-14858)\n\t\t\t\t\t// Strip and collapse whitespace\n\t\t\t\t\t// https://html.spec.whatwg.org/#strip-and-collapse-whitespace\n\t\t\t\t\tstripAndCollapse( jQuery.text( elem ) );\n\t\t\t}\n\t\t},\n\t\tselect: {\n\t\t\tget: function( elem ) {\n\t\t\t\tvar value, option, i,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tindex = elem.selectedIndex,\n\t\t\t\t\tone = elem.type === \"select-one\",\n\t\t\t\t\tvalues = one ? null : [],\n\t\t\t\t\tmax = one ? index + 1 : options.length;\n\n\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\ti = max;\n\n\t\t\t\t} else {\n\t\t\t\t\ti = one ? index : 0;\n\t\t\t\t}\n\n\t\t\t\t// Loop through all the selected options\n\t\t\t\tfor ( ; i < max; i++ ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t// IE8-9 doesn't update selected after form reset (trac-2551)\n\t\t\t\t\tif ( ( option.selected || i === index ) &&\n\n\t\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\t\t!option.disabled &&\n\t\t\t\t\t\t\t( !option.parentNode.disabled ||\n\t\t\t\t\t\t\t\t!nodeName( option.parentNode, \"optgroup\" ) ) ) {\n\n\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\tvalue = jQuery( option ).val();\n\n\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn values;\n\t\t\t},\n\n\t\t\tset: function( elem, value ) {\n\t\t\t\tvar optionSet, option,\n\t\t\t\t\toptions = elem.options,\n\t\t\t\t\tvalues = jQuery.makeArray( value ),\n\t\t\t\t\ti = options.length;\n\n\t\t\t\twhile ( i-- ) {\n\t\t\t\t\toption = options[ i ];\n\n\t\t\t\t\t/* eslint-disable no-cond-assign */\n\n\t\t\t\t\tif ( option.selected =\n\t\t\t\t\t\tjQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1\n\t\t\t\t\t) {\n\t\t\t\t\t\toptionSet = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t/* eslint-enable no-cond-assign */\n\t\t\t\t}\n\n\t\t\t\t// Force browsers to behave consistently when non-matching value is set\n\t\t\t\tif ( !optionSet ) {\n\t\t\t\t\telem.selectedIndex = -1;\n\t\t\t\t}\n\t\t\t\treturn values;\n\t\t\t}\n\t\t}\n\t}\n} );\n\n// Radios and checkboxes getter/setter\njQuery.each( [ \"radio\", \"checkbox\" ], function() {\n\tjQuery.valHooks[ this ] = {\n\t\tset: function( elem, value ) {\n\t\t\tif ( Array.isArray( value ) ) {\n\t\t\t\treturn ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );\n\t\t\t}\n\t\t}\n\t};\n\tif ( !support.checkOn ) {\n\t\tjQuery.valHooks[ this ].get = function( elem ) {\n\t\t\treturn elem.getAttribute( \"value\" ) === null ? \"on\" : elem.value;\n\t\t};\n\t}\n} );\n\n\n\n\n// Return jQuery for attributes-only inclusion\n\n\nsupport.focusin = \"onfocusin\" in window;\n\n\nvar rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,\n\tstopPropagationCallback = function( e ) {\n\t\te.stopPropagation();\n\t};\n\njQuery.extend( jQuery.event, {\n\n\ttrigger: function( event, data, elem, onlyHandlers ) {\n\n\t\tvar i, cur, tmp, bubbleType, ontype, handle, special, lastElement,\n\t\t\teventPath = [ elem || document ],\n\t\t\ttype = hasOwn.call( event, \"type\" ) ? event.type : event,\n\t\t\tnamespaces = hasOwn.call( event, \"namespace\" ) ? event.namespace.split( \".\" ) : [];\n\n\t\tcur = lastElement = tmp = elem = elem || document;\n\n\t\t// Don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// focus/blur morphs to focusin/out; ensure we're not firing them right now\n\t\tif ( rfocusMorph.test( type + jQuery.event.triggered ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( type.indexOf( \".\" ) > -1 ) {\n\n\t\t\t// Namespaced trigger; create a regexp to match event type in handle()\n\t\t\tnamespaces = type.split( \".\" );\n\t\t\ttype = namespaces.shift();\n\t\t\tnamespaces.sort();\n\t\t}\n\t\tontype = type.indexOf( \":\" ) < 0 && \"on\" + type;\n\n\t\t// Caller can pass in a jQuery.Event object, Object, or just an event type string\n\t\tevent = event[ jQuery.expando ] ?\n\t\t\tevent :\n\t\t\tnew jQuery.Event( type, typeof event === \"object\" && event );\n\n\t\t// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)\n\t\tevent.isTrigger = onlyHandlers ? 2 : 3;\n\t\tevent.namespace = namespaces.join( \".\" );\n\t\tevent.rnamespace = event.namespace ?\n\t\t\tnew RegExp( \"(^|\\\\.)\" + namespaces.join( \"\\\\.(?:.*\\\\.|)\" ) + \"(\\\\.|$)\" ) :\n\t\t\tnull;\n\n\t\t// Clean up the event in case it is being reused\n\t\tevent.result = undefined;\n\t\tif ( !event.target ) {\n\t\t\tevent.target = elem;\n\t\t}\n\n\t\t// Clone any incoming data and prepend the event, creating the handler arg list\n\t\tdata = data == null ?\n\t\t\t[ event ] :\n\t\t\tjQuery.makeArray( data, [ event ] );\n\n\t\t// Allow special events to draw outside the lines\n\t\tspecial = jQuery.event.special[ type ] || {};\n\t\tif ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Determine event propagation path in advance, per W3C events spec (trac-9951)\n\t\t// Bubble up to document, then to window; watch for a global ownerDocument var (trac-9724)\n\t\tif ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {\n\n\t\t\tbubbleType = special.delegateType || type;\n\t\t\tif ( !rfocusMorph.test( bubbleType + type ) ) {\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\tfor ( ; cur; cur = cur.parentNode ) {\n\t\t\t\teventPath.push( cur );\n\t\t\t\ttmp = cur;\n\t\t\t}\n\n\t\t\t// Only add window if we got to document (e.g., not plain obj or detached DOM)\n\t\t\tif ( tmp === ( elem.ownerDocument || document ) ) {\n\t\t\t\teventPath.push( tmp.defaultView || tmp.parentWindow || window );\n\t\t\t}\n\t\t}\n\n\t\t// Fire handlers on the event path\n\t\ti = 0;\n\t\twhile ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {\n\t\t\tlastElement = cur;\n\t\t\tevent.type = i > 1 ?\n\t\t\t\tbubbleType :\n\t\t\t\tspecial.bindType || type;\n\n\t\t\t// jQuery handler\n\t\t\thandle = ( dataPriv.get( cur, \"events\" ) || Object.create( null ) )[ event.type ] &&\n\t\t\t\tdataPriv.get( cur, \"handle\" );\n\t\t\tif ( handle ) {\n\t\t\t\thandle.apply( cur, data );\n\t\t\t}\n\n\t\t\t// Native handler\n\t\t\thandle = ontype && cur[ ontype ];\n\t\t\tif ( handle && handle.apply && acceptData( cur ) ) {\n\t\t\t\tevent.result = handle.apply( cur, data );\n\t\t\t\tif ( event.result === false ) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tevent.type = type;\n\n\t\t// If nobody prevented the default action, do it now\n\t\tif ( !onlyHandlers && !event.isDefaultPrevented() ) {\n\n\t\t\tif ( ( !special._default ||\n\t\t\t\tspecial._default.apply( eventPath.pop(), data ) === false ) &&\n\t\t\t\tacceptData( elem ) ) {\n\n\t\t\t\t// Call a native DOM method on the target with the same name as the event.\n\t\t\t\t// Don't do default actions on window, that's where global variables be (trac-6170)\n\t\t\t\tif ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {\n\n\t\t\t\t\t// Don't re-trigger an onFOO event when we call its FOO() method\n\t\t\t\t\ttmp = elem[ ontype ];\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Prevent re-triggering of the same event, since we already bubbled it above\n\t\t\t\t\tjQuery.event.triggered = type;\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.addEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\telem[ type ]();\n\n\t\t\t\t\tif ( event.isPropagationStopped() ) {\n\t\t\t\t\t\tlastElement.removeEventListener( type, stopPropagationCallback );\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery.event.triggered = undefined;\n\n\t\t\t\t\tif ( tmp ) {\n\t\t\t\t\t\telem[ ontype ] = tmp;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\t// Piggyback on a donor event to simulate a different one\n\t// Used only for `focus(in | out)` events\n\tsimulate: function( type, elem, event ) {\n\t\tvar e = jQuery.extend(\n\t\t\tnew jQuery.Event(),\n\t\t\tevent,\n\t\t\t{\n\t\t\t\ttype: type,\n\t\t\t\tisSimulated: true\n\t\t\t}\n\t\t);\n\n\t\tjQuery.event.trigger( e, null, elem );\n\t}\n\n} );\n\njQuery.fn.extend( {\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each( function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t} );\n\t},\n\ttriggerHandler: function( type, data ) {\n\t\tvar elem = this[ 0 ];\n\t\tif ( elem ) {\n\t\t\treturn jQuery.event.trigger( type, data, elem, true );\n\t\t}\n\t}\n} );\n\n\n// Support: Firefox <=44\n// Firefox doesn't have focus(in | out) events\n// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787\n//\n// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1\n// focus(in | out) events fire after focus & blur events,\n// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order\n// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857\nif ( !support.focusin ) {\n\tjQuery.each( { focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\n\t\t// Attach a single capturing handler on the document while someone wants focusin/focusout\n\t\tvar handler = function( event ) {\n\t\t\tjQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );\n\t\t};\n\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\n\t\t\t\t// Handle: regular nodes (via `this.ownerDocument`), window\n\t\t\t\t// (via `this.document`) & document (via `this`).\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix );\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.addEventListener( orig, handler, true );\n\t\t\t\t}\n\t\t\t\tdataPriv.access( doc, fix, ( attaches || 0 ) + 1 );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tvar doc = this.ownerDocument || this.document || this,\n\t\t\t\t\tattaches = dataPriv.access( doc, fix ) - 1;\n\n\t\t\t\tif ( !attaches ) {\n\t\t\t\t\tdoc.removeEventListener( orig, handler, true );\n\t\t\t\t\tdataPriv.remove( doc, fix );\n\n\t\t\t\t} else {\n\t\t\t\t\tdataPriv.access( doc, fix, attaches );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t} );\n}\nvar location = window.location;\n\nvar nonce = { guid: Date.now() };\n\nvar rquery = ( /\\?/ );\n\n\n\n// Cross-browser xml parsing\njQuery.parseXML = function( data ) {\n\tvar xml, parserErrorElem;\n\tif ( !data || typeof data !== \"string\" ) {\n\t\treturn null;\n\t}\n\n\t// Support: IE 9 - 11 only\n\t// IE throws on parseFromString with invalid input.\n\ttry {\n\t\txml = ( new window.DOMParser() ).parseFromString( data, \"text/xml\" );\n\t} catch ( e ) {}\n\n\tparserErrorElem = xml && xml.getElementsByTagName( \"parsererror\" )[ 0 ];\n\tif ( !xml || parserErrorElem ) {\n\t\tjQuery.error( \"Invalid XML: \" + (\n\t\t\tparserErrorElem ?\n\t\t\t\tjQuery.map( parserErrorElem.childNodes, function( el ) {\n\t\t\t\t\treturn el.textContent;\n\t\t\t\t} ).join( \"\\n\" ) :\n\t\t\t\tdata\n\t\t) );\n\t}\n\treturn xml;\n};\n\n\nvar\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,\n\trsubmittable = /^(?:input|select|textarea|keygen)/i;\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tvar name;\n\n\tif ( Array.isArray( obj ) ) {\n\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\n\t\t\t\t// Item is non-scalar (array or object), encode its numeric index.\n\t\t\t\tbuildParams(\n\t\t\t\t\tprefix + \"[\" + ( typeof v === \"object\" && v != null ? i : \"\" ) + \"]\",\n\t\t\t\t\tv,\n\t\t\t\t\ttraditional,\n\t\t\t\t\tadd\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\n\t} else if ( !traditional && toType( obj ) === \"object\" ) {\n\n\t\t// Serialize object item.\n\t\tfor ( name in obj ) {\n\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t}\n\n\t} else {\n\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// Serialize an array of form elements or a set of\n// key/values into a query string\njQuery.param = function( a, traditional ) {\n\tvar prefix,\n\t\ts = [],\n\t\tadd = function( key, valueOrFunction ) {\n\n\t\t\t// If value is a function, invoke it and use its return value\n\t\t\tvar value = isFunction( valueOrFunction ) ?\n\t\t\t\tvalueOrFunction() :\n\t\t\t\tvalueOrFunction;\n\n\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" +\n\t\t\t\tencodeURIComponent( value == null ? \"\" : value );\n\t\t};\n\n\tif ( a == null ) {\n\t\treturn \"\";\n\t}\n\n\t// If an array was passed in, assume that it is an array of form elements.\n\tif ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\n\t\t// Serialize the form elements\n\t\tjQuery.each( a, function() {\n\t\t\tadd( this.name, this.value );\n\t\t} );\n\n\t} else {\n\n\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t// did it), otherwise encode params recursively.\n\t\tfor ( prefix in a ) {\n\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t}\n\t}\n\n\t// Return the resulting serialization\n\treturn s.join( \"&\" );\n};\n\njQuery.fn.extend( {\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\tserializeArray: function() {\n\t\treturn this.map( function() {\n\n\t\t\t// Can add propHook for \"elements\" to filter or add form elements\n\t\t\tvar elements = jQuery.prop( this, \"elements\" );\n\t\t\treturn elements ? jQuery.makeArray( elements ) : this;\n\t\t} ).filter( function() {\n\t\t\tvar type = this.type;\n\n\t\t\t// Use .is( \":disabled\" ) so that fieldset[disabled] works\n\t\t\treturn this.name && !jQuery( this ).is( \":disabled\" ) &&\n\t\t\t\trsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&\n\t\t\t\t( this.checked || !rcheckableType.test( type ) );\n\t\t} ).map( function( _i, elem ) {\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\tif ( val == null ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif ( Array.isArray( val ) ) {\n\t\t\t\treturn jQuery.map( val, function( val ) {\n\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t} ).get();\n\t}\n} );\n\n\nvar\n\tr20 = /%20/g,\n\trhash = /#.*$/,\n\trantiCache = /([?&])_=[^&]*/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)$/mg,\n\n\t// trac-7653, trac-8125, trac-8152: local protocol detection\n\trlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t *    - BEFORE asking for a transport\n\t *    - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Avoid comment-prolog char sequence (trac-10098); must appease lint and evade compression\n\tallTypes = \"*/\".concat( \"*\" ),\n\n\t// Anchor tag for parsing the document origin\n\toriginAnchor = document.createElement( \"a\" );\n\noriginAnchor.href = location.href;\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tvar dataType,\n\t\t\ti = 0,\n\t\t\tdataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];\n\n\t\tif ( isFunction( func ) ) {\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\twhile ( ( dataType = dataTypes[ i++ ] ) ) {\n\n\t\t\t\t// Prepend if requested\n\t\t\t\tif ( dataType[ 0 ] === \"+\" ) {\n\t\t\t\t\tdataType = dataType.slice( 1 ) || \"*\";\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );\n\n\t\t\t\t// Otherwise append\n\t\t\t\t} else {\n\t\t\t\t\t( structure[ dataType ] = structure[ dataType ] || [] ).push( func );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n}\n\n// Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {\n\n\tvar inspected = {},\n\t\tseekingTransport = ( structure === transports );\n\n\tfunction inspect( dataType ) {\n\t\tvar selected;\n\t\tinspected[ dataType ] = true;\n\t\tjQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {\n\t\t\tvar dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );\n\t\t\tif ( typeof dataTypeOrTransport === \"string\" &&\n\t\t\t\t!seekingTransport && !inspected[ dataTypeOrTransport ] ) {\n\n\t\t\t\toptions.dataTypes.unshift( dataTypeOrTransport );\n\t\t\t\tinspect( dataTypeOrTransport );\n\t\t\t\treturn false;\n\t\t\t} else if ( seekingTransport ) {\n\t\t\t\treturn !( selected = dataTypeOrTransport );\n\t\t\t}\n\t\t} );\n\t\treturn selected;\n\t}\n\n\treturn inspect( options.dataTypes[ 0 ] ) || !inspected[ \"*\" ] && inspect( \"*\" );\n}\n\n// A special extend for ajax options\n// that takes \"flat\" options (not to be deep extended)\n// Fixes trac-9887\nfunction ajaxExtend( target, src ) {\n\tvar key, deep,\n\t\tflatOptions = jQuery.ajaxSettings.flatOptions || {};\n\n\tfor ( key in src ) {\n\t\tif ( src[ key ] !== undefined ) {\n\t\t\t( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];\n\t\t}\n\t}\n\tif ( deep ) {\n\t\tjQuery.extend( true, target, deep );\n\t}\n\n\treturn target;\n}\n\n/* Handles responses to an ajax request:\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar ct, type, finalDataType, firstDataType,\n\t\tcontents = s.contents,\n\t\tdataTypes = s.dataTypes;\n\n\t// Remove auto dataType and get content-type in the process\n\twhile ( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"Content-Type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[ 0 ] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n/* Chain conversions given the request and the original response\n * Also sets the responseXXX fields on the jqXHR instance\n */\nfunction ajaxConvert( s, response, jqXHR, isSuccess ) {\n\tvar conv2, current, conv, tmp, prev,\n\t\tconverters = {},\n\n\t\t// Work with a copy of dataTypes in case we need to modify it for conversion\n\t\tdataTypes = s.dataTypes.slice();\n\n\t// Create converters map with lowercased keys\n\tif ( dataTypes[ 1 ] ) {\n\t\tfor ( conv in s.converters ) {\n\t\t\tconverters[ conv.toLowerCase() ] = s.converters[ conv ];\n\t\t}\n\t}\n\n\tcurrent = dataTypes.shift();\n\n\t// Convert to each sequential dataType\n\twhile ( current ) {\n\n\t\tif ( s.responseFields[ current ] ) {\n\t\t\tjqXHR[ s.responseFields[ current ] ] = response;\n\t\t}\n\n\t\t// Apply the dataFilter if provided\n\t\tif ( !prev && isSuccess && s.dataFilter ) {\n\t\t\tresponse = s.dataFilter( response, s.dataType );\n\t\t}\n\n\t\tprev = current;\n\t\tcurrent = dataTypes.shift();\n\n\t\tif ( current ) {\n\n\t\t\t// There's only work to do if current dataType is non-auto\n\t\t\tif ( current === \"*\" ) {\n\n\t\t\t\tcurrent = prev;\n\n\t\t\t// Convert response if prev dataType is non-auto and differs from current\n\t\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t\t// Seek a direct converter\n\t\t\t\tconv = converters[ prev + \" \" + current ] || converters[ \"* \" + current ];\n\n\t\t\t\t// If none found, seek a pair\n\t\t\t\tif ( !conv ) {\n\t\t\t\t\tfor ( conv2 in converters ) {\n\n\t\t\t\t\t\t// If conv2 outputs current\n\t\t\t\t\t\ttmp = conv2.split( \" \" );\n\t\t\t\t\t\tif ( tmp[ 1 ] === current ) {\n\n\t\t\t\t\t\t\t// If prev can be converted to accepted input\n\t\t\t\t\t\t\tconv = converters[ prev + \" \" + tmp[ 0 ] ] ||\n\t\t\t\t\t\t\t\tconverters[ \"* \" + tmp[ 0 ] ];\n\t\t\t\t\t\t\tif ( conv ) {\n\n\t\t\t\t\t\t\t\t// Condense equivalence converters\n\t\t\t\t\t\t\t\tif ( conv === true ) {\n\t\t\t\t\t\t\t\t\tconv = converters[ conv2 ];\n\n\t\t\t\t\t\t\t\t// Otherwise, insert the intermediate dataType\n\t\t\t\t\t\t\t\t} else if ( converters[ conv2 ] !== true ) {\n\t\t\t\t\t\t\t\t\tcurrent = tmp[ 0 ];\n\t\t\t\t\t\t\t\t\tdataTypes.unshift( tmp[ 1 ] );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply converter (if not an equivalence)\n\t\t\t\tif ( conv !== true ) {\n\n\t\t\t\t\t// Unless errors are allowed to bubble, catch and return them\n\t\t\t\t\tif ( conv && s.throws ) {\n\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse = conv( response );\n\t\t\t\t\t\t} catch ( e ) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tstate: \"parsererror\",\n\t\t\t\t\t\t\t\terror: conv ? e : \"No conversion from \" + prev + \" to \" + current\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { state: \"success\", data: response };\n}\n\njQuery.extend( {\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\ttype: \"GET\",\n\t\tisLocal: rlocalProtocol.test( location.protocol ),\n\t\tglobal: true,\n\t\tprocessData: true,\n\t\tasync: true,\n\t\tcontentType: \"application/x-www-form-urlencoded; charset=UTF-8\",\n\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\tthrows: false,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\t*/\n\n\t\taccepts: {\n\t\t\t\"*\": allTypes,\n\t\t\ttext: \"text/plain\",\n\t\t\thtml: \"text/html\",\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\tjson: \"application/json, text/javascript\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /\\bxml\\b/,\n\t\t\thtml: /\\bhtml/,\n\t\t\tjson: /\\bjson\\b/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\",\n\t\t\tjson: \"responseJSON\"\n\t\t},\n\n\t\t// Data converters\n\t\t// Keys separate source (or catchall \"*\") and destination types with a single space\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": JSON.parse,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t},\n\n\t\t// For options that shouldn't be deep extended:\n\t\t// you can add your own custom options here if\n\t\t// and when you create one that shouldn't be\n\t\t// deep extended (see ajaxExtend)\n\t\tflatOptions: {\n\t\t\turl: true,\n\t\t\tcontext: true\n\t\t}\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function( target, settings ) {\n\t\treturn settings ?\n\n\t\t\t// Building a settings object\n\t\t\tajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :\n\n\t\t\t// Extending ajaxSettings\n\t\t\tajaxExtend( jQuery.ajaxSettings, target );\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar transport,\n\n\t\t\t// URL without anti-cache param\n\t\t\tcacheURL,\n\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\n\t\t\t// Url cleanup var\n\t\t\turlAnchor,\n\n\t\t\t// Request state (becomes false upon send and true upon completion)\n\t\t\tcompleted,\n\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\n\t\t\t// Loop variable\n\t\t\ti,\n\n\t\t\t// uncached part of the url\n\t\t\tuncached,\n\n\t\t\t// Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\n\t\t\t// Context for global events is callbackContext if it is a DOM node or jQuery collection\n\t\t\tglobalEventContext = s.context &&\n\t\t\t\t( callbackContext.nodeType || callbackContext.jquery ) ?\n\t\t\t\tjQuery( callbackContext ) :\n\t\t\t\tjQuery.event,\n\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery.Callbacks( \"once memory\" ),\n\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\trequestHeadersNames = {},\n\n\t\t\t// Default abort message\n\t\t\tstrAbort = \"canceled\",\n\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( completed ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile ( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[ 1 ].toLowerCase() + \" \" ] =\n\t\t\t\t\t\t\t\t\t( responseHeaders[ match[ 1 ].toLowerCase() + \" \" ] || [] )\n\t\t\t\t\t\t\t\t\t\t.concat( match[ 2 ] );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() + \" \" ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match == null ? null : match.join( \", \" );\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn completed ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\tname = requestHeadersNames[ name.toLowerCase() ] =\n\t\t\t\t\t\t\trequestHeadersNames[ name.toLowerCase() ] || name;\n\t\t\t\t\t\trequestHeaders[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( completed == null ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Status-dependent callbacks\n\t\t\t\tstatusCode: function( map ) {\n\t\t\t\t\tvar code;\n\t\t\t\t\tif ( map ) {\n\t\t\t\t\t\tif ( completed ) {\n\n\t\t\t\t\t\t\t// Execute the appropriate callbacks\n\t\t\t\t\t\t\tjqXHR.always( map[ jqXHR.status ] );\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Lazy-add the new callbacks in a way that preserves old ones\n\t\t\t\t\t\t\tfor ( code in map ) {\n\t\t\t\t\t\t\t\tstatusCode[ code ] = [ statusCode[ code ], map[ code ] ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tvar finalText = statusText || strAbort;\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( finalText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, finalText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\n\t\t// Add protocol if not provided (prefilters might expect it)\n\t\t// Handle falsy url in the settings object (trac-10093: consistency with old signature)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url || location.href ) + \"\" )\n\t\t\t.replace( rprotocol, location.protocol + \"//\" );\n\n\t\t// Alias method option to type as per ticket trac-12004\n\t\ts.type = options.method || options.type || s.method || s.type;\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = ( s.dataType || \"*\" ).toLowerCase().match( rnothtmlwhite ) || [ \"\" ];\n\n\t\t// A cross-domain request is in order when the origin doesn't match the current origin.\n\t\tif ( s.crossDomain == null ) {\n\t\t\turlAnchor = document.createElement( \"a\" );\n\n\t\t\t// Support: IE <=8 - 11, Edge 12 - 15\n\t\t\t// IE throws exception on accessing the href property if url is malformed,\n\t\t\t// e.g. http://example.com:80x/\n\t\t\ttry {\n\t\t\t\turlAnchor.href = s.url;\n\n\t\t\t\t// Support: IE <=8 - 11 only\n\t\t\t\t// Anchor's host property isn't correctly set when s.url is relative\n\t\t\t\turlAnchor.href = urlAnchor.href;\n\t\t\t\ts.crossDomain = originAnchor.protocol + \"//\" + originAnchor.host !==\n\t\t\t\t\turlAnchor.protocol + \"//\" + urlAnchor.host;\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// If there is an error parsing the URL, assume it is crossDomain,\n\t\t\t\t// it can be rejected by the transport if it is invalid\n\t\t\t\ts.crossDomain = true;\n\t\t\t}\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefilter, stop there\n\t\tif ( completed ) {\n\t\t\treturn jqXHR;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\t// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (trac-15118)\n\t\tfireGlobals = jQuery.event && s.global;\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Save the URL in case we're toying with the If-Modified-Since\n\t\t// and/or If-None-Match header later on\n\t\t// Remove hash to simplify url manipulation\n\t\tcacheURL = s.url.replace( rhash, \"\" );\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// Remember the hash so we can put it back\n\t\t\tuncached = s.url.slice( cacheURL.length );\n\n\t\t\t// If data is available and should be processed, append data to url\n\t\t\tif ( s.data && ( s.processData || typeof s.data === \"string\" ) ) {\n\t\t\t\tcacheURL += ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + s.data;\n\n\t\t\t\t// trac-9682: remove data so that it's not used in an eventual retry\n\t\t\t\tdelete s.data;\n\t\t\t}\n\n\t\t\t// Add or update anti-cache param if needed\n\t\t\tif ( s.cache === false ) {\n\t\t\t\tcacheURL = cacheURL.replace( rantiCache, \"$1\" );\n\t\t\t\tuncached = ( rquery.test( cacheURL ) ? \"&\" : \"?\" ) + \"_=\" + ( nonce.guid++ ) +\n\t\t\t\t\tuncached;\n\t\t\t}\n\n\t\t\t// Put hash and anti-cache on the URL that will be requested (gh-1732)\n\t\t\ts.url = cacheURL + uncached;\n\n\t\t// Change '%20' to '+' if this is encoded form body content (gh-2658)\n\t\t} else if ( s.data && s.processData &&\n\t\t\t( s.contentType || \"\" ).indexOf( \"application/x-www-form-urlencoded\" ) === 0 ) {\n\t\t\ts.data = s.data.replace( r20, \"+\" );\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tif ( jQuery.lastModified[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-Modified-Since\", jQuery.lastModified[ cacheURL ] );\n\t\t\t}\n\t\t\tif ( jQuery.etag[ cacheURL ] ) {\n\t\t\t\tjqXHR.setRequestHeader( \"If-None-Match\", jQuery.etag[ cacheURL ] );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\tjqXHR.setRequestHeader( \"Content-Type\", s.contentType );\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\tjqXHR.setRequestHeader(\n\t\t\t\"Accept\",\n\t\t\ts.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?\n\t\t\t\ts.accepts[ s.dataTypes[ 0 ] ] +\n\t\t\t\t\t( s.dataTypes[ 0 ] !== \"*\" ? \", \" + allTypes + \"; q=0.01\" : \"\" ) :\n\t\t\t\ts.accepts[ \"*\" ]\n\t\t);\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend &&\n\t\t\t( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {\n\n\t\t\t// Abort if not done already and return\n\t\t\treturn jqXHR.abort();\n\t\t}\n\n\t\t// Aborting is no longer a cancellation\n\t\tstrAbort = \"abort\";\n\n\t\t// Install callbacks on deferreds\n\t\tcompleteDeferred.add( s.complete );\n\t\tjqXHR.done( s.success );\n\t\tjqXHR.fail( s.error );\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\n\t\t\t// If request was aborted inside ajaxSend, stop there\n\t\t\tif ( completed ) {\n\t\t\t\treturn jqXHR;\n\t\t\t}\n\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = window.setTimeout( function() {\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tcompleted = false;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch ( e ) {\n\n\t\t\t\t// Rethrow post-completion exceptions\n\t\t\t\tif ( completed ) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\n\t\t\t\t// Propagate others as results\n\t\t\t\tdone( -1, e );\n\t\t\t}\n\t\t}\n\n\t\t// Callback for when everything is done\n\t\tfunction done( status, nativeStatusText, responses, headers ) {\n\t\t\tvar isSuccess, success, error, response, modified,\n\t\t\t\tstatusText = nativeStatusText;\n\n\t\t\t// Ignore repeat invocations\n\t\t\tif ( completed ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcompleted = true;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\twindow.clearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status > 0 ? 4 : 0;\n\n\t\t\t// Determine if successful\n\t\t\tisSuccess = status >= 200 && status < 300 || status === 304;\n\n\t\t\t// Get response data\n\t\t\tif ( responses ) {\n\t\t\t\tresponse = ajaxHandleResponses( s, jqXHR, responses );\n\t\t\t}\n\n\t\t\t// Use a noop converter for missing script but not if jsonp\n\t\t\tif ( !isSuccess &&\n\t\t\t\tjQuery.inArray( \"script\", s.dataTypes ) > -1 &&\n\t\t\t\tjQuery.inArray( \"json\", s.dataTypes ) < 0 ) {\n\t\t\t\ts.converters[ \"text script\" ] = function() {};\n\t\t\t}\n\n\t\t\t// Convert no matter what (that way responseXXX fields are always set)\n\t\t\tresponse = ajaxConvert( s, response, jqXHR, isSuccess );\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( isSuccess ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"Last-Modified\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.lastModified[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t\tmodified = jqXHR.getResponseHeader( \"etag\" );\n\t\t\t\t\tif ( modified ) {\n\t\t\t\t\t\tjQuery.etag[ cacheURL ] = modified;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// if no content\n\t\t\t\tif ( status === 204 || s.type === \"HEAD\" ) {\n\t\t\t\t\tstatusText = \"nocontent\";\n\n\t\t\t\t// if not modified\n\t\t\t\t} else if ( status === 304 ) {\n\t\t\t\t\tstatusText = \"notmodified\";\n\n\t\t\t\t// If we have data, let's convert it\n\t\t\t\t} else {\n\t\t\t\t\tstatusText = response.state;\n\t\t\t\t\tsuccess = response.data;\n\t\t\t\t\terror = response.error;\n\t\t\t\t\tisSuccess = !error;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Extract error from statusText and normalize for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif ( status || !statusText ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = ( nativeStatusText || statusText ) + \"\";\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( isSuccess ? \"ajaxSuccess\" : \"ajaxError\",\n\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s ] );\n\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t}\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( _i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\n\t\t// Shift arguments if data argument was omitted\n\t\tif ( isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\t// The url can be an options object (which then must have .url)\n\t\treturn jQuery.ajax( jQuery.extend( {\n\t\t\turl: url,\n\t\t\ttype: method,\n\t\t\tdataType: type,\n\t\t\tdata: data,\n\t\t\tsuccess: callback\n\t\t}, jQuery.isPlainObject( url ) && url ) );\n\t};\n} );\n\njQuery.ajaxPrefilter( function( s ) {\n\tvar i;\n\tfor ( i in s.headers ) {\n\t\tif ( i.toLowerCase() === \"content-type\" ) {\n\t\t\ts.contentType = s.headers[ i ] || \"\";\n\t\t}\n\t}\n} );\n\n\njQuery._evalUrl = function( url, options, doc ) {\n\treturn jQuery.ajax( {\n\t\turl: url,\n\n\t\t// Make this explicit, since user can override this through ajaxSetup (trac-11264)\n\t\ttype: \"GET\",\n\t\tdataType: \"script\",\n\t\tcache: true,\n\t\tasync: false,\n\t\tglobal: false,\n\n\t\t// Only evaluate the response if it is successful (gh-4126)\n\t\t// dataFilter is not invoked for failure responses, so using it instead\n\t\t// of the default converter is kludgy but it works.\n\t\tconverters: {\n\t\t\t\"text script\": function() {}\n\t\t},\n\t\tdataFilter: function( response ) {\n\t\t\tjQuery.globalEval( response, options, doc );\n\t\t}\n\t} );\n};\n\n\njQuery.fn.extend( {\n\twrapAll: function( html ) {\n\t\tvar wrap;\n\n\t\tif ( this[ 0 ] ) {\n\t\t\tif ( isFunction( html ) ) {\n\t\t\t\thtml = html.call( this[ 0 ] );\n\t\t\t}\n\n\t\t\t// The elements to wrap the target around\n\t\t\twrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );\n\n\t\t\tif ( this[ 0 ].parentNode ) {\n\t\t\t\twrap.insertBefore( this[ 0 ] );\n\t\t\t}\n\n\t\t\twrap.map( function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstElementChild ) {\n\t\t\t\t\telem = elem.firstElementChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t} ).append( this );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( isFunction( html ) ) {\n\t\t\treturn this.each( function( i ) {\n\t\t\t\tjQuery( this ).wrapInner( html.call( this, i ) );\n\t\t\t} );\n\t\t}\n\n\t\treturn this.each( function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t} );\n\t},\n\n\twrap: function( html ) {\n\t\tvar htmlIsFunction = isFunction( html );\n\n\t\treturn this.each( function( i ) {\n\t\t\tjQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );\n\t\t} );\n\t},\n\n\tunwrap: function( selector ) {\n\t\tthis.parent( selector ).not( \"body\" ).each( function() {\n\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t} );\n\t\treturn this;\n\t}\n} );\n\n\njQuery.expr.pseudos.hidden = function( elem ) {\n\treturn !jQuery.expr.pseudos.visible( elem );\n};\njQuery.expr.pseudos.visible = function( elem ) {\n\treturn !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );\n};\n\n\n\n\njQuery.ajaxSettings.xhr = function() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch ( e ) {}\n};\n\nvar xhrSuccessStatus = {\n\n\t\t// File protocol always yields status code 0, assume 200\n\t\t0: 200,\n\n\t\t// Support: IE <=9 only\n\t\t// trac-1450: sometimes IE returns 1223 when it should be 204\n\t\t1223: 204\n\t},\n\txhrSupported = jQuery.ajaxSettings.xhr();\n\nsupport.cors = !!xhrSupported && ( \"withCredentials\" in xhrSupported );\nsupport.ajax = xhrSupported = !!xhrSupported;\n\njQuery.ajaxTransport( function( options ) {\n\tvar callback, errorCallback;\n\n\t// Cross domain only allowed if supported through XMLHttpRequest\n\tif ( support.cors || xhrSupported && !options.crossDomain ) {\n\t\treturn {\n\t\t\tsend: function( headers, complete ) {\n\t\t\t\tvar i,\n\t\t\t\t\txhr = options.xhr();\n\n\t\t\t\txhr.open(\n\t\t\t\t\toptions.type,\n\t\t\t\t\toptions.url,\n\t\t\t\t\toptions.async,\n\t\t\t\t\toptions.username,\n\t\t\t\t\toptions.password\n\t\t\t\t);\n\n\t\t\t\t// Apply custom fields if provided\n\t\t\t\tif ( options.xhrFields ) {\n\t\t\t\t\tfor ( i in options.xhrFields ) {\n\t\t\t\t\t\txhr[ i ] = options.xhrFields[ i ];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Override mime type if needed\n\t\t\t\tif ( options.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\txhr.overrideMimeType( options.mimeType );\n\t\t\t\t}\n\n\t\t\t\t// X-Requested-With header\n\t\t\t\t// For cross-domain requests, seeing as conditions for a preflight are\n\t\t\t\t// akin to a jigsaw puzzle, we simply never set it to be sure.\n\t\t\t\t// (it can always be set on a per-request basis or even using ajaxSetup)\n\t\t\t\t// For same-domain requests, won't change header if already provided.\n\t\t\t\tif ( !options.crossDomain && !headers[ \"X-Requested-With\" ] ) {\n\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t}\n\n\t\t\t\t// Set headers\n\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t}\n\n\t\t\t\t// Callback\n\t\t\t\tcallback = function( type ) {\n\t\t\t\t\treturn function() {\n\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\tcallback = errorCallback = xhr.onload =\n\t\t\t\t\t\t\t\txhr.onerror = xhr.onabort = xhr.ontimeout =\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = null;\n\n\t\t\t\t\t\t\tif ( type === \"abort\" ) {\n\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t} else if ( type === \"error\" ) {\n\n\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t// On a manual native abort, IE9 throws\n\t\t\t\t\t\t\t\t// errors on any property access that is not readyState\n\t\t\t\t\t\t\t\tif ( typeof xhr.status !== \"number\" ) {\n\t\t\t\t\t\t\t\t\tcomplete( 0, \"error\" );\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcomplete(\n\n\t\t\t\t\t\t\t\t\t\t// File: protocol always yields status 0; see trac-8605, trac-14207\n\t\t\t\t\t\t\t\t\t\txhr.status,\n\t\t\t\t\t\t\t\t\t\txhr.statusText\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcomplete(\n\t\t\t\t\t\t\t\t\txhrSuccessStatus[ xhr.status ] || xhr.status,\n\t\t\t\t\t\t\t\t\txhr.statusText,\n\n\t\t\t\t\t\t\t\t\t// Support: IE <=9 only\n\t\t\t\t\t\t\t\t\t// IE9 has no XHR2 but throws on binary (trac-11426)\n\t\t\t\t\t\t\t\t\t// For XHR2 non-text, let the caller handle it (gh-2498)\n\t\t\t\t\t\t\t\t\t( xhr.responseType || \"text\" ) !== \"text\"  ||\n\t\t\t\t\t\t\t\t\ttypeof xhr.responseText !== \"string\" ?\n\t\t\t\t\t\t\t\t\t\t{ binary: xhr.response } :\n\t\t\t\t\t\t\t\t\t\t{ text: xhr.responseText },\n\t\t\t\t\t\t\t\t\txhr.getAllResponseHeaders()\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\n\t\t\t\t// Listen to events\n\t\t\t\txhr.onload = callback();\n\t\t\t\terrorCallback = xhr.onerror = xhr.ontimeout = callback( \"error\" );\n\n\t\t\t\t// Support: IE 9 only\n\t\t\t\t// Use onreadystatechange to replace onabort\n\t\t\t\t// to handle uncaught aborts\n\t\t\t\tif ( xhr.onabort !== undefined ) {\n\t\t\t\t\txhr.onabort = errorCallback;\n\t\t\t\t} else {\n\t\t\t\t\txhr.onreadystatechange = function() {\n\n\t\t\t\t\t\t// Check readyState before timeout as it changes\n\t\t\t\t\t\tif ( xhr.readyState === 4 ) {\n\n\t\t\t\t\t\t\t// Allow onerror to be called first,\n\t\t\t\t\t\t\t// but that will not handle a native abort\n\t\t\t\t\t\t\t// Also, save errorCallback to a variable\n\t\t\t\t\t\t\t// as xhr.onerror cannot be accessed\n\t\t\t\t\t\t\twindow.setTimeout( function() {\n\t\t\t\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\t\t\t\terrorCallback();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Create the abort callback\n\t\t\t\tcallback = callback( \"abort\" );\n\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do send the request (this may raise an exception)\n\t\t\t\t\txhr.send( options.hasContent && options.data || null );\n\t\t\t\t} catch ( e ) {\n\n\t\t\t\t\t// trac-14683: Only rethrow if this hasn't been notified as an error yet\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\n// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)\njQuery.ajaxPrefilter( function( s ) {\n\tif ( s.crossDomain ) {\n\t\ts.contents.script = false;\n\t}\n} );\n\n// Install script dataType\njQuery.ajaxSetup( {\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, \" +\n\t\t\t\"application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /\\b(?:java|ecma)script\\b/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n} );\n\n// Handle cache's special case and crossDomain\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function( s ) {\n\n\t// This transport only deals with cross domain or forced-by-attrs requests\n\tif ( s.crossDomain || s.scriptAttrs ) {\n\t\tvar script, callback;\n\t\treturn {\n\t\t\tsend: function( _, complete ) {\n\t\t\t\tscript = jQuery( \"<script>\" )\n\t\t\t\t\t.attr( s.scriptAttrs || {} )\n\t\t\t\t\t.prop( { charset: s.scriptCharset, src: s.url } )\n\t\t\t\t\t.on( \"load error\", callback = function( evt ) {\n\t\t\t\t\t\tscript.remove();\n\t\t\t\t\t\tcallback = null;\n\t\t\t\t\t\tif ( evt ) {\n\t\t\t\t\t\t\tcomplete( evt.type === \"error\" ? 404 : 200, evt.type );\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\n\t\t\t\t// Use native DOM manipulation to avoid our domManip AJAX trickery\n\t\t\t\tdocument.head.appendChild( script[ 0 ] );\n\t\t\t},\n\t\t\tabort: function() {\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tcallback();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\nvar oldCallbacks = [],\n\trjsonp = /(=)\\?(?=&|$)|\\?\\?/;\n\n// Default jsonp settings\njQuery.ajaxSetup( {\n\tjsonp: \"callback\",\n\tjsonpCallback: function() {\n\t\tvar callback = oldCallbacks.pop() || ( jQuery.expando + \"_\" + ( nonce.guid++ ) );\n\t\tthis[ callback ] = true;\n\t\treturn callback;\n\t}\n} );\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n\tvar callbackName, overwritten, responseContainer,\n\t\tjsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?\n\t\t\t\"url\" :\n\t\t\ttypeof s.data === \"string\" &&\n\t\t\t\t( s.contentType || \"\" )\n\t\t\t\t\t.indexOf( \"application/x-www-form-urlencoded\" ) === 0 &&\n\t\t\t\trjsonp.test( s.data ) && \"data\"\n\t\t);\n\n\t// Handle iff the expected data type is \"jsonp\" or we have a parameter to set\n\tif ( jsonProp || s.dataTypes[ 0 ] === \"jsonp\" ) {\n\n\t\t// Get callback name, remembering preexisting value associated with it\n\t\tcallbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?\n\t\t\ts.jsonpCallback() :\n\t\t\ts.jsonpCallback;\n\n\t\t// Insert callback into url or form data\n\t\tif ( jsonProp ) {\n\t\t\ts[ jsonProp ] = s[ jsonProp ].replace( rjsonp, \"$1\" + callbackName );\n\t\t} else if ( s.jsonp !== false ) {\n\t\t\ts.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.jsonp + \"=\" + callbackName;\n\t\t}\n\n\t\t// Use data converter to retrieve json after script execution\n\t\ts.converters[ \"script json\" ] = function() {\n\t\t\tif ( !responseContainer ) {\n\t\t\t\tjQuery.error( callbackName + \" was not called\" );\n\t\t\t}\n\t\t\treturn responseContainer[ 0 ];\n\t\t};\n\n\t\t// Force json dataType\n\t\ts.dataTypes[ 0 ] = \"json\";\n\n\t\t// Install callback\n\t\toverwritten = window[ callbackName ];\n\t\twindow[ callbackName ] = function() {\n\t\t\tresponseContainer = arguments;\n\t\t};\n\n\t\t// Clean-up function (fires after converters)\n\t\tjqXHR.always( function() {\n\n\t\t\t// If previous value didn't exist - remove it\n\t\t\tif ( overwritten === undefined ) {\n\t\t\t\tjQuery( window ).removeProp( callbackName );\n\n\t\t\t// Otherwise restore preexisting value\n\t\t\t} else {\n\t\t\t\twindow[ callbackName ] = overwritten;\n\t\t\t}\n\n\t\t\t// Save back as free\n\t\t\tif ( s[ callbackName ] ) {\n\n\t\t\t\t// Make sure that re-using the options doesn't screw things around\n\t\t\t\ts.jsonpCallback = originalSettings.jsonpCallback;\n\n\t\t\t\t// Save the callback name for future use\n\t\t\t\toldCallbacks.push( callbackName );\n\t\t\t}\n\n\t\t\t// Call if it was a function and we have a response\n\t\t\tif ( responseContainer && isFunction( overwritten ) ) {\n\t\t\t\toverwritten( responseContainer[ 0 ] );\n\t\t\t}\n\n\t\t\tresponseContainer = overwritten = undefined;\n\t\t} );\n\n\t\t// Delegate to script\n\t\treturn \"script\";\n\t}\n} );\n\n\n\n\n// Support: Safari 8 only\n// In Safari 8 documents created via document.implementation.createHTMLDocument\n// collapse sibling forms: the second one becomes a child of the first one.\n// Because of that, this security measure has to be disabled in Safari 8.\n// https://bugs.webkit.org/show_bug.cgi?id=137337\nsupport.createHTMLDocument = ( function() {\n\tvar body = document.implementation.createHTMLDocument( \"\" ).body;\n\tbody.innerHTML = \"<form></form><form></form>\";\n\treturn body.childNodes.length === 2;\n} )();\n\n\n// Argument \"data\" should be string of html\n// context (optional): If specified, the fragment will be created in this context,\n// defaults to document\n// keepScripts (optional): If true, will include scripts passed in the html string\njQuery.parseHTML = function( data, context, keepScripts ) {\n\tif ( typeof data !== \"string\" ) {\n\t\treturn [];\n\t}\n\tif ( typeof context === \"boolean\" ) {\n\t\tkeepScripts = context;\n\t\tcontext = false;\n\t}\n\n\tvar base, parsed, scripts;\n\n\tif ( !context ) {\n\n\t\t// Stop scripts or inline event handlers from being executed immediately\n\t\t// by using document.implementation\n\t\tif ( support.createHTMLDocument ) {\n\t\t\tcontext = document.implementation.createHTMLDocument( \"\" );\n\n\t\t\t// Set the base href for the created document\n\t\t\t// so any parsed elements with URLs\n\t\t\t// are based on the document's URL (gh-2965)\n\t\t\tbase = context.createElement( \"base\" );\n\t\t\tbase.href = document.location.href;\n\t\t\tcontext.head.appendChild( base );\n\t\t} else {\n\t\t\tcontext = document;\n\t\t}\n\t}\n\n\tparsed = rsingleTag.exec( data );\n\tscripts = !keepScripts && [];\n\n\t// Single tag\n\tif ( parsed ) {\n\t\treturn [ context.createElement( parsed[ 1 ] ) ];\n\t}\n\n\tparsed = buildFragment( [ data ], context, scripts );\n\n\tif ( scripts && scripts.length ) {\n\t\tjQuery( scripts ).remove();\n\t}\n\n\treturn jQuery.merge( [], parsed.childNodes );\n};\n\n\n/**\n * Load a url into a page\n */\njQuery.fn.load = function( url, params, callback ) {\n\tvar selector, type, response,\n\t\tself = this,\n\t\toff = url.indexOf( \" \" );\n\n\tif ( off > -1 ) {\n\t\tselector = stripAndCollapse( url.slice( off ) );\n\t\turl = url.slice( 0, off );\n\t}\n\n\t// If it's a function\n\tif ( isFunction( params ) ) {\n\n\t\t// We assume that it's the callback\n\t\tcallback = params;\n\t\tparams = undefined;\n\n\t// Otherwise, build a param string\n\t} else if ( params && typeof params === \"object\" ) {\n\t\ttype = \"POST\";\n\t}\n\n\t// If we have elements to modify, make the request\n\tif ( self.length > 0 ) {\n\t\tjQuery.ajax( {\n\t\t\turl: url,\n\n\t\t\t// If \"type\" variable is undefined, then \"GET\" method will be used.\n\t\t\t// Make value of this field explicit since\n\t\t\t// user can override it through ajaxSetup method\n\t\t\ttype: type || \"GET\",\n\t\t\tdataType: \"html\",\n\t\t\tdata: params\n\t\t} ).done( function( responseText ) {\n\n\t\t\t// Save response for use in complete callback\n\t\t\tresponse = arguments;\n\n\t\t\tself.html( selector ?\n\n\t\t\t\t// If a selector was specified, locate the right elements in a dummy div\n\t\t\t\t// Exclude scripts to avoid IE 'Permission Denied' errors\n\t\t\t\tjQuery( \"<div>\" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :\n\n\t\t\t\t// Otherwise use the full result\n\t\t\t\tresponseText );\n\n\t\t// If the request succeeds, this function gets \"data\", \"status\", \"jqXHR\"\n\t\t// but they are ignored because response was set above.\n\t\t// If it fails, this function gets \"jqXHR\", \"status\", \"error\"\n\t\t} ).always( callback && function( jqXHR, status ) {\n\t\t\tself.each( function() {\n\t\t\t\tcallback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );\n\t\t\t} );\n\t\t} );\n\t}\n\n\treturn this;\n};\n\n\n\n\njQuery.expr.pseudos.animated = function( elem ) {\n\treturn jQuery.grep( jQuery.timers, function( fn ) {\n\t\treturn elem === fn.elem;\n\t} ).length;\n};\n\n\n\n\njQuery.offset = {\n\tsetOffset: function( elem, options, i ) {\n\t\tvar curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,\n\t\t\tposition = jQuery.css( elem, \"position\" ),\n\t\t\tcurElem = jQuery( elem ),\n\t\t\tprops = {};\n\n\t\t// Set position first, in-case top/left are set even on static elem\n\t\tif ( position === \"static\" ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\n\t\tcurOffset = curElem.offset();\n\t\tcurCSSTop = jQuery.css( elem, \"top\" );\n\t\tcurCSSLeft = jQuery.css( elem, \"left\" );\n\t\tcalculatePosition = ( position === \"absolute\" || position === \"fixed\" ) &&\n\t\t\t( curCSSTop + curCSSLeft ).indexOf( \"auto\" ) > -1;\n\n\t\t// Need to be able to calculate position if either\n\t\t// top or left is auto and position is either absolute or fixed\n\t\tif ( calculatePosition ) {\n\t\t\tcurPosition = curElem.position();\n\t\t\tcurTop = curPosition.top;\n\t\t\tcurLeft = curPosition.left;\n\n\t\t} else {\n\t\t\tcurTop = parseFloat( curCSSTop ) || 0;\n\t\t\tcurLeft = parseFloat( curCSSLeft ) || 0;\n\t\t}\n\n\t\tif ( isFunction( options ) ) {\n\n\t\t\t// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)\n\t\t\toptions = options.call( elem, i, jQuery.extend( {}, curOffset ) );\n\t\t}\n\n\t\tif ( options.top != null ) {\n\t\t\tprops.top = ( options.top - curOffset.top ) + curTop;\n\t\t}\n\t\tif ( options.left != null ) {\n\t\t\tprops.left = ( options.left - curOffset.left ) + curLeft;\n\t\t}\n\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\njQuery.fn.extend( {\n\n\t// offset() relates an element's border box to the document origin\n\toffset: function( options ) {\n\n\t\t// Preserve chaining for setter\n\t\tif ( arguments.length ) {\n\t\t\treturn options === undefined ?\n\t\t\t\tthis :\n\t\t\t\tthis.each( function( i ) {\n\t\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t\t} );\n\t\t}\n\n\t\tvar rect, win,\n\t\t\telem = this[ 0 ];\n\n\t\tif ( !elem ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Return zeros for disconnected and hidden (display: none) elements (gh-2310)\n\t\t// Support: IE <=11 only\n\t\t// Running getBoundingClientRect on a\n\t\t// disconnected node in IE throws an error\n\t\tif ( !elem.getClientRects().length ) {\n\t\t\treturn { top: 0, left: 0 };\n\t\t}\n\n\t\t// Get document-relative position by adding viewport scroll to viewport-relative gBCR\n\t\trect = elem.getBoundingClientRect();\n\t\twin = elem.ownerDocument.defaultView;\n\t\treturn {\n\t\t\ttop: rect.top + win.pageYOffset,\n\t\t\tleft: rect.left + win.pageXOffset\n\t\t};\n\t},\n\n\t// position() relates an element's margin box to its offset parent's padding box\n\t// This corresponds to the behavior of CSS absolute positioning\n\tposition: function() {\n\t\tif ( !this[ 0 ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar offsetParent, offset, doc,\n\t\t\telem = this[ 0 ],\n\t\t\tparentOffset = { top: 0, left: 0 };\n\n\t\t// position:fixed elements are offset from the viewport, which itself always has zero offset\n\t\tif ( jQuery.css( elem, \"position\" ) === \"fixed\" ) {\n\n\t\t\t// Assume position:fixed implies availability of getBoundingClientRect\n\t\t\toffset = elem.getBoundingClientRect();\n\n\t\t} else {\n\t\t\toffset = this.offset();\n\n\t\t\t// Account for the *real* offset parent, which can be the document or its root element\n\t\t\t// when a statically positioned element is identified\n\t\t\tdoc = elem.ownerDocument;\n\t\t\toffsetParent = elem.offsetParent || doc.documentElement;\n\t\t\twhile ( offsetParent &&\n\t\t\t\t( offsetParent === doc.body || offsetParent === doc.documentElement ) &&\n\t\t\t\tjQuery.css( offsetParent, \"position\" ) === \"static\" ) {\n\n\t\t\t\toffsetParent = offsetParent.parentNode;\n\t\t\t}\n\t\t\tif ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {\n\n\t\t\t\t// Incorporate borders into its offset, since they are outside its content origin\n\t\t\t\tparentOffset = jQuery( offsetParent ).offset();\n\t\t\t\tparentOffset.top += jQuery.css( offsetParent, \"borderTopWidth\", true );\n\t\t\t\tparentOffset.left += jQuery.css( offsetParent, \"borderLeftWidth\", true );\n\t\t\t}\n\t\t}\n\n\t\t// Subtract parent offsets and element margins\n\t\treturn {\n\t\t\ttop: offset.top - parentOffset.top - jQuery.css( elem, \"marginTop\", true ),\n\t\t\tleft: offset.left - parentOffset.left - jQuery.css( elem, \"marginLeft\", true )\n\t\t};\n\t},\n\n\t// This method will return documentElement in the following cases:\n\t// 1) For the element inside the iframe without offsetParent, this method will return\n\t//    documentElement of the parent window\n\t// 2) For the hidden or detached element\n\t// 3) For body or html element, i.e. in case of the html node - it will return itself\n\t//\n\t// but those exceptions were never presented as a real life use-cases\n\t// and might be considered as more preferable results.\n\t//\n\t// This logic, however, is not guaranteed and can change at any point in the future\n\toffsetParent: function() {\n\t\treturn this.map( function() {\n\t\t\tvar offsetParent = this.offsetParent;\n\n\t\t\twhile ( offsetParent && jQuery.css( offsetParent, \"position\" ) === \"static\" ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\n\t\t\treturn offsetParent || documentElement;\n\t\t} );\n\t}\n} );\n\n// Create scrollLeft and scrollTop methods\njQuery.each( { scrollLeft: \"pageXOffset\", scrollTop: \"pageYOffset\" }, function( method, prop ) {\n\tvar top = \"pageYOffset\" === prop;\n\n\tjQuery.fn[ method ] = function( val ) {\n\t\treturn access( this, function( elem, method, val ) {\n\n\t\t\t// Coalesce documents and windows\n\t\t\tvar win;\n\t\t\tif ( isWindow( elem ) ) {\n\t\t\t\twin = elem;\n\t\t\t} else if ( elem.nodeType === 9 ) {\n\t\t\t\twin = elem.defaultView;\n\t\t\t}\n\n\t\t\tif ( val === undefined ) {\n\t\t\t\treturn win ? win[ prop ] : elem[ method ];\n\t\t\t}\n\n\t\t\tif ( win ) {\n\t\t\t\twin.scrollTo(\n\t\t\t\t\t!top ? val : win.pageXOffset,\n\t\t\t\t\ttop ? val : win.pageYOffset\n\t\t\t\t);\n\n\t\t\t} else {\n\t\t\t\telem[ method ] = val;\n\t\t\t}\n\t\t}, method, val, arguments.length );\n\t};\n} );\n\n// Support: Safari <=7 - 9.1, Chrome <=37 - 49\n// Add the top/left cssHooks using jQuery.fn.position\n// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084\n// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347\n// getComputedStyle returns percent when specified for top/left/bottom/right;\n// rather than make the css module depend on the offset module, just check for it here\njQuery.each( [ \"top\", \"left\" ], function( _i, prop ) {\n\tjQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,\n\t\tfunction( elem, computed ) {\n\t\t\tif ( computed ) {\n\t\t\t\tcomputed = curCSS( elem, prop );\n\n\t\t\t\t// If curCSS returns percentage, fallback to offset\n\t\t\t\treturn rnumnonpx.test( computed ) ?\n\t\t\t\t\tjQuery( elem ).position()[ prop ] + \"px\" :\n\t\t\t\t\tcomputed;\n\t\t\t}\n\t\t}\n\t);\n} );\n\n\n// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods\njQuery.each( { Height: \"height\", Width: \"width\" }, function( name, type ) {\n\tjQuery.each( {\n\t\tpadding: \"inner\" + name,\n\t\tcontent: type,\n\t\t\"\": \"outer\" + name\n\t}, function( defaultExtra, funcName ) {\n\n\t\t// Margin is only for outerHeight, outerWidth\n\t\tjQuery.fn[ funcName ] = function( margin, value ) {\n\t\t\tvar chainable = arguments.length && ( defaultExtra || typeof margin !== \"boolean\" ),\n\t\t\t\textra = defaultExtra || ( margin === true || value === true ? \"margin\" : \"border\" );\n\n\t\t\treturn access( this, function( elem, type, value ) {\n\t\t\t\tvar doc;\n\n\t\t\t\tif ( isWindow( elem ) ) {\n\n\t\t\t\t\t// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)\n\t\t\t\t\treturn funcName.indexOf( \"outer\" ) === 0 ?\n\t\t\t\t\t\telem[ \"inner\" + name ] :\n\t\t\t\t\t\telem.document.documentElement[ \"client\" + name ];\n\t\t\t\t}\n\n\t\t\t\t// Get document width or height\n\t\t\t\tif ( elem.nodeType === 9 ) {\n\t\t\t\t\tdoc = elem.documentElement;\n\n\t\t\t\t\t// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],\n\t\t\t\t\t// whichever is greatest\n\t\t\t\t\treturn Math.max(\n\t\t\t\t\t\telem.body[ \"scroll\" + name ], doc[ \"scroll\" + name ],\n\t\t\t\t\t\telem.body[ \"offset\" + name ], doc[ \"offset\" + name ],\n\t\t\t\t\t\tdoc[ \"client\" + name ]\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn value === undefined ?\n\n\t\t\t\t\t// Get width or height on the element, requesting but not forcing parseFloat\n\t\t\t\t\tjQuery.css( elem, type, extra ) :\n\n\t\t\t\t\t// Set width or height on the element\n\t\t\t\t\tjQuery.style( elem, type, value, extra );\n\t\t\t}, type, chainable ? margin : undefined, chainable );\n\t\t};\n\t} );\n} );\n\n\njQuery.each( [\n\t\"ajaxStart\",\n\t\"ajaxStop\",\n\t\"ajaxComplete\",\n\t\"ajaxError\",\n\t\"ajaxSuccess\",\n\t\"ajaxSend\"\n], function( _i, type ) {\n\tjQuery.fn[ type ] = function( fn ) {\n\t\treturn this.on( type, fn );\n\t};\n} );\n\n\n\n\njQuery.fn.extend( {\n\n\tbind: function( types, data, fn ) {\n\t\treturn this.on( types, null, data, fn );\n\t},\n\tunbind: function( types, fn ) {\n\t\treturn this.off( types, null, fn );\n\t},\n\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.on( types, selector, data, fn );\n\t},\n\tundelegate: function( selector, types, fn ) {\n\n\t\t// ( namespace ) or ( selector, types [, fn] )\n\t\treturn arguments.length === 1 ?\n\t\t\tthis.off( selector, \"**\" ) :\n\t\t\tthis.off( types, selector || \"**\", fn );\n\t},\n\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n} );\n\njQuery.each(\n\t( \"blur focus focusin focusout resize scroll click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup contextmenu\" ).split( \" \" ),\n\tfunction( _i, name ) {\n\n\t\t// Handle event binding\n\t\tjQuery.fn[ name ] = function( data, fn ) {\n\t\t\treturn arguments.length > 0 ?\n\t\t\t\tthis.on( name, null, data, fn ) :\n\t\t\t\tthis.trigger( name );\n\t\t};\n\t}\n);\n\n\n\n\n// Support: Android <=4.0 only\n// Make sure we trim BOM and NBSP\n// Require that the \"whitespace run\" starts from a non-whitespace\n// to avoid O(N^2) behavior when the engine would try matching \"\\s+$\" at each space position.\nvar rtrim = /^[\\s\\uFEFF\\xA0]+|([^\\s\\uFEFF\\xA0])[\\s\\uFEFF\\xA0]+$/g;\n\n// Bind a function to a context, optionally partially applying any\n// arguments.\n// jQuery.proxy is deprecated to promote standards (specifically Function#bind)\n// However, it is not slated for removal any time soon\njQuery.proxy = function( fn, context ) {\n\tvar tmp, args, proxy;\n\n\tif ( typeof context === \"string\" ) {\n\t\ttmp = fn[ context ];\n\t\tcontext = fn;\n\t\tfn = tmp;\n\t}\n\n\t// Quick check to determine if target is callable, in the spec\n\t// this throws a TypeError, but we will just return undefined.\n\tif ( !isFunction( fn ) ) {\n\t\treturn undefined;\n\t}\n\n\t// Simulated bind\n\targs = slice.call( arguments, 2 );\n\tproxy = function() {\n\t\treturn fn.apply( context || this, args.concat( slice.call( arguments ) ) );\n\t};\n\n\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\tproxy.guid = fn.guid = fn.guid || jQuery.guid++;\n\n\treturn proxy;\n};\n\njQuery.holdReady = function( hold ) {\n\tif ( hold ) {\n\t\tjQuery.readyWait++;\n\t} else {\n\t\tjQuery.ready( true );\n\t}\n};\njQuery.isArray = Array.isArray;\njQuery.parseJSON = JSON.parse;\njQuery.nodeName = nodeName;\njQuery.isFunction = isFunction;\njQuery.isWindow = isWindow;\njQuery.camelCase = camelCase;\njQuery.type = toType;\n\njQuery.now = Date.now;\n\njQuery.isNumeric = function( obj ) {\n\n\t// As of jQuery 3.0, isNumeric is limited to\n\t// strings and numbers (primitives or objects)\n\t// that can be coerced to finite numbers (gh-2662)\n\tvar type = jQuery.type( obj );\n\treturn ( type === \"number\" || type === \"string\" ) &&\n\n\t\t// parseFloat NaNs numeric-cast false positives (\"\")\n\t\t// ...but misinterprets leading-number strings, particularly hex literals (\"0x...\")\n\t\t// subtraction forces infinities to NaN\n\t\t!isNaN( obj - parseFloat( obj ) );\n};\n\njQuery.trim = function( text ) {\n\treturn text == null ?\n\t\t\"\" :\n\t\t( text + \"\" ).replace( rtrim, \"$1\" );\n};\n\n\n\n// Register as a named AMD module, since jQuery can be concatenated with other\n// files that may use define, but not via a proper concatenation script that\n// understands anonymous AMD modules. A named AMD is safest and most robust\n// way to register. Lowercase jquery is used because AMD module names are\n// derived from file names, and jQuery is normally delivered in a lowercase\n// file name. Do this after creating the global so that if an AMD module wants\n// to call noConflict to hide this version of jQuery, it will work.\n\n// Note that for maximum portability, libraries that are not jQuery should\n// declare themselves as anonymous modules, and avoid setting a global if an\n// AMD loader is present. jQuery is a special case. For more information, see\n// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon\n\nif ( typeof define === \"function\" && define.amd ) {\n\tdefine( \"jquery\", [], function() {\n\t\treturn jQuery;\n\t} );\n}\n\n\n\n\nvar\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$;\n\njQuery.noConflict = function( deep ) {\n\tif ( window.$ === jQuery ) {\n\t\twindow.$ = _$;\n\t}\n\n\tif ( deep && window.jQuery === jQuery ) {\n\t\twindow.jQuery = _jQuery;\n\t}\n\n\treturn jQuery;\n};\n\n// Expose jQuery and $ identifiers, even in AMD\n// (trac-7102#comment:10, https://github.com/jquery/jquery/pull/557)\n// and CommonJS for browser emulators (trac-13566)\nif ( typeof noGlobal === \"undefined\" ) {\n\twindow.jQuery = window.$ = jQuery;\n}\n\n\n\n\nreturn jQuery;\n} );", "/**\n * @popperjs/core v2.11.8 - MIT License\n */\n\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n  typeof define === 'function' && define.amd ? define(['exports'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Popper = {}));\n}(this, (function (exports) { 'use strict';\n\n  function getWindow(node) {\n    if (node == null) {\n      return window;\n    }\n\n    if (node.toString() !== '[object Window]') {\n      var ownerDocument = node.ownerDocument;\n      return ownerDocument ? ownerDocument.defaultView || window : window;\n    }\n\n    return node;\n  }\n\n  function isElement(node) {\n    var OwnElement = getWindow(node).Element;\n    return node instanceof OwnElement || node instanceof Element;\n  }\n\n  function isHTMLElement(node) {\n    var OwnElement = getWindow(node).HTMLElement;\n    return node instanceof OwnElement || node instanceof HTMLElement;\n  }\n\n  function isShadowRoot(node) {\n    // IE 11 has no ShadowRoot\n    if (typeof ShadowRoot === 'undefined') {\n      return false;\n    }\n\n    var OwnElement = getWindow(node).ShadowRoot;\n    return node instanceof OwnElement || node instanceof ShadowRoot;\n  }\n\n  var max = Math.max;\n  var min = Math.min;\n  var round = Math.round;\n\n  function getUAString() {\n    var uaData = navigator.userAgentData;\n\n    if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n      return uaData.brands.map(function (item) {\n        return item.brand + \"/\" + item.version;\n      }).join(' ');\n    }\n\n    return navigator.userAgent;\n  }\n\n  function isLayoutViewport() {\n    return !/^((?!chrome|android).)*safari/i.test(getUAString());\n  }\n\n  function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n    if (includeScale === void 0) {\n      includeScale = false;\n    }\n\n    if (isFixedStrategy === void 0) {\n      isFixedStrategy = false;\n    }\n\n    var clientRect = element.getBoundingClientRect();\n    var scaleX = 1;\n    var scaleY = 1;\n\n    if (includeScale && isHTMLElement(element)) {\n      scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n      scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n    }\n\n    var _ref = isElement(element) ? getWindow(element) : window,\n        visualViewport = _ref.visualViewport;\n\n    var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n    var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n    var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n    var width = clientRect.width / scaleX;\n    var height = clientRect.height / scaleY;\n    return {\n      width: width,\n      height: height,\n      top: y,\n      right: x + width,\n      bottom: y + height,\n      left: x,\n      x: x,\n      y: y\n    };\n  }\n\n  function getWindowScroll(node) {\n    var win = getWindow(node);\n    var scrollLeft = win.pageXOffset;\n    var scrollTop = win.pageYOffset;\n    return {\n      scrollLeft: scrollLeft,\n      scrollTop: scrollTop\n    };\n  }\n\n  function getHTMLElementScroll(element) {\n    return {\n      scrollLeft: element.scrollLeft,\n      scrollTop: element.scrollTop\n    };\n  }\n\n  function getNodeScroll(node) {\n    if (node === getWindow(node) || !isHTMLElement(node)) {\n      return getWindowScroll(node);\n    } else {\n      return getHTMLElementScroll(node);\n    }\n  }\n\n  function getNodeName(element) {\n    return element ? (element.nodeName || '').toLowerCase() : null;\n  }\n\n  function getDocumentElement(element) {\n    // $FlowFixMe[incompatible-return]: assume body is always available\n    return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n    element.document) || window.document).documentElement;\n  }\n\n  function getWindowScrollBarX(element) {\n    // If <html> has a CSS width greater than the viewport, then this will be\n    // incorrect for RTL.\n    // Popper 1 is broken in this case and never had a bug report so let's assume\n    // it's not an issue. I don't think anyone ever specifies width on <html>\n    // anyway.\n    // Browsers where the left scrollbar doesn't cause an issue report `0` for\n    // this (e.g. Edge 2019, IE11, Safari)\n    return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n  }\n\n  function getComputedStyle(element) {\n    return getWindow(element).getComputedStyle(element);\n  }\n\n  function isScrollParent(element) {\n    // Firefox wants us to check `-x` and `-y` variations as well\n    var _getComputedStyle = getComputedStyle(element),\n        overflow = _getComputedStyle.overflow,\n        overflowX = _getComputedStyle.overflowX,\n        overflowY = _getComputedStyle.overflowY;\n\n    return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n  }\n\n  function isElementScaled(element) {\n    var rect = element.getBoundingClientRect();\n    var scaleX = round(rect.width) / element.offsetWidth || 1;\n    var scaleY = round(rect.height) / element.offsetHeight || 1;\n    return scaleX !== 1 || scaleY !== 1;\n  } // Returns the composite rect of an element relative to its offsetParent.\n  // Composite means it takes into account transforms as well as layout.\n\n\n  function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n    if (isFixed === void 0) {\n      isFixed = false;\n    }\n\n    var isOffsetParentAnElement = isHTMLElement(offsetParent);\n    var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n    var documentElement = getDocumentElement(offsetParent);\n    var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n    var scroll = {\n      scrollLeft: 0,\n      scrollTop: 0\n    };\n    var offsets = {\n      x: 0,\n      y: 0\n    };\n\n    if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n      if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n      isScrollParent(documentElement)) {\n        scroll = getNodeScroll(offsetParent);\n      }\n\n      if (isHTMLElement(offsetParent)) {\n        offsets = getBoundingClientRect(offsetParent, true);\n        offsets.x += offsetParent.clientLeft;\n        offsets.y += offsetParent.clientTop;\n      } else if (documentElement) {\n        offsets.x = getWindowScrollBarX(documentElement);\n      }\n    }\n\n    return {\n      x: rect.left + scroll.scrollLeft - offsets.x,\n      y: rect.top + scroll.scrollTop - offsets.y,\n      width: rect.width,\n      height: rect.height\n    };\n  }\n\n  // means it doesn't take into account transforms.\n\n  function getLayoutRect(element) {\n    var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n    // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n    var width = element.offsetWidth;\n    var height = element.offsetHeight;\n\n    if (Math.abs(clientRect.width - width) <= 1) {\n      width = clientRect.width;\n    }\n\n    if (Math.abs(clientRect.height - height) <= 1) {\n      height = clientRect.height;\n    }\n\n    return {\n      x: element.offsetLeft,\n      y: element.offsetTop,\n      width: width,\n      height: height\n    };\n  }\n\n  function getParentNode(element) {\n    if (getNodeName(element) === 'html') {\n      return element;\n    }\n\n    return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n      // $FlowFixMe[incompatible-return]\n      // $FlowFixMe[prop-missing]\n      element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n      element.parentNode || ( // DOM Element detected\n      isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n      // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n      getDocumentElement(element) // fallback\n\n    );\n  }\n\n  function getScrollParent(node) {\n    if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n      // $FlowFixMe[incompatible-return]: assume body is always available\n      return node.ownerDocument.body;\n    }\n\n    if (isHTMLElement(node) && isScrollParent(node)) {\n      return node;\n    }\n\n    return getScrollParent(getParentNode(node));\n  }\n\n  /*\n  given a DOM element, return the list of all scroll parents, up the list of ancesors\n  until we get to the top window object. This list is what we attach scroll listeners\n  to, because if any of these parent elements scroll, we'll need to re-calculate the\n  reference element's position.\n  */\n\n  function listScrollParents(element, list) {\n    var _element$ownerDocumen;\n\n    if (list === void 0) {\n      list = [];\n    }\n\n    var scrollParent = getScrollParent(element);\n    var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n    var win = getWindow(scrollParent);\n    var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n    var updatedList = list.concat(target);\n    return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n    updatedList.concat(listScrollParents(getParentNode(target)));\n  }\n\n  function isTableElement(element) {\n    return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n  }\n\n  function getTrueOffsetParent(element) {\n    if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n    getComputedStyle(element).position === 'fixed') {\n      return null;\n    }\n\n    return element.offsetParent;\n  } // `.offsetParent` reports `null` for fixed elements, while absolute elements\n  // return the containing block\n\n\n  function getContainingBlock(element) {\n    var isFirefox = /firefox/i.test(getUAString());\n    var isIE = /Trident/i.test(getUAString());\n\n    if (isIE && isHTMLElement(element)) {\n      // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n      var elementCss = getComputedStyle(element);\n\n      if (elementCss.position === 'fixed') {\n        return null;\n      }\n    }\n\n    var currentNode = getParentNode(element);\n\n    if (isShadowRoot(currentNode)) {\n      currentNode = currentNode.host;\n    }\n\n    while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n      var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n      // create a containing block.\n      // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n      if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n        return currentNode;\n      } else {\n        currentNode = currentNode.parentNode;\n      }\n    }\n\n    return null;\n  } // Gets the closest ancestor positioned element. Handles some edge cases,\n  // such as table ancestors and cross browser bugs.\n\n\n  function getOffsetParent(element) {\n    var window = getWindow(element);\n    var offsetParent = getTrueOffsetParent(element);\n\n    while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n      offsetParent = getTrueOffsetParent(offsetParent);\n    }\n\n    if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n      return window;\n    }\n\n    return offsetParent || getContainingBlock(element) || window;\n  }\n\n  var top = 'top';\n  var bottom = 'bottom';\n  var right = 'right';\n  var left = 'left';\n  var auto = 'auto';\n  var basePlacements = [top, bottom, right, left];\n  var start = 'start';\n  var end = 'end';\n  var clippingParents = 'clippingParents';\n  var viewport = 'viewport';\n  var popper = 'popper';\n  var reference = 'reference';\n  var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n    return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n  }, []);\n  var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n    return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n  }, []); // modifiers that need to read the DOM\n\n  var beforeRead = 'beforeRead';\n  var read = 'read';\n  var afterRead = 'afterRead'; // pure-logic modifiers\n\n  var beforeMain = 'beforeMain';\n  var main = 'main';\n  var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\n  var beforeWrite = 'beforeWrite';\n  var write = 'write';\n  var afterWrite = 'afterWrite';\n  var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];\n\n  function order(modifiers) {\n    var map = new Map();\n    var visited = new Set();\n    var result = [];\n    modifiers.forEach(function (modifier) {\n      map.set(modifier.name, modifier);\n    }); // On visiting object, check for its dependencies and visit them recursively\n\n    function sort(modifier) {\n      visited.add(modifier.name);\n      var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n      requires.forEach(function (dep) {\n        if (!visited.has(dep)) {\n          var depModifier = map.get(dep);\n\n          if (depModifier) {\n            sort(depModifier);\n          }\n        }\n      });\n      result.push(modifier);\n    }\n\n    modifiers.forEach(function (modifier) {\n      if (!visited.has(modifier.name)) {\n        // check for visited object\n        sort(modifier);\n      }\n    });\n    return result;\n  }\n\n  function orderModifiers(modifiers) {\n    // order based on dependencies\n    var orderedModifiers = order(modifiers); // order based on phase\n\n    return modifierPhases.reduce(function (acc, phase) {\n      return acc.concat(orderedModifiers.filter(function (modifier) {\n        return modifier.phase === phase;\n      }));\n    }, []);\n  }\n\n  function debounce(fn) {\n    var pending;\n    return function () {\n      if (!pending) {\n        pending = new Promise(function (resolve) {\n          Promise.resolve().then(function () {\n            pending = undefined;\n            resolve(fn());\n          });\n        });\n      }\n\n      return pending;\n    };\n  }\n\n  function mergeByName(modifiers) {\n    var merged = modifiers.reduce(function (merged, current) {\n      var existing = merged[current.name];\n      merged[current.name] = existing ? Object.assign({}, existing, current, {\n        options: Object.assign({}, existing.options, current.options),\n        data: Object.assign({}, existing.data, current.data)\n      }) : current;\n      return merged;\n    }, {}); // IE11 does not support Object.values\n\n    return Object.keys(merged).map(function (key) {\n      return merged[key];\n    });\n  }\n\n  function getViewportRect(element, strategy) {\n    var win = getWindow(element);\n    var html = getDocumentElement(element);\n    var visualViewport = win.visualViewport;\n    var width = html.clientWidth;\n    var height = html.clientHeight;\n    var x = 0;\n    var y = 0;\n\n    if (visualViewport) {\n      width = visualViewport.width;\n      height = visualViewport.height;\n      var layoutViewport = isLayoutViewport();\n\n      if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n        x = visualViewport.offsetLeft;\n        y = visualViewport.offsetTop;\n      }\n    }\n\n    return {\n      width: width,\n      height: height,\n      x: x + getWindowScrollBarX(element),\n      y: y\n    };\n  }\n\n  // of the `<html>` and `<body>` rect bounds if horizontally scrollable\n\n  function getDocumentRect(element) {\n    var _element$ownerDocumen;\n\n    var html = getDocumentElement(element);\n    var winScroll = getWindowScroll(element);\n    var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n    var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n    var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n    var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n    var y = -winScroll.scrollTop;\n\n    if (getComputedStyle(body || html).direction === 'rtl') {\n      x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n    }\n\n    return {\n      width: width,\n      height: height,\n      x: x,\n      y: y\n    };\n  }\n\n  function contains(parent, child) {\n    var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n    if (parent.contains(child)) {\n      return true;\n    } // then fallback to custom implementation with Shadow DOM support\n    else if (rootNode && isShadowRoot(rootNode)) {\n        var next = child;\n\n        do {\n          if (next && parent.isSameNode(next)) {\n            return true;\n          } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n          next = next.parentNode || next.host;\n        } while (next);\n      } // Give up, the result is false\n\n\n    return false;\n  }\n\n  function rectToClientRect(rect) {\n    return Object.assign({}, rect, {\n      left: rect.x,\n      top: rect.y,\n      right: rect.x + rect.width,\n      bottom: rect.y + rect.height\n    });\n  }\n\n  function getInnerBoundingClientRect(element, strategy) {\n    var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n    rect.top = rect.top + element.clientTop;\n    rect.left = rect.left + element.clientLeft;\n    rect.bottom = rect.top + element.clientHeight;\n    rect.right = rect.left + element.clientWidth;\n    rect.width = element.clientWidth;\n    rect.height = element.clientHeight;\n    rect.x = rect.left;\n    rect.y = rect.top;\n    return rect;\n  }\n\n  function getClientRectFromMixedType(element, clippingParent, strategy) {\n    return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n  } // A \"clipping parent\" is an overflowable container with the characteristic of\n  // clipping (or hiding) overflowing elements with a position different from\n  // `initial`\n\n\n  function getClippingParents(element) {\n    var clippingParents = listScrollParents(getParentNode(element));\n    var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n    var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n    if (!isElement(clipperElement)) {\n      return [];\n    } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n    return clippingParents.filter(function (clippingParent) {\n      return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n    });\n  } // Gets the maximum area that the element is visible in due to any number of\n  // clipping parents\n\n\n  function getClippingRect(element, boundary, rootBoundary, strategy) {\n    var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n    var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n    var firstClippingParent = clippingParents[0];\n    var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n      var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n      accRect.top = max(rect.top, accRect.top);\n      accRect.right = min(rect.right, accRect.right);\n      accRect.bottom = min(rect.bottom, accRect.bottom);\n      accRect.left = max(rect.left, accRect.left);\n      return accRect;\n    }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n    clippingRect.width = clippingRect.right - clippingRect.left;\n    clippingRect.height = clippingRect.bottom - clippingRect.top;\n    clippingRect.x = clippingRect.left;\n    clippingRect.y = clippingRect.top;\n    return clippingRect;\n  }\n\n  function getBasePlacement(placement) {\n    return placement.split('-')[0];\n  }\n\n  function getVariation(placement) {\n    return placement.split('-')[1];\n  }\n\n  function getMainAxisFromPlacement(placement) {\n    return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n  }\n\n  function computeOffsets(_ref) {\n    var reference = _ref.reference,\n        element = _ref.element,\n        placement = _ref.placement;\n    var basePlacement = placement ? getBasePlacement(placement) : null;\n    var variation = placement ? getVariation(placement) : null;\n    var commonX = reference.x + reference.width / 2 - element.width / 2;\n    var commonY = reference.y + reference.height / 2 - element.height / 2;\n    var offsets;\n\n    switch (basePlacement) {\n      case top:\n        offsets = {\n          x: commonX,\n          y: reference.y - element.height\n        };\n        break;\n\n      case bottom:\n        offsets = {\n          x: commonX,\n          y: reference.y + reference.height\n        };\n        break;\n\n      case right:\n        offsets = {\n          x: reference.x + reference.width,\n          y: commonY\n        };\n        break;\n\n      case left:\n        offsets = {\n          x: reference.x - element.width,\n          y: commonY\n        };\n        break;\n\n      default:\n        offsets = {\n          x: reference.x,\n          y: reference.y\n        };\n    }\n\n    var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n    if (mainAxis != null) {\n      var len = mainAxis === 'y' ? 'height' : 'width';\n\n      switch (variation) {\n        case start:\n          offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n          break;\n\n        case end:\n          offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n          break;\n      }\n    }\n\n    return offsets;\n  }\n\n  function getFreshSideObject() {\n    return {\n      top: 0,\n      right: 0,\n      bottom: 0,\n      left: 0\n    };\n  }\n\n  function mergePaddingObject(paddingObject) {\n    return Object.assign({}, getFreshSideObject(), paddingObject);\n  }\n\n  function expandToHashMap(value, keys) {\n    return keys.reduce(function (hashMap, key) {\n      hashMap[key] = value;\n      return hashMap;\n    }, {});\n  }\n\n  function detectOverflow(state, options) {\n    if (options === void 0) {\n      options = {};\n    }\n\n    var _options = options,\n        _options$placement = _options.placement,\n        placement = _options$placement === void 0 ? state.placement : _options$placement,\n        _options$strategy = _options.strategy,\n        strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n        _options$boundary = _options.boundary,\n        boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n        _options$rootBoundary = _options.rootBoundary,\n        rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n        _options$elementConte = _options.elementContext,\n        elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n        _options$altBoundary = _options.altBoundary,\n        altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n        _options$padding = _options.padding,\n        padding = _options$padding === void 0 ? 0 : _options$padding;\n    var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n    var altContext = elementContext === popper ? reference : popper;\n    var popperRect = state.rects.popper;\n    var element = state.elements[altBoundary ? altContext : elementContext];\n    var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n    var referenceClientRect = getBoundingClientRect(state.elements.reference);\n    var popperOffsets = computeOffsets({\n      reference: referenceClientRect,\n      element: popperRect,\n      strategy: 'absolute',\n      placement: placement\n    });\n    var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n    var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n    // 0 or negative = within the clipping rect\n\n    var overflowOffsets = {\n      top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n      bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n      left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n      right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n    };\n    var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n    if (elementContext === popper && offsetData) {\n      var offset = offsetData[placement];\n      Object.keys(overflowOffsets).forEach(function (key) {\n        var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n        var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n        overflowOffsets[key] += offset[axis] * multiply;\n      });\n    }\n\n    return overflowOffsets;\n  }\n\n  var DEFAULT_OPTIONS = {\n    placement: 'bottom',\n    modifiers: [],\n    strategy: 'absolute'\n  };\n\n  function areValidElements() {\n    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n      args[_key] = arguments[_key];\n    }\n\n    return !args.some(function (element) {\n      return !(element && typeof element.getBoundingClientRect === 'function');\n    });\n  }\n\n  function popperGenerator(generatorOptions) {\n    if (generatorOptions === void 0) {\n      generatorOptions = {};\n    }\n\n    var _generatorOptions = generatorOptions,\n        _generatorOptions$def = _generatorOptions.defaultModifiers,\n        defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n        _generatorOptions$def2 = _generatorOptions.defaultOptions,\n        defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n    return function createPopper(reference, popper, options) {\n      if (options === void 0) {\n        options = defaultOptions;\n      }\n\n      var state = {\n        placement: 'bottom',\n        orderedModifiers: [],\n        options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n        modifiersData: {},\n        elements: {\n          reference: reference,\n          popper: popper\n        },\n        attributes: {},\n        styles: {}\n      };\n      var effectCleanupFns = [];\n      var isDestroyed = false;\n      var instance = {\n        state: state,\n        setOptions: function setOptions(setOptionsAction) {\n          var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n          cleanupModifierEffects();\n          state.options = Object.assign({}, defaultOptions, state.options, options);\n          state.scrollParents = {\n            reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n            popper: listScrollParents(popper)\n          }; // Orders the modifiers based on their dependencies and `phase`\n          // properties\n\n          var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n          state.orderedModifiers = orderedModifiers.filter(function (m) {\n            return m.enabled;\n          });\n          runModifierEffects();\n          return instance.update();\n        },\n        // Sync update \u2013 it will always be executed, even if not necessary. This\n        // is useful for low frequency updates where sync behavior simplifies the\n        // logic.\n        // For high frequency updates (e.g. `resize` and `scroll` events), always\n        // prefer the async Popper#update method\n        forceUpdate: function forceUpdate() {\n          if (isDestroyed) {\n            return;\n          }\n\n          var _state$elements = state.elements,\n              reference = _state$elements.reference,\n              popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n          // anymore\n\n          if (!areValidElements(reference, popper)) {\n            return;\n          } // Store the reference and popper rects to be read by modifiers\n\n\n          state.rects = {\n            reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n            popper: getLayoutRect(popper)\n          }; // Modifiers have the ability to reset the current update cycle. The\n          // most common use case for this is the `flip` modifier changing the\n          // placement, which then needs to re-run all the modifiers, because the\n          // logic was previously ran for the previous placement and is therefore\n          // stale/incorrect\n\n          state.reset = false;\n          state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n          // is filled with the initial data specified by the modifier. This means\n          // it doesn't persist and is fresh on each update.\n          // To ensure persistent data, use `${name}#persistent`\n\n          state.orderedModifiers.forEach(function (modifier) {\n            return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n          });\n\n          for (var index = 0; index < state.orderedModifiers.length; index++) {\n            if (state.reset === true) {\n              state.reset = false;\n              index = -1;\n              continue;\n            }\n\n            var _state$orderedModifie = state.orderedModifiers[index],\n                fn = _state$orderedModifie.fn,\n                _state$orderedModifie2 = _state$orderedModifie.options,\n                _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n                name = _state$orderedModifie.name;\n\n            if (typeof fn === 'function') {\n              state = fn({\n                state: state,\n                options: _options,\n                name: name,\n                instance: instance\n              }) || state;\n            }\n          }\n        },\n        // Async and optimistically optimized update \u2013 it will not be executed if\n        // not necessary (debounced to run at most once-per-tick)\n        update: debounce(function () {\n          return new Promise(function (resolve) {\n            instance.forceUpdate();\n            resolve(state);\n          });\n        }),\n        destroy: function destroy() {\n          cleanupModifierEffects();\n          isDestroyed = true;\n        }\n      };\n\n      if (!areValidElements(reference, popper)) {\n        return instance;\n      }\n\n      instance.setOptions(options).then(function (state) {\n        if (!isDestroyed && options.onFirstUpdate) {\n          options.onFirstUpdate(state);\n        }\n      }); // Modifiers have the ability to execute arbitrary code before the first\n      // update cycle runs. They will be executed in the same order as the update\n      // cycle. This is useful when a modifier adds some persistent data that\n      // other modifiers need to use, but the modifier is run after the dependent\n      // one.\n\n      function runModifierEffects() {\n        state.orderedModifiers.forEach(function (_ref) {\n          var name = _ref.name,\n              _ref$options = _ref.options,\n              options = _ref$options === void 0 ? {} : _ref$options,\n              effect = _ref.effect;\n\n          if (typeof effect === 'function') {\n            var cleanupFn = effect({\n              state: state,\n              name: name,\n              instance: instance,\n              options: options\n            });\n\n            var noopFn = function noopFn() {};\n\n            effectCleanupFns.push(cleanupFn || noopFn);\n          }\n        });\n      }\n\n      function cleanupModifierEffects() {\n        effectCleanupFns.forEach(function (fn) {\n          return fn();\n        });\n        effectCleanupFns = [];\n      }\n\n      return instance;\n    };\n  }\n\n  var passive = {\n    passive: true\n  };\n\n  function effect$2(_ref) {\n    var state = _ref.state,\n        instance = _ref.instance,\n        options = _ref.options;\n    var _options$scroll = options.scroll,\n        scroll = _options$scroll === void 0 ? true : _options$scroll,\n        _options$resize = options.resize,\n        resize = _options$resize === void 0 ? true : _options$resize;\n    var window = getWindow(state.elements.popper);\n    var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n    if (scroll) {\n      scrollParents.forEach(function (scrollParent) {\n        scrollParent.addEventListener('scroll', instance.update, passive);\n      });\n    }\n\n    if (resize) {\n      window.addEventListener('resize', instance.update, passive);\n    }\n\n    return function () {\n      if (scroll) {\n        scrollParents.forEach(function (scrollParent) {\n          scrollParent.removeEventListener('scroll', instance.update, passive);\n        });\n      }\n\n      if (resize) {\n        window.removeEventListener('resize', instance.update, passive);\n      }\n    };\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var eventListeners = {\n    name: 'eventListeners',\n    enabled: true,\n    phase: 'write',\n    fn: function fn() {},\n    effect: effect$2,\n    data: {}\n  };\n\n  function popperOffsets(_ref) {\n    var state = _ref.state,\n        name = _ref.name;\n    // Offsets are the actual position the popper needs to have to be\n    // properly positioned near its reference element\n    // This is the most basic placement, and will be adjusted by\n    // the modifiers in the next step\n    state.modifiersData[name] = computeOffsets({\n      reference: state.rects.reference,\n      element: state.rects.popper,\n      strategy: 'absolute',\n      placement: state.placement\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var popperOffsets$1 = {\n    name: 'popperOffsets',\n    enabled: true,\n    phase: 'read',\n    fn: popperOffsets,\n    data: {}\n  };\n\n  var unsetSides = {\n    top: 'auto',\n    right: 'auto',\n    bottom: 'auto',\n    left: 'auto'\n  }; // Round the offsets to the nearest suitable subpixel based on the DPR.\n  // Zooming can change the DPR, but it seems to report a value that will\n  // cleanly divide the values into the appropriate subpixels.\n\n  function roundOffsetsByDPR(_ref, win) {\n    var x = _ref.x,\n        y = _ref.y;\n    var dpr = win.devicePixelRatio || 1;\n    return {\n      x: round(x * dpr) / dpr || 0,\n      y: round(y * dpr) / dpr || 0\n    };\n  }\n\n  function mapToStyles(_ref2) {\n    var _Object$assign2;\n\n    var popper = _ref2.popper,\n        popperRect = _ref2.popperRect,\n        placement = _ref2.placement,\n        variation = _ref2.variation,\n        offsets = _ref2.offsets,\n        position = _ref2.position,\n        gpuAcceleration = _ref2.gpuAcceleration,\n        adaptive = _ref2.adaptive,\n        roundOffsets = _ref2.roundOffsets,\n        isFixed = _ref2.isFixed;\n    var _offsets$x = offsets.x,\n        x = _offsets$x === void 0 ? 0 : _offsets$x,\n        _offsets$y = offsets.y,\n        y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n    var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n      x: x,\n      y: y\n    }) : {\n      x: x,\n      y: y\n    };\n\n    x = _ref3.x;\n    y = _ref3.y;\n    var hasX = offsets.hasOwnProperty('x');\n    var hasY = offsets.hasOwnProperty('y');\n    var sideX = left;\n    var sideY = top;\n    var win = window;\n\n    if (adaptive) {\n      var offsetParent = getOffsetParent(popper);\n      var heightProp = 'clientHeight';\n      var widthProp = 'clientWidth';\n\n      if (offsetParent === getWindow(popper)) {\n        offsetParent = getDocumentElement(popper);\n\n        if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n          heightProp = 'scrollHeight';\n          widthProp = 'scrollWidth';\n        }\n      } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n      offsetParent = offsetParent;\n\n      if (placement === top || (placement === left || placement === right) && variation === end) {\n        sideY = bottom;\n        var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n        offsetParent[heightProp];\n        y -= offsetY - popperRect.height;\n        y *= gpuAcceleration ? 1 : -1;\n      }\n\n      if (placement === left || (placement === top || placement === bottom) && variation === end) {\n        sideX = right;\n        var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n        offsetParent[widthProp];\n        x -= offsetX - popperRect.width;\n        x *= gpuAcceleration ? 1 : -1;\n      }\n    }\n\n    var commonStyles = Object.assign({\n      position: position\n    }, adaptive && unsetSides);\n\n    var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n      x: x,\n      y: y\n    }, getWindow(popper)) : {\n      x: x,\n      y: y\n    };\n\n    x = _ref4.x;\n    y = _ref4.y;\n\n    if (gpuAcceleration) {\n      var _Object$assign;\n\n      return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n    }\n\n    return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n  }\n\n  function computeStyles(_ref5) {\n    var state = _ref5.state,\n        options = _ref5.options;\n    var _options$gpuAccelerat = options.gpuAcceleration,\n        gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n        _options$adaptive = options.adaptive,\n        adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n        _options$roundOffsets = options.roundOffsets,\n        roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n    var commonStyles = {\n      placement: getBasePlacement(state.placement),\n      variation: getVariation(state.placement),\n      popper: state.elements.popper,\n      popperRect: state.rects.popper,\n      gpuAcceleration: gpuAcceleration,\n      isFixed: state.options.strategy === 'fixed'\n    };\n\n    if (state.modifiersData.popperOffsets != null) {\n      state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n        offsets: state.modifiersData.popperOffsets,\n        position: state.options.strategy,\n        adaptive: adaptive,\n        roundOffsets: roundOffsets\n      })));\n    }\n\n    if (state.modifiersData.arrow != null) {\n      state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n        offsets: state.modifiersData.arrow,\n        position: 'absolute',\n        adaptive: false,\n        roundOffsets: roundOffsets\n      })));\n    }\n\n    state.attributes.popper = Object.assign({}, state.attributes.popper, {\n      'data-popper-placement': state.placement\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var computeStyles$1 = {\n    name: 'computeStyles',\n    enabled: true,\n    phase: 'beforeWrite',\n    fn: computeStyles,\n    data: {}\n  };\n\n  // and applies them to the HTMLElements such as popper and arrow\n\n  function applyStyles(_ref) {\n    var state = _ref.state;\n    Object.keys(state.elements).forEach(function (name) {\n      var style = state.styles[name] || {};\n      var attributes = state.attributes[name] || {};\n      var element = state.elements[name]; // arrow is optional + virtual elements\n\n      if (!isHTMLElement(element) || !getNodeName(element)) {\n        return;\n      } // Flow doesn't support to extend this property, but it's the most\n      // effective way to apply styles to an HTMLElement\n      // $FlowFixMe[cannot-write]\n\n\n      Object.assign(element.style, style);\n      Object.keys(attributes).forEach(function (name) {\n        var value = attributes[name];\n\n        if (value === false) {\n          element.removeAttribute(name);\n        } else {\n          element.setAttribute(name, value === true ? '' : value);\n        }\n      });\n    });\n  }\n\n  function effect$1(_ref2) {\n    var state = _ref2.state;\n    var initialStyles = {\n      popper: {\n        position: state.options.strategy,\n        left: '0',\n        top: '0',\n        margin: '0'\n      },\n      arrow: {\n        position: 'absolute'\n      },\n      reference: {}\n    };\n    Object.assign(state.elements.popper.style, initialStyles.popper);\n    state.styles = initialStyles;\n\n    if (state.elements.arrow) {\n      Object.assign(state.elements.arrow.style, initialStyles.arrow);\n    }\n\n    return function () {\n      Object.keys(state.elements).forEach(function (name) {\n        var element = state.elements[name];\n        var attributes = state.attributes[name] || {};\n        var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n        var style = styleProperties.reduce(function (style, property) {\n          style[property] = '';\n          return style;\n        }, {}); // arrow is optional + virtual elements\n\n        if (!isHTMLElement(element) || !getNodeName(element)) {\n          return;\n        }\n\n        Object.assign(element.style, style);\n        Object.keys(attributes).forEach(function (attribute) {\n          element.removeAttribute(attribute);\n        });\n      });\n    };\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var applyStyles$1 = {\n    name: 'applyStyles',\n    enabled: true,\n    phase: 'write',\n    fn: applyStyles,\n    effect: effect$1,\n    requires: ['computeStyles']\n  };\n\n  function distanceAndSkiddingToXY(placement, rects, offset) {\n    var basePlacement = getBasePlacement(placement);\n    var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n    var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n      placement: placement\n    })) : offset,\n        skidding = _ref[0],\n        distance = _ref[1];\n\n    skidding = skidding || 0;\n    distance = (distance || 0) * invertDistance;\n    return [left, right].indexOf(basePlacement) >= 0 ? {\n      x: distance,\n      y: skidding\n    } : {\n      x: skidding,\n      y: distance\n    };\n  }\n\n  function offset(_ref2) {\n    var state = _ref2.state,\n        options = _ref2.options,\n        name = _ref2.name;\n    var _options$offset = options.offset,\n        offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n    var data = placements.reduce(function (acc, placement) {\n      acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n      return acc;\n    }, {});\n    var _data$state$placement = data[state.placement],\n        x = _data$state$placement.x,\n        y = _data$state$placement.y;\n\n    if (state.modifiersData.popperOffsets != null) {\n      state.modifiersData.popperOffsets.x += x;\n      state.modifiersData.popperOffsets.y += y;\n    }\n\n    state.modifiersData[name] = data;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var offset$1 = {\n    name: 'offset',\n    enabled: true,\n    phase: 'main',\n    requires: ['popperOffsets'],\n    fn: offset\n  };\n\n  var hash$1 = {\n    left: 'right',\n    right: 'left',\n    bottom: 'top',\n    top: 'bottom'\n  };\n  function getOppositePlacement(placement) {\n    return placement.replace(/left|right|bottom|top/g, function (matched) {\n      return hash$1[matched];\n    });\n  }\n\n  var hash = {\n    start: 'end',\n    end: 'start'\n  };\n  function getOppositeVariationPlacement(placement) {\n    return placement.replace(/start|end/g, function (matched) {\n      return hash[matched];\n    });\n  }\n\n  function computeAutoPlacement(state, options) {\n    if (options === void 0) {\n      options = {};\n    }\n\n    var _options = options,\n        placement = _options.placement,\n        boundary = _options.boundary,\n        rootBoundary = _options.rootBoundary,\n        padding = _options.padding,\n        flipVariations = _options.flipVariations,\n        _options$allowedAutoP = _options.allowedAutoPlacements,\n        allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP;\n    var variation = getVariation(placement);\n    var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n      return getVariation(placement) === variation;\n    }) : basePlacements;\n    var allowedPlacements = placements$1.filter(function (placement) {\n      return allowedAutoPlacements.indexOf(placement) >= 0;\n    });\n\n    if (allowedPlacements.length === 0) {\n      allowedPlacements = placements$1;\n    } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n    var overflows = allowedPlacements.reduce(function (acc, placement) {\n      acc[placement] = detectOverflow(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        padding: padding\n      })[getBasePlacement(placement)];\n      return acc;\n    }, {});\n    return Object.keys(overflows).sort(function (a, b) {\n      return overflows[a] - overflows[b];\n    });\n  }\n\n  function getExpandedFallbackPlacements(placement) {\n    if (getBasePlacement(placement) === auto) {\n      return [];\n    }\n\n    var oppositePlacement = getOppositePlacement(placement);\n    return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n  }\n\n  function flip(_ref) {\n    var state = _ref.state,\n        options = _ref.options,\n        name = _ref.name;\n\n    if (state.modifiersData[name]._skip) {\n      return;\n    }\n\n    var _options$mainAxis = options.mainAxis,\n        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n        _options$altAxis = options.altAxis,\n        checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n        specifiedFallbackPlacements = options.fallbackPlacements,\n        padding = options.padding,\n        boundary = options.boundary,\n        rootBoundary = options.rootBoundary,\n        altBoundary = options.altBoundary,\n        _options$flipVariatio = options.flipVariations,\n        flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n        allowedAutoPlacements = options.allowedAutoPlacements;\n    var preferredPlacement = state.options.placement;\n    var basePlacement = getBasePlacement(preferredPlacement);\n    var isBasePlacement = basePlacement === preferredPlacement;\n    var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n    var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n      return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        padding: padding,\n        flipVariations: flipVariations,\n        allowedAutoPlacements: allowedAutoPlacements\n      }) : placement);\n    }, []);\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var checksMap = new Map();\n    var makeFallbackChecks = true;\n    var firstFittingPlacement = placements[0];\n\n    for (var i = 0; i < placements.length; i++) {\n      var placement = placements[i];\n\n      var _basePlacement = getBasePlacement(placement);\n\n      var isStartVariation = getVariation(placement) === start;\n      var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n      var len = isVertical ? 'width' : 'height';\n      var overflow = detectOverflow(state, {\n        placement: placement,\n        boundary: boundary,\n        rootBoundary: rootBoundary,\n        altBoundary: altBoundary,\n        padding: padding\n      });\n      var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n      if (referenceRect[len] > popperRect[len]) {\n        mainVariationSide = getOppositePlacement(mainVariationSide);\n      }\n\n      var altVariationSide = getOppositePlacement(mainVariationSide);\n      var checks = [];\n\n      if (checkMainAxis) {\n        checks.push(overflow[_basePlacement] <= 0);\n      }\n\n      if (checkAltAxis) {\n        checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n      }\n\n      if (checks.every(function (check) {\n        return check;\n      })) {\n        firstFittingPlacement = placement;\n        makeFallbackChecks = false;\n        break;\n      }\n\n      checksMap.set(placement, checks);\n    }\n\n    if (makeFallbackChecks) {\n      // `2` may be desired in some cases \u2013 research later\n      var numberOfChecks = flipVariations ? 3 : 1;\n\n      var _loop = function _loop(_i) {\n        var fittingPlacement = placements.find(function (placement) {\n          var checks = checksMap.get(placement);\n\n          if (checks) {\n            return checks.slice(0, _i).every(function (check) {\n              return check;\n            });\n          }\n        });\n\n        if (fittingPlacement) {\n          firstFittingPlacement = fittingPlacement;\n          return \"break\";\n        }\n      };\n\n      for (var _i = numberOfChecks; _i > 0; _i--) {\n        var _ret = _loop(_i);\n\n        if (_ret === \"break\") break;\n      }\n    }\n\n    if (state.placement !== firstFittingPlacement) {\n      state.modifiersData[name]._skip = true;\n      state.placement = firstFittingPlacement;\n      state.reset = true;\n    }\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var flip$1 = {\n    name: 'flip',\n    enabled: true,\n    phase: 'main',\n    fn: flip,\n    requiresIfExists: ['offset'],\n    data: {\n      _skip: false\n    }\n  };\n\n  function getAltAxis(axis) {\n    return axis === 'x' ? 'y' : 'x';\n  }\n\n  function within(min$1, value, max$1) {\n    return max(min$1, min(value, max$1));\n  }\n  function withinMaxClamp(min, value, max) {\n    var v = within(min, value, max);\n    return v > max ? max : v;\n  }\n\n  function preventOverflow(_ref) {\n    var state = _ref.state,\n        options = _ref.options,\n        name = _ref.name;\n    var _options$mainAxis = options.mainAxis,\n        checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n        _options$altAxis = options.altAxis,\n        checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n        boundary = options.boundary,\n        rootBoundary = options.rootBoundary,\n        altBoundary = options.altBoundary,\n        padding = options.padding,\n        _options$tether = options.tether,\n        tether = _options$tether === void 0 ? true : _options$tether,\n        _options$tetherOffset = options.tetherOffset,\n        tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n    var overflow = detectOverflow(state, {\n      boundary: boundary,\n      rootBoundary: rootBoundary,\n      padding: padding,\n      altBoundary: altBoundary\n    });\n    var basePlacement = getBasePlacement(state.placement);\n    var variation = getVariation(state.placement);\n    var isBasePlacement = !variation;\n    var mainAxis = getMainAxisFromPlacement(basePlacement);\n    var altAxis = getAltAxis(mainAxis);\n    var popperOffsets = state.modifiersData.popperOffsets;\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n      placement: state.placement\n    })) : tetherOffset;\n    var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n      mainAxis: tetherOffsetValue,\n      altAxis: tetherOffsetValue\n    } : Object.assign({\n      mainAxis: 0,\n      altAxis: 0\n    }, tetherOffsetValue);\n    var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n    var data = {\n      x: 0,\n      y: 0\n    };\n\n    if (!popperOffsets) {\n      return;\n    }\n\n    if (checkMainAxis) {\n      var _offsetModifierState$;\n\n      var mainSide = mainAxis === 'y' ? top : left;\n      var altSide = mainAxis === 'y' ? bottom : right;\n      var len = mainAxis === 'y' ? 'height' : 'width';\n      var offset = popperOffsets[mainAxis];\n      var min$1 = offset + overflow[mainSide];\n      var max$1 = offset - overflow[altSide];\n      var additive = tether ? -popperRect[len] / 2 : 0;\n      var minLen = variation === start ? referenceRect[len] : popperRect[len];\n      var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n      // outside the reference bounds\n\n      var arrowElement = state.elements.arrow;\n      var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n        width: 0,\n        height: 0\n      };\n      var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n      var arrowPaddingMin = arrowPaddingObject[mainSide];\n      var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n      // to include its full size in the calculation. If the reference is small\n      // and near the edge of a boundary, the popper can overflow even if the\n      // reference is not overflowing as well (e.g. virtual elements with no\n      // width or height)\n\n      var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n      var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n      var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n      var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n      var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n      var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n      var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n      var tetherMax = offset + maxOffset - offsetModifierValue;\n      var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1);\n      popperOffsets[mainAxis] = preventedOffset;\n      data[mainAxis] = preventedOffset - offset;\n    }\n\n    if (checkAltAxis) {\n      var _offsetModifierState$2;\n\n      var _mainSide = mainAxis === 'x' ? top : left;\n\n      var _altSide = mainAxis === 'x' ? bottom : right;\n\n      var _offset = popperOffsets[altAxis];\n\n      var _len = altAxis === 'y' ? 'height' : 'width';\n\n      var _min = _offset + overflow[_mainSide];\n\n      var _max = _offset - overflow[_altSide];\n\n      var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n      var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n      var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n      var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n      var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n      popperOffsets[altAxis] = _preventedOffset;\n      data[altAxis] = _preventedOffset - _offset;\n    }\n\n    state.modifiersData[name] = data;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var preventOverflow$1 = {\n    name: 'preventOverflow',\n    enabled: true,\n    phase: 'main',\n    fn: preventOverflow,\n    requiresIfExists: ['offset']\n  };\n\n  var toPaddingObject = function toPaddingObject(padding, state) {\n    padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n      placement: state.placement\n    })) : padding;\n    return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n  };\n\n  function arrow(_ref) {\n    var _state$modifiersData$;\n\n    var state = _ref.state,\n        name = _ref.name,\n        options = _ref.options;\n    var arrowElement = state.elements.arrow;\n    var popperOffsets = state.modifiersData.popperOffsets;\n    var basePlacement = getBasePlacement(state.placement);\n    var axis = getMainAxisFromPlacement(basePlacement);\n    var isVertical = [left, right].indexOf(basePlacement) >= 0;\n    var len = isVertical ? 'height' : 'width';\n\n    if (!arrowElement || !popperOffsets) {\n      return;\n    }\n\n    var paddingObject = toPaddingObject(options.padding, state);\n    var arrowRect = getLayoutRect(arrowElement);\n    var minProp = axis === 'y' ? top : left;\n    var maxProp = axis === 'y' ? bottom : right;\n    var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n    var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n    var arrowOffsetParent = getOffsetParent(arrowElement);\n    var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n    var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n    // outside of the popper bounds\n\n    var min = paddingObject[minProp];\n    var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n    var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n    var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n    var axisProp = axis;\n    state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n  }\n\n  function effect(_ref2) {\n    var state = _ref2.state,\n        options = _ref2.options;\n    var _options$element = options.element,\n        arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n    if (arrowElement == null) {\n      return;\n    } // CSS selector\n\n\n    if (typeof arrowElement === 'string') {\n      arrowElement = state.elements.popper.querySelector(arrowElement);\n\n      if (!arrowElement) {\n        return;\n      }\n    }\n\n    if (!contains(state.elements.popper, arrowElement)) {\n      return;\n    }\n\n    state.elements.arrow = arrowElement;\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var arrow$1 = {\n    name: 'arrow',\n    enabled: true,\n    phase: 'main',\n    fn: arrow,\n    effect: effect,\n    requires: ['popperOffsets'],\n    requiresIfExists: ['preventOverflow']\n  };\n\n  function getSideOffsets(overflow, rect, preventedOffsets) {\n    if (preventedOffsets === void 0) {\n      preventedOffsets = {\n        x: 0,\n        y: 0\n      };\n    }\n\n    return {\n      top: overflow.top - rect.height - preventedOffsets.y,\n      right: overflow.right - rect.width + preventedOffsets.x,\n      bottom: overflow.bottom - rect.height + preventedOffsets.y,\n      left: overflow.left - rect.width - preventedOffsets.x\n    };\n  }\n\n  function isAnySideFullyClipped(overflow) {\n    return [top, right, bottom, left].some(function (side) {\n      return overflow[side] >= 0;\n    });\n  }\n\n  function hide(_ref) {\n    var state = _ref.state,\n        name = _ref.name;\n    var referenceRect = state.rects.reference;\n    var popperRect = state.rects.popper;\n    var preventedOffsets = state.modifiersData.preventOverflow;\n    var referenceOverflow = detectOverflow(state, {\n      elementContext: 'reference'\n    });\n    var popperAltOverflow = detectOverflow(state, {\n      altBoundary: true\n    });\n    var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n    var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n    var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n    var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n    state.modifiersData[name] = {\n      referenceClippingOffsets: referenceClippingOffsets,\n      popperEscapeOffsets: popperEscapeOffsets,\n      isReferenceHidden: isReferenceHidden,\n      hasPopperEscaped: hasPopperEscaped\n    };\n    state.attributes.popper = Object.assign({}, state.attributes.popper, {\n      'data-popper-reference-hidden': isReferenceHidden,\n      'data-popper-escaped': hasPopperEscaped\n    });\n  } // eslint-disable-next-line import/no-unused-modules\n\n\n  var hide$1 = {\n    name: 'hide',\n    enabled: true,\n    phase: 'main',\n    requiresIfExists: ['preventOverflow'],\n    fn: hide\n  };\n\n  var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1];\n  var createPopper$1 = /*#__PURE__*/popperGenerator({\n    defaultModifiers: defaultModifiers$1\n  }); // eslint-disable-next-line import/no-unused-modules\n\n  var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1];\n  var createPopper = /*#__PURE__*/popperGenerator({\n    defaultModifiers: defaultModifiers\n  }); // eslint-disable-next-line import/no-unused-modules\n\n  exports.applyStyles = applyStyles$1;\n  exports.arrow = arrow$1;\n  exports.computeStyles = computeStyles$1;\n  exports.createPopper = createPopper;\n  exports.createPopperLite = createPopper$1;\n  exports.defaultModifiers = defaultModifiers;\n  exports.detectOverflow = detectOverflow;\n  exports.eventListeners = eventListeners;\n  exports.flip = flip$1;\n  exports.hide = hide$1;\n  exports.offset = offset$1;\n  exports.popperGenerator = popperGenerator;\n  exports.popperOffsets = popperOffsets$1;\n  exports.preventOverflow = preventOverflow$1;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n//# sourceMappingURL=popper.js.map\n", "/*!\n  * Bootstrap index.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n  typeof define === 'function' && define.amd ? define(['exports'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Index = {}));\n})(this, (function (exports) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/index.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  const MAX_UID = 1000000;\n  const MILLISECONDS_MULTIPLIER = 1000;\n  const TRANSITION_END = 'transitionend';\n\n  /**\n   * Properly escape IDs selectors to handle weird IDs\n   * @param {string} selector\n   * @returns {string}\n   */\n  const parseSelector = selector => {\n    if (selector && window.CSS && window.CSS.escape) {\n      // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n      selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n    }\n    return selector;\n  };\n\n  // Shout-out Angus Croll (https://goo.gl/pxwQGp)\n  const toType = object => {\n    if (object === null || object === undefined) {\n      return `${object}`;\n    }\n    return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n  };\n\n  /**\n   * Public Util API\n   */\n\n  const getUID = prefix => {\n    do {\n      prefix += Math.floor(Math.random() * MAX_UID);\n    } while (document.getElementById(prefix));\n    return prefix;\n  };\n  const getTransitionDurationFromElement = element => {\n    if (!element) {\n      return 0;\n    }\n\n    // Get transition-duration of the element\n    let {\n      transitionDuration,\n      transitionDelay\n    } = window.getComputedStyle(element);\n    const floatTransitionDuration = Number.parseFloat(transitionDuration);\n    const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n    // Return 0 if element or transition duration is not found\n    if (!floatTransitionDuration && !floatTransitionDelay) {\n      return 0;\n    }\n\n    // If multiple durations are defined, take the first\n    transitionDuration = transitionDuration.split(',')[0];\n    transitionDelay = transitionDelay.split(',')[0];\n    return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n  };\n  const triggerTransitionEnd = element => {\n    element.dispatchEvent(new Event(TRANSITION_END));\n  };\n  const isElement = object => {\n    if (!object || typeof object !== 'object') {\n      return false;\n    }\n    if (typeof object.jquery !== 'undefined') {\n      object = object[0];\n    }\n    return typeof object.nodeType !== 'undefined';\n  };\n  const getElement = object => {\n    // it's a jQuery object or a node element\n    if (isElement(object)) {\n      return object.jquery ? object[0] : object;\n    }\n    if (typeof object === 'string' && object.length > 0) {\n      return document.querySelector(parseSelector(object));\n    }\n    return null;\n  };\n  const isVisible = element => {\n    if (!isElement(element) || element.getClientRects().length === 0) {\n      return false;\n    }\n    const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n    // Handle `details` element as its content may falsie appear visible when it is closed\n    const closedDetails = element.closest('details:not([open])');\n    if (!closedDetails) {\n      return elementIsVisible;\n    }\n    if (closedDetails !== element) {\n      const summary = element.closest('summary');\n      if (summary && summary.parentNode !== closedDetails) {\n        return false;\n      }\n      if (summary === null) {\n        return false;\n      }\n    }\n    return elementIsVisible;\n  };\n  const isDisabled = element => {\n    if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n      return true;\n    }\n    if (element.classList.contains('disabled')) {\n      return true;\n    }\n    if (typeof element.disabled !== 'undefined') {\n      return element.disabled;\n    }\n    return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n  };\n  const findShadowRoot = element => {\n    if (!document.documentElement.attachShadow) {\n      return null;\n    }\n\n    // Can find the shadow root otherwise it'll return the document\n    if (typeof element.getRootNode === 'function') {\n      const root = element.getRootNode();\n      return root instanceof ShadowRoot ? root : null;\n    }\n    if (element instanceof ShadowRoot) {\n      return element;\n    }\n\n    // when we don't find a shadow root\n    if (!element.parentNode) {\n      return null;\n    }\n    return findShadowRoot(element.parentNode);\n  };\n  const noop = () => {};\n\n  /**\n   * Trick to restart an element's animation\n   *\n   * @param {HTMLElement} element\n   * @return void\n   *\n   * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n   */\n  const reflow = element => {\n    element.offsetHeight; // eslint-disable-line no-unused-expressions\n  };\n  const getjQuery = () => {\n    if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n      return window.jQuery;\n    }\n    return null;\n  };\n  const DOMContentLoadedCallbacks = [];\n  const onDOMContentLoaded = callback => {\n    if (document.readyState === 'loading') {\n      // add listener on the first call when the document is in loading state\n      if (!DOMContentLoadedCallbacks.length) {\n        document.addEventListener('DOMContentLoaded', () => {\n          for (const callback of DOMContentLoadedCallbacks) {\n            callback();\n          }\n        });\n      }\n      DOMContentLoadedCallbacks.push(callback);\n    } else {\n      callback();\n    }\n  };\n  const isRTL = () => document.documentElement.dir === 'rtl';\n  const defineJQueryPlugin = plugin => {\n    onDOMContentLoaded(() => {\n      const $ = getjQuery();\n      /* istanbul ignore if */\n      if ($) {\n        const name = plugin.NAME;\n        const JQUERY_NO_CONFLICT = $.fn[name];\n        $.fn[name] = plugin.jQueryInterface;\n        $.fn[name].Constructor = plugin;\n        $.fn[name].noConflict = () => {\n          $.fn[name] = JQUERY_NO_CONFLICT;\n          return plugin.jQueryInterface;\n        };\n      }\n    });\n  };\n  const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n    return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n  };\n  const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n    if (!waitForTransition) {\n      execute(callback);\n      return;\n    }\n    const durationPadding = 5;\n    const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n    let called = false;\n    const handler = ({\n      target\n    }) => {\n      if (target !== transitionElement) {\n        return;\n      }\n      called = true;\n      transitionElement.removeEventListener(TRANSITION_END, handler);\n      execute(callback);\n    };\n    transitionElement.addEventListener(TRANSITION_END, handler);\n    setTimeout(() => {\n      if (!called) {\n        triggerTransitionEnd(transitionElement);\n      }\n    }, emulatedDuration);\n  };\n\n  /**\n   * Return the previous/next element of a list.\n   *\n   * @param {array} list    The list of elements\n   * @param activeElement   The active element\n   * @param shouldGetNext   Choose to get next or previous element\n   * @param isCycleAllowed\n   * @return {Element|elem} The proper element\n   */\n  const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n    const listLength = list.length;\n    let index = list.indexOf(activeElement);\n\n    // if the element does not exist in the list return an element\n    // depending on the direction and if cycle is allowed\n    if (index === -1) {\n      return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n    }\n    index += shouldGetNext ? 1 : -1;\n    if (isCycleAllowed) {\n      index = (index + listLength) % listLength;\n    }\n    return list[Math.max(0, Math.min(index, listLength - 1))];\n  };\n\n  exports.defineJQueryPlugin = defineJQueryPlugin;\n  exports.execute = execute;\n  exports.executeAfterTransition = executeAfterTransition;\n  exports.findShadowRoot = findShadowRoot;\n  exports.getElement = getElement;\n  exports.getNextActiveElement = getNextActiveElement;\n  exports.getTransitionDurationFromElement = getTransitionDurationFromElement;\n  exports.getUID = getUID;\n  exports.getjQuery = getjQuery;\n  exports.isDisabled = isDisabled;\n  exports.isElement = isElement;\n  exports.isRTL = isRTL;\n  exports.isVisible = isVisible;\n  exports.noop = noop;\n  exports.onDOMContentLoaded = onDOMContentLoaded;\n  exports.parseSelector = parseSelector;\n  exports.reflow = reflow;\n  exports.toType = toType;\n  exports.triggerTransitionEnd = triggerTransitionEnd;\n\n  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\n}));\n//# sourceMappingURL=index.js.map\n", "/*!\n  * Bootstrap data.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Data = factory());\n})(this, (function () { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap dom/data.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  /**\n   * Constants\n   */\n\n  const elementMap = new Map();\n  const data = {\n    set(element, key, instance) {\n      if (!elementMap.has(element)) {\n        elementMap.set(element, new Map());\n      }\n      const instanceMap = elementMap.get(element);\n\n      // make it clear we only want one instance per element\n      // can be removed later when multiple key/instances are fine to be used\n      if (!instanceMap.has(key) && instanceMap.size !== 0) {\n        // eslint-disable-next-line no-console\n        console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n        return;\n      }\n      instanceMap.set(key, instance);\n    },\n    get(element, key) {\n      if (elementMap.has(element)) {\n        return elementMap.get(element).get(key) || null;\n      }\n      return null;\n    },\n    remove(element, key) {\n      if (!elementMap.has(element)) {\n        return;\n      }\n      const instanceMap = elementMap.get(element);\n      instanceMap.delete(key);\n\n      // free up element references if there are no instances left for an element\n      if (instanceMap.size === 0) {\n        elementMap.delete(element);\n      }\n    }\n  };\n\n  return data;\n\n}));\n//# sourceMappingURL=data.js.map\n", "/*!\n  * Bootstrap event-handler.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['../util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory(global.Index));\n})(this, (function (index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap dom/event-handler.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\n  const stripNameRegex = /\\..*/;\n  const stripUidRegex = /::\\d+$/;\n  const eventRegistry = {}; // Events storage\n  let uidEvent = 1;\n  const customEvents = {\n    mouseenter: 'mouseover',\n    mouseleave: 'mouseout'\n  };\n  const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n  /**\n   * Private methods\n   */\n\n  function makeEventUid(element, uid) {\n    return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n  }\n  function getElementEvents(element) {\n    const uid = makeEventUid(element);\n    element.uidEvent = uid;\n    eventRegistry[uid] = eventRegistry[uid] || {};\n    return eventRegistry[uid];\n  }\n  function bootstrapHandler(element, fn) {\n    return function handler(event) {\n      hydrateObj(event, {\n        delegateTarget: element\n      });\n      if (handler.oneOff) {\n        EventHandler.off(element, event.type, fn);\n      }\n      return fn.apply(element, [event]);\n    };\n  }\n  function bootstrapDelegationHandler(element, selector, fn) {\n    return function handler(event) {\n      const domElements = element.querySelectorAll(selector);\n      for (let {\n        target\n      } = event; target && target !== this; target = target.parentNode) {\n        for (const domElement of domElements) {\n          if (domElement !== target) {\n            continue;\n          }\n          hydrateObj(event, {\n            delegateTarget: target\n          });\n          if (handler.oneOff) {\n            EventHandler.off(element, event.type, selector, fn);\n          }\n          return fn.apply(target, [event]);\n        }\n      }\n    };\n  }\n  function findHandler(events, callable, delegationSelector = null) {\n    return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n  }\n  function normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n    const isDelegated = typeof handler === 'string';\n    // TODO: tooltip passes `false` instead of selector, so we need to check\n    const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n    let typeEvent = getTypeEvent(originalTypeEvent);\n    if (!nativeEvents.has(typeEvent)) {\n      typeEvent = originalTypeEvent;\n    }\n    return [isDelegated, callable, typeEvent];\n  }\n  function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n    if (typeof originalTypeEvent !== 'string' || !element) {\n      return;\n    }\n    let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n    // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n    // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n    if (originalTypeEvent in customEvents) {\n      const wrapFunction = fn => {\n        return function (event) {\n          if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n            return fn.call(this, event);\n          }\n        };\n      };\n      callable = wrapFunction(callable);\n    }\n    const events = getElementEvents(element);\n    const handlers = events[typeEvent] || (events[typeEvent] = {});\n    const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n    if (previousFunction) {\n      previousFunction.oneOff = previousFunction.oneOff && oneOff;\n      return;\n    }\n    const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n    const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n    fn.delegationSelector = isDelegated ? handler : null;\n    fn.callable = callable;\n    fn.oneOff = oneOff;\n    fn.uidEvent = uid;\n    handlers[uid] = fn;\n    element.addEventListener(typeEvent, fn, isDelegated);\n  }\n  function removeHandler(element, events, typeEvent, handler, delegationSelector) {\n    const fn = findHandler(events[typeEvent], handler, delegationSelector);\n    if (!fn) {\n      return;\n    }\n    element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n    delete events[typeEvent][fn.uidEvent];\n  }\n  function removeNamespacedHandlers(element, events, typeEvent, namespace) {\n    const storeElementEvent = events[typeEvent] || {};\n    for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n      if (handlerKey.includes(namespace)) {\n        removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n      }\n    }\n  }\n  function getTypeEvent(event) {\n    // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n    event = event.replace(stripNameRegex, '');\n    return customEvents[event] || event;\n  }\n  const EventHandler = {\n    on(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, false);\n    },\n    one(element, event, handler, delegationFunction) {\n      addHandler(element, event, handler, delegationFunction, true);\n    },\n    off(element, originalTypeEvent, handler, delegationFunction) {\n      if (typeof originalTypeEvent !== 'string' || !element) {\n        return;\n      }\n      const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n      const inNamespace = typeEvent !== originalTypeEvent;\n      const events = getElementEvents(element);\n      const storeElementEvent = events[typeEvent] || {};\n      const isNamespace = originalTypeEvent.startsWith('.');\n      if (typeof callable !== 'undefined') {\n        // Simplest case: handler is passed, remove that listener ONLY.\n        if (!Object.keys(storeElementEvent).length) {\n          return;\n        }\n        removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n        return;\n      }\n      if (isNamespace) {\n        for (const elementEvent of Object.keys(events)) {\n          removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n        }\n      }\n      for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n        const handlerKey = keyHandlers.replace(stripUidRegex, '');\n        if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n          removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n        }\n      }\n    },\n    trigger(element, event, args) {\n      if (typeof event !== 'string' || !element) {\n        return null;\n      }\n      const $ = index_js.getjQuery();\n      const typeEvent = getTypeEvent(event);\n      const inNamespace = event !== typeEvent;\n      let jQueryEvent = null;\n      let bubbles = true;\n      let nativeDispatch = true;\n      let defaultPrevented = false;\n      if (inNamespace && $) {\n        jQueryEvent = $.Event(event, args);\n        $(element).trigger(jQueryEvent);\n        bubbles = !jQueryEvent.isPropagationStopped();\n        nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n        defaultPrevented = jQueryEvent.isDefaultPrevented();\n      }\n      const evt = hydrateObj(new Event(event, {\n        bubbles,\n        cancelable: true\n      }), args);\n      if (defaultPrevented) {\n        evt.preventDefault();\n      }\n      if (nativeDispatch) {\n        element.dispatchEvent(evt);\n      }\n      if (evt.defaultPrevented && jQueryEvent) {\n        jQueryEvent.preventDefault();\n      }\n      return evt;\n    }\n  };\n  function hydrateObj(obj, meta = {}) {\n    for (const [key, value] of Object.entries(meta)) {\n      try {\n        obj[key] = value;\n      } catch (_unused) {\n        Object.defineProperty(obj, key, {\n          configurable: true,\n          get() {\n            return value;\n          }\n        });\n      }\n    }\n    return obj;\n  }\n\n  return EventHandler;\n\n}));\n//# sourceMappingURL=event-handler.js.map\n", "/*!\n  * Bootstrap manipulator.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n  typeof define === 'function' && define.amd ? define(factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Manipulator = factory());\n})(this, (function () { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap dom/manipulator.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  function normalizeData(value) {\n    if (value === 'true') {\n      return true;\n    }\n    if (value === 'false') {\n      return false;\n    }\n    if (value === Number(value).toString()) {\n      return Number(value);\n    }\n    if (value === '' || value === 'null') {\n      return null;\n    }\n    if (typeof value !== 'string') {\n      return value;\n    }\n    try {\n      return JSON.parse(decodeURIComponent(value));\n    } catch (_unused) {\n      return value;\n    }\n  }\n  function normalizeDataKey(key) {\n    return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n  }\n  const Manipulator = {\n    setDataAttribute(element, key, value) {\n      element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n    },\n    removeDataAttribute(element, key) {\n      element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n    },\n    getDataAttributes(element) {\n      if (!element) {\n        return {};\n      }\n      const attributes = {};\n      const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n      for (const key of bsKeys) {\n        let pureKey = key.replace(/^bs/, '');\n        pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n        attributes[pureKey] = normalizeData(element.dataset[key]);\n      }\n      return attributes;\n    },\n    getDataAttribute(element, key) {\n      return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n    }\n  };\n\n  return Manipulator;\n\n}));\n//# sourceMappingURL=manipulator.js.map\n", "/*!\n  * Bootstrap selector-engine.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['../util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SelectorEngine = factory(global.Index));\n})(this, (function (index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap dom/selector-engine.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  const getSelector = element => {\n    let selector = element.getAttribute('data-bs-target');\n    if (!selector || selector === '#') {\n      let hrefAttribute = element.getAttribute('href');\n\n      // The only valid content that could double as a selector are IDs or classes,\n      // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n      // `document.querySelector` will rightfully complain it is invalid.\n      // See https://github.com/twbs/bootstrap/issues/32273\n      if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n        return null;\n      }\n\n      // Just in case some CMS puts out a full URL with the anchor appended\n      if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n        hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n      }\n      selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n    }\n    return selector ? selector.split(',').map(sel => index_js.parseSelector(sel)).join(',') : null;\n  };\n  const SelectorEngine = {\n    find(selector, element = document.documentElement) {\n      return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n    },\n    findOne(selector, element = document.documentElement) {\n      return Element.prototype.querySelector.call(element, selector);\n    },\n    children(element, selector) {\n      return [].concat(...element.children).filter(child => child.matches(selector));\n    },\n    parents(element, selector) {\n      const parents = [];\n      let ancestor = element.parentNode.closest(selector);\n      while (ancestor) {\n        parents.push(ancestor);\n        ancestor = ancestor.parentNode.closest(selector);\n      }\n      return parents;\n    },\n    prev(element, selector) {\n      let previous = element.previousElementSibling;\n      while (previous) {\n        if (previous.matches(selector)) {\n          return [previous];\n        }\n        previous = previous.previousElementSibling;\n      }\n      return [];\n    },\n    // TODO: this is now unused; remove later along with prev()\n    next(element, selector) {\n      let next = element.nextElementSibling;\n      while (next) {\n        if (next.matches(selector)) {\n          return [next];\n        }\n        next = next.nextElementSibling;\n      }\n      return [];\n    },\n    focusableChildren(element) {\n      const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n      return this.find(focusables, element).filter(el => !index_js.isDisabled(el) && index_js.isVisible(el));\n    },\n    getSelectorFromElement(element) {\n      const selector = getSelector(element);\n      if (selector) {\n        return SelectorEngine.findOne(selector) ? selector : null;\n      }\n      return null;\n    },\n    getElementFromSelector(element) {\n      const selector = getSelector(element);\n      return selector ? SelectorEngine.findOne(selector) : null;\n    },\n    getMultipleElementsFromSelector(element) {\n      const selector = getSelector(element);\n      return selector ? SelectorEngine.find(selector) : [];\n    }\n  };\n\n  return SelectorEngine;\n\n}));\n//# sourceMappingURL=selector-engine.js.map\n", "/*!\n  * Bootstrap config.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/manipulator.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/manipulator', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Config = factory(global.Manipulator, global.Index));\n})(this, (function (Manipulator, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/config.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Class definition\n   */\n\n  class Config {\n    // Getters\n    static get Default() {\n      return {};\n    }\n    static get DefaultType() {\n      return {};\n    }\n    static get NAME() {\n      throw new Error('You have to implement the static method \"NAME\", for each component!');\n    }\n    _getConfig(config) {\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n      this._typeCheckConfig(config);\n      return config;\n    }\n    _configAfterMerge(config) {\n      return config;\n    }\n    _mergeConfigObj(config, element) {\n      const jsonConfig = index_js.isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n      return {\n        ...this.constructor.Default,\n        ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n        ...(index_js.isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n        ...(typeof config === 'object' ? config : {})\n      };\n    }\n    _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n      for (const [property, expectedTypes] of Object.entries(configTypes)) {\n        const value = config[property];\n        const valueType = index_js.isElement(value) ? 'element' : index_js.toType(value);\n        if (!new RegExp(expectedTypes).test(valueType)) {\n          throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n        }\n      }\n    }\n  }\n\n  return Config;\n\n}));\n//# sourceMappingURL=config.js.map\n", "/*!\n  * Bootstrap component-functions.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('../dom/event-handler.js'), require('../dom/selector-engine.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['exports', '../dom/event-handler', '../dom/selector-engine', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComponentFunctions = {}, global.EventHandler, global.SelectorEngine, global.Index));\n})(this, (function (exports, EventHandler, SelectorEngine, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/component-functions.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  const enableDismissTrigger = (component, method = 'hide') => {\n    const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n    const name = component.NAME;\n    EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n      if (['A', 'AREA'].includes(this.tagName)) {\n        event.preventDefault();\n      }\n      if (index_js.isDisabled(this)) {\n        return;\n      }\n      const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n      const instance = component.getOrCreateInstance(target);\n\n      // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n      instance[method]();\n    });\n  };\n\n  exports.enableDismissTrigger = enableDismissTrigger;\n\n  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\n}));\n//# sourceMappingURL=component-functions.js.map\n", "/*!\n  * Bootstrap backdrop.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('./config.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/event-handler', './config', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Backdrop = factory(global.EventHandler, global.Config, global.Index));\n})(this, (function (EventHandler, Config, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/backdrop.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'backdrop';\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_SHOW = 'show';\n  const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`;\n  const Default = {\n    className: 'modal-backdrop',\n    clickCallback: null,\n    isAnimated: false,\n    isVisible: true,\n    // if false, we use the backdrop helper without adding any element to the dom\n    rootElement: 'body' // give the choice to place backdrop under different elements\n  };\n  const DefaultType = {\n    className: 'string',\n    clickCallback: '(function|null)',\n    isAnimated: 'boolean',\n    isVisible: 'boolean',\n    rootElement: '(element|string)'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Backdrop extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isAppended = false;\n      this._element = null;\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    show(callback) {\n      if (!this._config.isVisible) {\n        index_js.execute(callback);\n        return;\n      }\n      this._append();\n      const element = this._getElement();\n      if (this._config.isAnimated) {\n        index_js.reflow(element);\n      }\n      element.classList.add(CLASS_NAME_SHOW);\n      this._emulateAnimation(() => {\n        index_js.execute(callback);\n      });\n    }\n    hide(callback) {\n      if (!this._config.isVisible) {\n        index_js.execute(callback);\n        return;\n      }\n      this._getElement().classList.remove(CLASS_NAME_SHOW);\n      this._emulateAnimation(() => {\n        this.dispose();\n        index_js.execute(callback);\n      });\n    }\n    dispose() {\n      if (!this._isAppended) {\n        return;\n      }\n      EventHandler.off(this._element, EVENT_MOUSEDOWN);\n      this._element.remove();\n      this._isAppended = false;\n    }\n\n    // Private\n    _getElement() {\n      if (!this._element) {\n        const backdrop = document.createElement('div');\n        backdrop.className = this._config.className;\n        if (this._config.isAnimated) {\n          backdrop.classList.add(CLASS_NAME_FADE);\n        }\n        this._element = backdrop;\n      }\n      return this._element;\n    }\n    _configAfterMerge(config) {\n      // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n      config.rootElement = index_js.getElement(config.rootElement);\n      return config;\n    }\n    _append() {\n      if (this._isAppended) {\n        return;\n      }\n      const element = this._getElement();\n      this._config.rootElement.append(element);\n      EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n        index_js.execute(this._config.clickCallback);\n      });\n      this._isAppended = true;\n    }\n    _emulateAnimation(callback) {\n      index_js.executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n    }\n  }\n\n  return Backdrop;\n\n}));\n//# sourceMappingURL=backdrop.js.map\n", "/*!\n  * Bootstrap focustrap.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('../dom/selector-engine.js'), require('./config.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/event-handler', '../dom/selector-engine', './config'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Focustrap = factory(global.EventHandler, global.SelectorEngine, global.Config));\n})(this, (function (EventHandler, SelectorEngine, Config) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/focustrap.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'focustrap';\n  const DATA_KEY = 'bs.focustrap';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;\n  const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`;\n  const TAB_KEY = 'Tab';\n  const TAB_NAV_FORWARD = 'forward';\n  const TAB_NAV_BACKWARD = 'backward';\n  const Default = {\n    autofocus: true,\n    trapElement: null // The element to trap focus inside of\n  };\n  const DefaultType = {\n    autofocus: 'boolean',\n    trapElement: 'element'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class FocusTrap extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n      this._isActive = false;\n      this._lastTabNavDirection = null;\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    activate() {\n      if (this._isActive) {\n        return;\n      }\n      if (this._config.autofocus) {\n        this._config.trapElement.focus();\n      }\n      EventHandler.off(document, EVENT_KEY); // guard against infinite focus loop\n      EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));\n      EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n      this._isActive = true;\n    }\n    deactivate() {\n      if (!this._isActive) {\n        return;\n      }\n      this._isActive = false;\n      EventHandler.off(document, EVENT_KEY);\n    }\n\n    // Private\n    _handleFocusin(event) {\n      const {\n        trapElement\n      } = this._config;\n      if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n        return;\n      }\n      const elements = SelectorEngine.focusableChildren(trapElement);\n      if (elements.length === 0) {\n        trapElement.focus();\n      } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n        elements[elements.length - 1].focus();\n      } else {\n        elements[0].focus();\n      }\n    }\n    _handleKeydown(event) {\n      if (event.key !== TAB_KEY) {\n        return;\n      }\n      this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n    }\n  }\n\n  return FocusTrap;\n\n}));\n//# sourceMappingURL=focustrap.js.map\n", "/*!\n  * Bootstrap sanitizer.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n  typeof define === 'function' && define.amd ? define(['exports'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Sanitizer = {}));\n})(this, (function (exports) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/sanitizer.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n  // js-docs-start allow-list\n  const ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\n  const DefaultAllowlist = {\n    // Global attributes allowed on any supplied element below.\n    '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n    a: ['target', 'href', 'title', 'rel'],\n    area: [],\n    b: [],\n    br: [],\n    col: [],\n    code: [],\n    dd: [],\n    div: [],\n    dl: [],\n    dt: [],\n    em: [],\n    hr: [],\n    h1: [],\n    h2: [],\n    h3: [],\n    h4: [],\n    h5: [],\n    h6: [],\n    i: [],\n    img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n    li: [],\n    ol: [],\n    p: [],\n    pre: [],\n    s: [],\n    small: [],\n    span: [],\n    sub: [],\n    sup: [],\n    strong: [],\n    u: [],\n    ul: []\n  };\n  // js-docs-end allow-list\n\n  const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n  /**\n   * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n   * contexts.\n   *\n   * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n   */\n  // eslint-disable-next-line unicorn/better-regex\n  const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\n  const allowedAttribute = (attribute, allowedAttributeList) => {\n    const attributeName = attribute.nodeName.toLowerCase();\n    if (allowedAttributeList.includes(attributeName)) {\n      if (uriAttributes.has(attributeName)) {\n        return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n      }\n      return true;\n    }\n\n    // Check if a regular expression validates the attribute.\n    return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n  };\n  function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n    if (!unsafeHtml.length) {\n      return unsafeHtml;\n    }\n    if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n      return sanitizeFunction(unsafeHtml);\n    }\n    const domParser = new window.DOMParser();\n    const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n    const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n    for (const element of elements) {\n      const elementName = element.nodeName.toLowerCase();\n      if (!Object.keys(allowList).includes(elementName)) {\n        element.remove();\n        continue;\n      }\n      const attributeList = [].concat(...element.attributes);\n      const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n      for (const attribute of attributeList) {\n        if (!allowedAttribute(attribute, allowedAttributes)) {\n          element.removeAttribute(attribute.nodeName);\n        }\n      }\n    }\n    return createdDocument.body.innerHTML;\n  }\n\n  exports.DefaultAllowlist = DefaultAllowlist;\n  exports.sanitizeHtml = sanitizeHtml;\n\n  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\n}));\n//# sourceMappingURL=sanitizer.js.map\n", "/*!\n  * Bootstrap scrollbar.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/manipulator.js'), require('../dom/selector-engine.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/manipulator', '../dom/selector-engine', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Scrollbar = factory(global.Manipulator, global.SelectorEngine, global.Index));\n})(this, (function (Manipulator, SelectorEngine, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/scrollBar.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\n  const SELECTOR_STICKY_CONTENT = '.sticky-top';\n  const PROPERTY_PADDING = 'padding-right';\n  const PROPERTY_MARGIN = 'margin-right';\n\n  /**\n   * Class definition\n   */\n\n  class ScrollBarHelper {\n    constructor() {\n      this._element = document.body;\n    }\n\n    // Public\n    getWidth() {\n      // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n      const documentWidth = document.documentElement.clientWidth;\n      return Math.abs(window.innerWidth - documentWidth);\n    }\n    hide() {\n      const width = this.getWidth();\n      this._disableOverFlow();\n      // give padding to element to balance the hidden scrollbar width\n      this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n      // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n      this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n      this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n    }\n    reset() {\n      this._resetElementAttributes(this._element, 'overflow');\n      this._resetElementAttributes(this._element, PROPERTY_PADDING);\n      this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n      this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n    }\n    isOverflowing() {\n      return this.getWidth() > 0;\n    }\n\n    // Private\n    _disableOverFlow() {\n      this._saveInitialAttribute(this._element, 'overflow');\n      this._element.style.overflow = 'hidden';\n    }\n    _setElementAttributes(selector, styleProperty, callback) {\n      const scrollbarWidth = this.getWidth();\n      const manipulationCallBack = element => {\n        if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n          return;\n        }\n        this._saveInitialAttribute(element, styleProperty);\n        const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n        element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n      };\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n    _saveInitialAttribute(element, styleProperty) {\n      const actualValue = element.style.getPropertyValue(styleProperty);\n      if (actualValue) {\n        Manipulator.setDataAttribute(element, styleProperty, actualValue);\n      }\n    }\n    _resetElementAttributes(selector, styleProperty) {\n      const manipulationCallBack = element => {\n        const value = Manipulator.getDataAttribute(element, styleProperty);\n        // We only want to remove the property if the value is `null`; the value can also be zero\n        if (value === null) {\n          element.style.removeProperty(styleProperty);\n          return;\n        }\n        Manipulator.removeDataAttribute(element, styleProperty);\n        element.style.setProperty(styleProperty, value);\n      };\n      this._applyManipulationCallback(selector, manipulationCallBack);\n    }\n    _applyManipulationCallback(selector, callBack) {\n      if (index_js.isElement(selector)) {\n        callBack(selector);\n        return;\n      }\n      for (const sel of SelectorEngine.find(selector, this._element)) {\n        callBack(sel);\n      }\n    }\n  }\n\n  return ScrollBarHelper;\n\n}));\n//# sourceMappingURL=scrollbar.js.map\n", "/*!\n  * Bootstrap swipe.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('./config.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/event-handler', './config', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Swipe = factory(global.EventHandler, global.Config, global.Index));\n})(this, (function (EventHandler, Config, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/swipe.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'swipe';\n  const EVENT_KEY = '.bs.swipe';\n  const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`;\n  const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`;\n  const EVENT_TOUCHEND = `touchend${EVENT_KEY}`;\n  const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`;\n  const EVENT_POINTERUP = `pointerup${EVENT_KEY}`;\n  const POINTER_TYPE_TOUCH = 'touch';\n  const POINTER_TYPE_PEN = 'pen';\n  const CLASS_NAME_POINTER_EVENT = 'pointer-event';\n  const SWIPE_THRESHOLD = 40;\n  const Default = {\n    endCallback: null,\n    leftCallback: null,\n    rightCallback: null\n  };\n  const DefaultType = {\n    endCallback: '(function|null)',\n    leftCallback: '(function|null)',\n    rightCallback: '(function|null)'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Swipe extends Config {\n    constructor(element, config) {\n      super();\n      this._element = element;\n      if (!element || !Swipe.isSupported()) {\n        return;\n      }\n      this._config = this._getConfig(config);\n      this._deltaX = 0;\n      this._supportPointerEvents = Boolean(window.PointerEvent);\n      this._initEvents();\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    dispose() {\n      EventHandler.off(this._element, EVENT_KEY);\n    }\n\n    // Private\n    _start(event) {\n      if (!this._supportPointerEvents) {\n        this._deltaX = event.touches[0].clientX;\n        return;\n      }\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX;\n      }\n    }\n    _end(event) {\n      if (this._eventIsPointerPenTouch(event)) {\n        this._deltaX = event.clientX - this._deltaX;\n      }\n      this._handleSwipe();\n      index_js.execute(this._config.endCallback);\n    }\n    _move(event) {\n      this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n    }\n    _handleSwipe() {\n      const absDeltaX = Math.abs(this._deltaX);\n      if (absDeltaX <= SWIPE_THRESHOLD) {\n        return;\n      }\n      const direction = absDeltaX / this._deltaX;\n      this._deltaX = 0;\n      if (!direction) {\n        return;\n      }\n      index_js.execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n    }\n    _initEvents() {\n      if (this._supportPointerEvents) {\n        EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n        EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n        this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n      } else {\n        EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n        EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n        EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n      }\n    }\n    _eventIsPointerPenTouch(event) {\n      return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n    }\n\n    // Static\n    static isSupported() {\n      return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n    }\n  }\n\n  return Swipe;\n\n}));\n//# sourceMappingURL=swipe.js.map\n", "/*!\n  * Bootstrap template-factory.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/selector-engine.js'), require('./config.js'), require('./sanitizer.js'), require('./index.js')) :\n  typeof define === 'function' && define.amd ? define(['../dom/selector-engine', './config', './sanitizer', './index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.TemplateFactory = factory(global.SelectorEngine, global.Config, global.Sanitizer, global.Index));\n})(this, (function (SelectorEngine, Config, sanitizer_js, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap util/template-factory.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'TemplateFactory';\n  const Default = {\n    allowList: sanitizer_js.DefaultAllowlist,\n    content: {},\n    // { selector : text ,  selector2 : text2 , }\n    extraClass: '',\n    html: false,\n    sanitize: true,\n    sanitizeFn: null,\n    template: '<div></div>'\n  };\n  const DefaultType = {\n    allowList: 'object',\n    content: 'object',\n    extraClass: '(string|function)',\n    html: 'boolean',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    template: 'string'\n  };\n  const DefaultContentType = {\n    entry: '(string|element|function|null)',\n    selector: '(string|element)'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class TemplateFactory extends Config {\n    constructor(config) {\n      super();\n      this._config = this._getConfig(config);\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    getContent() {\n      return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n    }\n    hasContent() {\n      return this.getContent().length > 0;\n    }\n    changeContent(content) {\n      this._checkContent(content);\n      this._config.content = {\n        ...this._config.content,\n        ...content\n      };\n      return this;\n    }\n    toHtml() {\n      const templateWrapper = document.createElement('div');\n      templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n      for (const [selector, text] of Object.entries(this._config.content)) {\n        this._setContent(templateWrapper, text, selector);\n      }\n      const template = templateWrapper.children[0];\n      const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n      if (extraClass) {\n        template.classList.add(...extraClass.split(' '));\n      }\n      return template;\n    }\n\n    // Private\n    _typeCheckConfig(config) {\n      super._typeCheckConfig(config);\n      this._checkContent(config.content);\n    }\n    _checkContent(arg) {\n      for (const [selector, content] of Object.entries(arg)) {\n        super._typeCheckConfig({\n          selector,\n          entry: content\n        }, DefaultContentType);\n      }\n    }\n    _setContent(template, content, selector) {\n      const templateElement = SelectorEngine.findOne(selector, template);\n      if (!templateElement) {\n        return;\n      }\n      content = this._resolvePossibleFunction(content);\n      if (!content) {\n        templateElement.remove();\n        return;\n      }\n      if (index_js.isElement(content)) {\n        this._putElementInTemplate(index_js.getElement(content), templateElement);\n        return;\n      }\n      if (this._config.html) {\n        templateElement.innerHTML = this._maybeSanitize(content);\n        return;\n      }\n      templateElement.textContent = content;\n    }\n    _maybeSanitize(arg) {\n      return this._config.sanitize ? sanitizer_js.sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n    }\n    _resolvePossibleFunction(arg) {\n      return index_js.execute(arg, [this]);\n    }\n    _putElementInTemplate(element, templateElement) {\n      if (this._config.html) {\n        templateElement.innerHTML = '';\n        templateElement.append(element);\n        return;\n      }\n      templateElement.textContent = element.textContent;\n    }\n  }\n\n  return TemplateFactory;\n\n}));\n//# sourceMappingURL=template-factory.js.map\n", "/*!\n  * Bootstrap base-component.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./util/config.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './util/config', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BaseComponent = factory(global.Data, global.EventHandler, global.Config, global.Index));\n})(this, (function (Data, EventHandler, Config, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap base-component.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const VERSION = '5.3.3';\n\n  /**\n   * Class definition\n   */\n\n  class BaseComponent extends Config {\n    constructor(element, config) {\n      super();\n      element = index_js.getElement(element);\n      if (!element) {\n        return;\n      }\n      this._element = element;\n      this._config = this._getConfig(config);\n      Data.set(this._element, this.constructor.DATA_KEY, this);\n    }\n\n    // Public\n    dispose() {\n      Data.remove(this._element, this.constructor.DATA_KEY);\n      EventHandler.off(this._element, this.constructor.EVENT_KEY);\n      for (const propertyName of Object.getOwnPropertyNames(this)) {\n        this[propertyName] = null;\n      }\n    }\n    _queueCallback(callback, element, isAnimated = true) {\n      index_js.executeAfterTransition(callback, element, isAnimated);\n    }\n    _getConfig(config) {\n      config = this._mergeConfigObj(config, this._element);\n      config = this._configAfterMerge(config);\n      this._typeCheckConfig(config);\n      return config;\n    }\n\n    // Static\n    static getInstance(element) {\n      return Data.get(index_js.getElement(element), this.DATA_KEY);\n    }\n    static getOrCreateInstance(element, config = {}) {\n      return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n    }\n    static get VERSION() {\n      return VERSION;\n    }\n    static get DATA_KEY() {\n      return `bs.${this.NAME}`;\n    }\n    static get EVENT_KEY() {\n      return `.${this.DATA_KEY}`;\n    }\n    static eventName(name) {\n      return `${name}${this.EVENT_KEY}`;\n    }\n  }\n\n  return BaseComponent;\n\n}));\n//# sourceMappingURL=base-component.js.map\n", "/*!\n  * Bootstrap alert.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alert = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions, global.Index));\n})(this, (function (BaseComponent, EventHandler, componentFunctions_js, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap alert.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'alert';\n  const DATA_KEY = 'bs.alert';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_CLOSE = `close${EVENT_KEY}`;\n  const EVENT_CLOSED = `closed${EVENT_KEY}`;\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_SHOW = 'show';\n\n  /**\n   * Class definition\n   */\n\n  class Alert extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    close() {\n      const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n      if (closeEvent.defaultPrevented) {\n        return;\n      }\n      this._element.classList.remove(CLASS_NAME_SHOW);\n      const isAnimated = this._element.classList.contains(CLASS_NAME_FADE);\n      this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n    }\n\n    // Private\n    _destroyElement() {\n      this._element.remove();\n      EventHandler.trigger(this._element, EVENT_CLOSED);\n      this.dispose();\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Alert.getOrCreateInstance(this);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config](this);\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  componentFunctions_js.enableDismissTrigger(Alert, 'close');\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Alert);\n\n  return Alert;\n\n}));\n//# sourceMappingURL=alert.js.map\n", "/*!\n  * Bootstrap button.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Button = factory(global.BaseComponent, global.EventHandler, global.Index));\n})(this, (function (BaseComponent, EventHandler, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap button.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'button';\n  const DATA_KEY = 'bs.button';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const CLASS_NAME_ACTIVE = 'active';\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]';\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n\n  /**\n   * Class definition\n   */\n\n  class Button extends BaseComponent {\n    // Getters\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    toggle() {\n      // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n      this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE));\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Button.getOrCreateInstance(this);\n        if (config === 'toggle') {\n          data[config]();\n        }\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n    event.preventDefault();\n    const button = event.target.closest(SELECTOR_DATA_TOGGLE);\n    const data = Button.getOrCreateInstance(button);\n    data.toggle();\n  });\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Button);\n\n  return Button;\n\n}));\n//# sourceMappingURL=button.js.map\n", "/*!\n  * Bootstrap carousel.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js'), require('./util/swipe.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index', './util/swipe'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Carousel = factory(global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index, global.Swipe));\n})(this, (function (BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js, Swipe) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap carousel.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'carousel';\n  const DATA_KEY = 'bs.carousel';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const ARROW_LEFT_KEY = 'ArrowLeft';\n  const ARROW_RIGHT_KEY = 'ArrowRight';\n  const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\n  const ORDER_NEXT = 'next';\n  const ORDER_PREV = 'prev';\n  const DIRECTION_LEFT = 'left';\n  const DIRECTION_RIGHT = 'right';\n  const EVENT_SLIDE = `slide${EVENT_KEY}`;\n  const EVENT_SLID = `slid${EVENT_KEY}`;\n  const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;\n  const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`;\n  const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`;\n  const EVENT_DRAG_START = `dragstart${EVENT_KEY}`;\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n  const CLASS_NAME_CAROUSEL = 'carousel';\n  const CLASS_NAME_ACTIVE = 'active';\n  const CLASS_NAME_SLIDE = 'slide';\n  const CLASS_NAME_END = 'carousel-item-end';\n  const CLASS_NAME_START = 'carousel-item-start';\n  const CLASS_NAME_NEXT = 'carousel-item-next';\n  const CLASS_NAME_PREV = 'carousel-item-prev';\n  const SELECTOR_ACTIVE = '.active';\n  const SELECTOR_ITEM = '.carousel-item';\n  const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\n  const SELECTOR_ITEM_IMG = '.carousel-item img';\n  const SELECTOR_INDICATORS = '.carousel-indicators';\n  const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\n  const SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\n  const KEY_TO_DIRECTION = {\n    [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n    [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n  };\n  const Default = {\n    interval: 5000,\n    keyboard: true,\n    pause: 'hover',\n    ride: false,\n    touch: true,\n    wrap: true\n  };\n  const DefaultType = {\n    interval: '(number|boolean)',\n    // TODO:v6 remove boolean support\n    keyboard: 'boolean',\n    pause: '(string|boolean)',\n    ride: '(boolean|string)',\n    touch: 'boolean',\n    wrap: 'boolean'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Carousel extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._interval = null;\n      this._activeElement = null;\n      this._isSliding = false;\n      this.touchTimeout = null;\n      this._swipeHelper = null;\n      this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n      this._addEventListeners();\n      if (this._config.ride === CLASS_NAME_CAROUSEL) {\n        this.cycle();\n      }\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    next() {\n      this._slide(ORDER_NEXT);\n    }\n    nextWhenVisible() {\n      // FIXME TODO use `document.visibilityState`\n      // Don't call next when the page isn't visible\n      // or the carousel or its parent isn't visible\n      if (!document.hidden && index_js.isVisible(this._element)) {\n        this.next();\n      }\n    }\n    prev() {\n      this._slide(ORDER_PREV);\n    }\n    pause() {\n      if (this._isSliding) {\n        index_js.triggerTransitionEnd(this._element);\n      }\n      this._clearInterval();\n    }\n    cycle() {\n      this._clearInterval();\n      this._updateInterval();\n      this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n    }\n    _maybeEnableCycle() {\n      if (!this._config.ride) {\n        return;\n      }\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n        return;\n      }\n      this.cycle();\n    }\n    to(index) {\n      const items = this._getItems();\n      if (index > items.length - 1 || index < 0) {\n        return;\n      }\n      if (this._isSliding) {\n        EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n        return;\n      }\n      const activeIndex = this._getItemIndex(this._getActive());\n      if (activeIndex === index) {\n        return;\n      }\n      const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n      this._slide(order, items[index]);\n    }\n    dispose() {\n      if (this._swipeHelper) {\n        this._swipeHelper.dispose();\n      }\n      super.dispose();\n    }\n\n    // Private\n    _configAfterMerge(config) {\n      config.defaultInterval = config.interval;\n      return config;\n    }\n    _addEventListeners() {\n      if (this._config.keyboard) {\n        EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));\n      }\n      if (this._config.pause === 'hover') {\n        EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause());\n        EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle());\n      }\n      if (this._config.touch && Swipe.isSupported()) {\n        this._addTouchEventListeners();\n      }\n    }\n    _addTouchEventListeners() {\n      for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n        EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n      }\n      const endCallBack = () => {\n        if (this._config.pause !== 'hover') {\n          return;\n        }\n\n        // If it's a touch-enabled device, mouseenter/leave are fired as\n        // part of the mouse compatibility events on first tap - the carousel\n        // would stop cycling until user tapped out of it;\n        // here, we listen for touchend, explicitly pause the carousel\n        // (as if it's the second time we tap on it, mouseenter compat event\n        // is NOT fired) and after a timeout (to allow for mouse compatibility\n        // events to fire) we explicitly restart cycling\n\n        this.pause();\n        if (this.touchTimeout) {\n          clearTimeout(this.touchTimeout);\n        }\n        this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n      };\n      const swipeConfig = {\n        leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n        rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n        endCallback: endCallBack\n      };\n      this._swipeHelper = new Swipe(this._element, swipeConfig);\n    }\n    _keydown(event) {\n      if (/input|textarea/i.test(event.target.tagName)) {\n        return;\n      }\n      const direction = KEY_TO_DIRECTION[event.key];\n      if (direction) {\n        event.preventDefault();\n        this._slide(this._directionToOrder(direction));\n      }\n    }\n    _getItemIndex(element) {\n      return this._getItems().indexOf(element);\n    }\n    _setActiveIndicatorElement(index) {\n      if (!this._indicatorsElement) {\n        return;\n      }\n      const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n      activeIndicator.classList.remove(CLASS_NAME_ACTIVE);\n      activeIndicator.removeAttribute('aria-current');\n      const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n      if (newActiveIndicator) {\n        newActiveIndicator.classList.add(CLASS_NAME_ACTIVE);\n        newActiveIndicator.setAttribute('aria-current', 'true');\n      }\n    }\n    _updateInterval() {\n      const element = this._activeElement || this._getActive();\n      if (!element) {\n        return;\n      }\n      const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n      this._config.interval = elementInterval || this._config.defaultInterval;\n    }\n    _slide(order, element = null) {\n      if (this._isSliding) {\n        return;\n      }\n      const activeElement = this._getActive();\n      const isNext = order === ORDER_NEXT;\n      const nextElement = element || index_js.getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n      if (nextElement === activeElement) {\n        return;\n      }\n      const nextElementIndex = this._getItemIndex(nextElement);\n      const triggerEvent = eventName => {\n        return EventHandler.trigger(this._element, eventName, {\n          relatedTarget: nextElement,\n          direction: this._orderToDirection(order),\n          from: this._getItemIndex(activeElement),\n          to: nextElementIndex\n        });\n      };\n      const slideEvent = triggerEvent(EVENT_SLIDE);\n      if (slideEvent.defaultPrevented) {\n        return;\n      }\n      if (!activeElement || !nextElement) {\n        // Some weirdness is happening, so we bail\n        // TODO: change tests that use empty divs to avoid this check\n        return;\n      }\n      const isCycling = Boolean(this._interval);\n      this.pause();\n      this._isSliding = true;\n      this._setActiveIndicatorElement(nextElementIndex);\n      this._activeElement = nextElement;\n      const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n      const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n      nextElement.classList.add(orderClassName);\n      index_js.reflow(nextElement);\n      activeElement.classList.add(directionalClassName);\n      nextElement.classList.add(directionalClassName);\n      const completeCallBack = () => {\n        nextElement.classList.remove(directionalClassName, orderClassName);\n        nextElement.classList.add(CLASS_NAME_ACTIVE);\n        activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName);\n        this._isSliding = false;\n        triggerEvent(EVENT_SLID);\n      };\n      this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n      if (isCycling) {\n        this.cycle();\n      }\n    }\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_SLIDE);\n    }\n    _getActive() {\n      return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n    }\n    _getItems() {\n      return SelectorEngine.find(SELECTOR_ITEM, this._element);\n    }\n    _clearInterval() {\n      if (this._interval) {\n        clearInterval(this._interval);\n        this._interval = null;\n      }\n    }\n    _directionToOrder(direction) {\n      if (index_js.isRTL()) {\n        return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n      }\n      return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n    }\n    _orderToDirection(order) {\n      if (index_js.isRTL()) {\n        return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n      }\n      return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Carousel.getOrCreateInstance(this, config);\n        if (typeof config === 'number') {\n          data.to(config);\n          return;\n        }\n        if (typeof config === 'string') {\n          if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n          data[config]();\n        }\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n    const target = SelectorEngine.getElementFromSelector(this);\n    if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n      return;\n    }\n    event.preventDefault();\n    const carousel = Carousel.getOrCreateInstance(target);\n    const slideIndex = this.getAttribute('data-bs-slide-to');\n    if (slideIndex) {\n      carousel.to(slideIndex);\n      carousel._maybeEnableCycle();\n      return;\n    }\n    if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n      carousel.next();\n      carousel._maybeEnableCycle();\n      return;\n    }\n    carousel.prev();\n    carousel._maybeEnableCycle();\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n    for (const carousel of carousels) {\n      Carousel.getOrCreateInstance(carousel);\n    }\n  });\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Carousel);\n\n  return Carousel;\n\n}));\n//# sourceMappingURL=carousel.js.map\n", "/*!\n  * Bootstrap collapse.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));\n})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap collapse.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'collapse';\n  const DATA_KEY = 'bs.collapse';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_COLLAPSE = 'collapse';\n  const CLASS_NAME_COLLAPSING = 'collapsing';\n  const CLASS_NAME_COLLAPSED = 'collapsed';\n  const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\n  const CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\n  const WIDTH = 'width';\n  const HEIGHT = 'height';\n  const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]';\n  const Default = {\n    parent: null,\n    toggle: true\n  };\n  const DefaultType = {\n    parent: '(null|element)',\n    toggle: 'boolean'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Collapse extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isTransitioning = false;\n      this._triggerArray = [];\n      const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE);\n      for (const elem of toggleList) {\n        const selector = SelectorEngine.getSelectorFromElement(elem);\n        const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n        if (selector !== null && filterElement.length) {\n          this._triggerArray.push(elem);\n        }\n      }\n      this._initializeChildren();\n      if (!this._config.parent) {\n        this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n      }\n      if (this._config.toggle) {\n        this.toggle();\n      }\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    toggle() {\n      if (this._isShown()) {\n        this.hide();\n      } else {\n        this.show();\n      }\n    }\n    show() {\n      if (this._isTransitioning || this._isShown()) {\n        return;\n      }\n      let activeChildren = [];\n\n      // find active children\n      if (this._config.parent) {\n        activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n          toggle: false\n        }));\n      }\n      if (activeChildren.length && activeChildren[0]._isTransitioning) {\n        return;\n      }\n      const startEvent = EventHandler.trigger(this._element, EVENT_SHOW);\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n      for (const activeInstance of activeChildren) {\n        activeInstance.hide();\n      }\n      const dimension = this._getDimension();\n      this._element.classList.remove(CLASS_NAME_COLLAPSE);\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n      this._element.style[dimension] = 0;\n      this._addAriaAndCollapsedClass(this._triggerArray, true);\n      this._isTransitioning = true;\n      const complete = () => {\n        this._isTransitioning = false;\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n        this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);\n        this._element.style[dimension] = '';\n        EventHandler.trigger(this._element, EVENT_SHOWN);\n      };\n      const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n      const scrollSize = `scroll${capitalizedDimension}`;\n      this._queueCallback(complete, this._element, true);\n      this._element.style[dimension] = `${this._element[scrollSize]}px`;\n    }\n    hide() {\n      if (this._isTransitioning || !this._isShown()) {\n        return;\n      }\n      const startEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n      if (startEvent.defaultPrevented) {\n        return;\n      }\n      const dimension = this._getDimension();\n      this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n      index_js.reflow(this._element);\n      this._element.classList.add(CLASS_NAME_COLLAPSING);\n      this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);\n      for (const trigger of this._triggerArray) {\n        const element = SelectorEngine.getElementFromSelector(trigger);\n        if (element && !this._isShown(element)) {\n          this._addAriaAndCollapsedClass([trigger], false);\n        }\n      }\n      this._isTransitioning = true;\n      const complete = () => {\n        this._isTransitioning = false;\n        this._element.classList.remove(CLASS_NAME_COLLAPSING);\n        this._element.classList.add(CLASS_NAME_COLLAPSE);\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      };\n      this._element.style[dimension] = '';\n      this._queueCallback(complete, this._element, true);\n    }\n    _isShown(element = this._element) {\n      return element.classList.contains(CLASS_NAME_SHOW);\n    }\n\n    // Private\n    _configAfterMerge(config) {\n      config.toggle = Boolean(config.toggle); // Coerce string values\n      config.parent = index_js.getElement(config.parent);\n      return config;\n    }\n    _getDimension() {\n      return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n    }\n    _initializeChildren() {\n      if (!this._config.parent) {\n        return;\n      }\n      const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE);\n      for (const element of children) {\n        const selected = SelectorEngine.getElementFromSelector(element);\n        if (selected) {\n          this._addAriaAndCollapsedClass([element], this._isShown(selected));\n        }\n      }\n    }\n    _getFirstLevelChildren(selector) {\n      const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n      // remove children if greater depth\n      return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n    }\n    _addAriaAndCollapsedClass(triggerArray, isOpen) {\n      if (!triggerArray.length) {\n        return;\n      }\n      for (const element of triggerArray) {\n        element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n        element.setAttribute('aria-expanded', isOpen);\n      }\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      const _config = {};\n      if (typeof config === 'string' && /show|hide/.test(config)) {\n        _config.toggle = false;\n      }\n      return this.each(function () {\n        const data = Collapse.getOrCreateInstance(this, _config);\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n          data[config]();\n        }\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n    if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n      event.preventDefault();\n    }\n    for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n      Collapse.getOrCreateInstance(element, {\n        toggle: false\n      }).toggle();\n    }\n  });\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Collapse);\n\n  return Collapse;\n\n}));\n//# sourceMappingURL=collapse.js.map\n", "/*!\n  * Bootstrap dropdown.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global.Popper, global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index));\n})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js) { 'use strict';\n\n  function _interopNamespaceDefault(e) {\n    const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });\n    if (e) {\n      for (const k in e) {\n        if (k !== 'default') {\n          const d = Object.getOwnPropertyDescriptor(e, k);\n          Object.defineProperty(n, k, d.get ? d : {\n            enumerable: true,\n            get: () => e[k]\n          });\n        }\n      }\n    }\n    n.default = e;\n    return Object.freeze(n);\n  }\n\n  const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap dropdown.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'dropdown';\n  const DATA_KEY = 'bs.dropdown';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const ESCAPE_KEY = 'Escape';\n  const TAB_KEY = 'Tab';\n  const ARROW_UP_KEY = 'ArrowUp';\n  const ARROW_DOWN_KEY = 'ArrowDown';\n  const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n  const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`;\n  const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`;\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_DROPUP = 'dropup';\n  const CLASS_NAME_DROPEND = 'dropend';\n  const CLASS_NAME_DROPSTART = 'dropstart';\n  const CLASS_NAME_DROPUP_CENTER = 'dropup-center';\n  const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\n  const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`;\n  const SELECTOR_MENU = '.dropdown-menu:not(.o-dropdown--menu)'; // Odoo fix task-2764821\n  const SELECTOR_NAVBAR = '.navbar';\n  const SELECTOR_MENU_NOT_SUB = '.dropdown-menu:not(.o-dropdown--menu):not(.o_wysiwyg_submenu)';\n  const SELECTOR_NAVBAR_NAV = '.navbar-nav';\n  const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\n  const PLACEMENT_TOP = index_js.isRTL() ? 'top-end' : 'top-start';\n  const PLACEMENT_TOPEND = index_js.isRTL() ? 'top-start' : 'top-end';\n  const PLACEMENT_BOTTOM = index_js.isRTL() ? 'bottom-end' : 'bottom-start';\n  const PLACEMENT_BOTTOMEND = index_js.isRTL() ? 'bottom-start' : 'bottom-end';\n  const PLACEMENT_RIGHT = index_js.isRTL() ? 'left-start' : 'right-start';\n  const PLACEMENT_LEFT = index_js.isRTL() ? 'right-start' : 'left-start';\n  const PLACEMENT_TOPCENTER = 'top';\n  const PLACEMENT_BOTTOMCENTER = 'bottom';\n  const Default = {\n    autoClose: true,\n    boundary: 'clippingParents',\n    display: 'dynamic',\n    offset: [0, 2],\n    popperConfig: null,\n    reference: 'toggle'\n  };\n  const DefaultType = {\n    autoClose: '(boolean|string)',\n    boundary: '(string|element)',\n    display: 'string',\n    offset: '(array|string|function)',\n    popperConfig: '(null|object|function)',\n    reference: '(string|element|object)'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Dropdown extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._popper = null;\n      this._parent = this._element.parentNode; // dropdown wrapper\n      // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n      this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n      this._inNavbar = this._detectNavbar();\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    toggle() {\n      return this._isShown() ? this.hide() : this.show();\n    }\n    show() {\n      if (index_js.isDisabled(this._element) || this._isShown()) {\n        return;\n      }\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget);\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n      this._createPopper();\n\n      // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n      if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', index_js.noop);\n        }\n      }\n      this._element.focus();\n      this._element.setAttribute('aria-expanded', true);\n      this._menu.classList.add(CLASS_NAME_SHOW);\n      this._element.classList.add(CLASS_NAME_SHOW);\n      EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget);\n    }\n    hide() {\n      if (index_js.isDisabled(this._element) || !this._isShown()) {\n        return;\n      }\n      const relatedTarget = {\n        relatedTarget: this._element\n      };\n      this._completeHide(relatedTarget);\n    }\n    dispose() {\n      if (this._popper) {\n        this._popper.destroy();\n      }\n      super.dispose();\n    }\n    update() {\n      this._inNavbar = this._detectNavbar();\n      if (this._popper) {\n        this._popper.update();\n      }\n    }\n\n    // Private\n    _completeHide(relatedTarget) {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget);\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n\n      // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', index_js.noop);\n        }\n      }\n      if (this._popper) {\n        this._popper.destroy();\n      }\n      this._menu.classList.remove(CLASS_NAME_SHOW);\n      this._element.classList.remove(CLASS_NAME_SHOW);\n      this._element.setAttribute('aria-expanded', 'false');\n      Manipulator.removeDataAttribute(this._menu, 'popper');\n      EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget);\n    }\n    _getConfig(config) {\n      config = super._getConfig(config);\n      if (typeof config.reference === 'object' && !index_js.isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n        // Popper virtual elements require a getBoundingClientRect method\n        throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n      }\n      return config;\n    }\n    _createPopper() {\n      if (typeof Popper__namespace === 'undefined') {\n        throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n      }\n      let referenceElement = this._element;\n      if (this._config.reference === 'parent') {\n        referenceElement = this._parent;\n      } else if (index_js.isElement(this._config.reference)) {\n        referenceElement = index_js.getElement(this._config.reference);\n      } else if (typeof this._config.reference === 'object') {\n        referenceElement = this._config.reference;\n      }\n      const popperConfig = this._getPopperConfig();\n      this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig);\n    }\n    _isShown() {\n      return this._menu.classList.contains(CLASS_NAME_SHOW);\n    }\n    _getPlacement() {\n      const parentDropdown = this._parent;\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n        return PLACEMENT_RIGHT;\n      }\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n        return PLACEMENT_LEFT;\n      }\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n        return PLACEMENT_TOPCENTER;\n      }\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n        return PLACEMENT_BOTTOMCENTER;\n      }\n\n      // We need to trim the value because custom properties can also include spaces\n      const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n      if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n        return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n      }\n      return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n    }\n    _detectNavbar() {\n      return this._element.closest(SELECTOR_NAVBAR) !== null;\n    }\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n      return offset;\n    }\n    _getPopperConfig() {\n      const defaultBsPopperConfig = {\n        placement: this._getPlacement(),\n        modifiers: [{\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }]\n      };\n\n      // Disable Popper if we have a static display or Dropdown is in Navbar\n      if (this._inNavbar || this._config.display === 'static') {\n        Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n        defaultBsPopperConfig.modifiers = [{\n          name: 'applyStyles',\n          enabled: false\n        }];\n      }\n      return {\n        ...defaultBsPopperConfig,\n        ...index_js.execute(this._config.popperConfig, [defaultBsPopperConfig])\n      };\n    }\n    _selectMenuItem({\n      key,\n      target\n    }) {\n      const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => index_js.isVisible(element));\n      if (!items.length) {\n        return;\n      }\n\n      // if target isn't included in items (e.g. when expanding the dropdown)\n      // allow cycling to get the last item in case key equals ARROW_UP_KEY\n      index_js.getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus();\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Dropdown.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config]();\n      });\n    }\n    static clearMenus(event) {\n      if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY) {\n        return;\n      }\n      const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n      for (const toggle of openToggles) {\n        const context = Dropdown.getInstance(toggle);\n        if (!context || context._config.autoClose === false) {\n          continue;\n        }\n        const composedPath = event.composedPath();\n        const isMenuTarget = composedPath.includes(context._menu);\n        if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n          continue;\n        }\n\n        // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n        if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n          continue;\n        }\n        const relatedTarget = {\n          relatedTarget: context._element\n        };\n        if (event.type === 'click') {\n          relatedTarget.clickEvent = event;\n        }\n        context._completeHide(relatedTarget);\n      }\n    }\n    static dataApiKeydownHandler(event) {\n      // If not an UP | DOWN | ESCAPE key => not a dropdown command\n      // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n      const isInput = /input|textarea/i.test(event.target.tagName);\n      const isEscapeEvent = event.key === ESCAPE_KEY;\n      const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key);\n      if (!isUpOrDownEvent && !isEscapeEvent) {\n        return;\n      }\n      if (isInput && !isEscapeEvent) {\n        return;\n      }\n      event.preventDefault();\n\n      // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n      const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode);\n      const instance = Dropdown.getOrCreateInstance(getToggleButton);\n      if (isUpOrDownEvent) {\n        event.stopPropagation();\n        instance.show();\n        instance._selectMenuItem(event);\n        return;\n      }\n      if (instance._isShown()) {\n        // else is escape and we check if it is shown\n        event.stopPropagation();\n        instance.hide();\n        getToggleButton.focus();\n      }\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU_NOT_SUB, Dropdown.dataApiKeydownHandler);\n  EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    event.preventDefault();\n    Dropdown.getOrCreateInstance(this).toggle();\n  });\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Dropdown);\n\n  return Dropdown;\n\n}));\n//# sourceMappingURL=dropdown.js.map\n", "/*!\n  * Bootstrap modal.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar));\n})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap modal.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'modal';\n  const DATA_KEY = 'bs.modal';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const ESCAPE_KEY = 'Escape';\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const EVENT_RESIZE = `resize${EVENT_KEY}`;\n  const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;\n  const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;\n  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n  const CLASS_NAME_OPEN = 'modal-open';\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_STATIC = 'modal-static';\n  const OPEN_SELECTOR = '.modal.show';\n  const SELECTOR_DIALOG = '.modal-dialog';\n  const SELECTOR_MODAL_BODY = '.modal-body';\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]';\n  const Default = {\n    backdrop: true,\n    focus: true,\n    keyboard: true\n  };\n  const DefaultType = {\n    backdrop: '(boolean|string)',\n    focus: 'boolean',\n    keyboard: 'boolean'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Modal extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n      this._isShown = false;\n      this._isTransitioning = false;\n      this._scrollBar = new ScrollBarHelper();\n      this._addEventListeners();\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n    show(relatedTarget) {\n      if (this._isShown || this._isTransitioning) {\n        return;\n      }\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n        relatedTarget\n      });\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n      this._isShown = true;\n      this._isTransitioning = true;\n      this._scrollBar.hide();\n      document.body.classList.add(CLASS_NAME_OPEN);\n      this._adjustDialog();\n      this._backdrop.show(() => this._showElement(relatedTarget));\n    }\n    hide() {\n      if (!this._isShown || this._isTransitioning) {\n        return;\n      }\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n      this._isShown = false;\n      this._isTransitioning = true;\n      this._focustrap.deactivate();\n      this._element.classList.remove(CLASS_NAME_SHOW);\n      this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n    }\n    dispose() {\n      EventHandler.off(window, EVENT_KEY);\n      EventHandler.off(this._dialog, EVENT_KEY);\n      this._backdrop.dispose();\n      this._focustrap.deactivate();\n      super.dispose();\n    }\n    handleUpdate() {\n      this._adjustDialog();\n    }\n\n    // Private\n    _initializeBackDrop() {\n      return new Backdrop({\n        isVisible: Boolean(this._config.backdrop),\n        // 'static' option will be translated to true, and booleans will keep their value,\n        isAnimated: this._isAnimated()\n      });\n    }\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n    _showElement(relatedTarget) {\n      // try to append dynamic modal\n      if (!document.body.contains(this._element)) {\n        document.body.append(this._element);\n      }\n      this._element.style.display = 'block';\n      this._element.removeAttribute('aria-hidden');\n      this._element.setAttribute('aria-modal', true);\n      this._element.setAttribute('role', 'dialog');\n      this._element.scrollTop = 0;\n      const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n      if (modalBody) {\n        modalBody.scrollTop = 0;\n      }\n      index_js.reflow(this._element);\n      this._element.classList.add(CLASS_NAME_SHOW);\n      const transitionComplete = () => {\n        if (this._config.focus) {\n          this._focustrap.activate();\n        }\n        this._isTransitioning = false;\n        EventHandler.trigger(this._element, EVENT_SHOWN, {\n          relatedTarget\n        });\n      };\n      this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n    }\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n        if (event.key !== ESCAPE_KEY) {\n          return;\n        }\n        if (this._config.keyboard) {\n          this.hide();\n          return;\n        }\n        this._triggerBackdropTransition();\n      });\n      EventHandler.on(window, EVENT_RESIZE, () => {\n        if (this._isShown && !this._isTransitioning) {\n          this._adjustDialog();\n        }\n      });\n      EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n        // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n        EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n          if (this._element !== event.target || this._element !== event2.target) {\n            return;\n          }\n          if (this._config.backdrop === 'static') {\n            this._triggerBackdropTransition();\n            return;\n          }\n          if (this._config.backdrop) {\n            this.hide();\n          }\n        });\n      });\n    }\n    _hideModal() {\n      this._element.style.display = 'none';\n      this._element.setAttribute('aria-hidden', true);\n      this._element.removeAttribute('aria-modal');\n      this._element.removeAttribute('role');\n      this._isTransitioning = false;\n      this._backdrop.hide(() => {\n        document.body.classList.remove(CLASS_NAME_OPEN);\n        this._resetAdjustments();\n        this._scrollBar.reset();\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      });\n    }\n    _isAnimated() {\n      return this._element.classList.contains(CLASS_NAME_FADE);\n    }\n    _triggerBackdropTransition() {\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n      const initialOverflowY = this._element.style.overflowY;\n      // return if the following background transition hasn't yet completed\n      if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n        return;\n      }\n      if (!isModalOverflowing) {\n        this._element.style.overflowY = 'hidden';\n      }\n      this._element.classList.add(CLASS_NAME_STATIC);\n      this._queueCallback(() => {\n        this._element.classList.remove(CLASS_NAME_STATIC);\n        this._queueCallback(() => {\n          this._element.style.overflowY = initialOverflowY;\n        }, this._dialog);\n      }, this._dialog);\n      this._element.focus();\n    }\n\n    /**\n     * The following methods are used to handle overflowing modals\n     */\n\n    _adjustDialog() {\n      const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n      const scrollbarWidth = this._scrollBar.getWidth();\n      const isBodyOverflowing = scrollbarWidth > 0;\n      if (isBodyOverflowing && !isModalOverflowing) {\n        const property = index_js.isRTL() ? 'paddingLeft' : 'paddingRight';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n      if (!isBodyOverflowing && isModalOverflowing) {\n        const property = index_js.isRTL() ? 'paddingRight' : 'paddingLeft';\n        this._element.style[property] = `${scrollbarWidth}px`;\n      }\n    }\n    _resetAdjustments() {\n      this._element.style.paddingLeft = '';\n      this._element.style.paddingRight = '';\n    }\n\n    // Static\n    static jQueryInterface(config, relatedTarget) {\n      return this.each(function () {\n        const data = Modal.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config](relatedTarget);\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    const target = SelectorEngine.getElementFromSelector(this);\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n    EventHandler.one(target, EVENT_SHOW, showEvent => {\n      if (showEvent.defaultPrevented) {\n        // only register focus restorer if modal will actually get shown\n        return;\n      }\n      EventHandler.one(target, EVENT_HIDDEN, () => {\n        if (index_js.isVisible(this)) {\n          this.focus();\n        }\n      });\n    });\n\n    // avoid conflict when clicking modal toggler while another one is open\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n    if (alreadyOpen) {\n      Modal.getInstance(alreadyOpen).hide();\n    }\n    const data = Modal.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  componentFunctions_js.enableDismissTrigger(Modal);\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Modal);\n\n  return Modal;\n\n}));\n//# sourceMappingURL=modal.js.map\n", "/*!\n  * Bootstrap offcanvas.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Offcanvas = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar));\n})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap offcanvas.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'offcanvas';\n  const DATA_KEY = 'bs.offcanvas';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;\n  const ESCAPE_KEY = 'Escape';\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_SHOWING = 'showing';\n  const CLASS_NAME_HIDING = 'hiding';\n  const CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\n  const OPEN_SELECTOR = '.offcanvas.show';\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_RESIZE = `resize${EVENT_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;\n  const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]';\n  const Default = {\n    backdrop: true,\n    keyboard: true,\n    scroll: false\n  };\n  const DefaultType = {\n    backdrop: '(boolean|string)',\n    keyboard: 'boolean',\n    scroll: 'boolean'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Offcanvas extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._isShown = false;\n      this._backdrop = this._initializeBackDrop();\n      this._focustrap = this._initializeFocusTrap();\n      this._addEventListeners();\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    toggle(relatedTarget) {\n      return this._isShown ? this.hide() : this.show(relatedTarget);\n    }\n    show(relatedTarget) {\n      if (this._isShown) {\n        return;\n      }\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n        relatedTarget\n      });\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n      this._isShown = true;\n      this._backdrop.show();\n      if (!this._config.scroll) {\n        new ScrollBarHelper().hide();\n      }\n      this._element.setAttribute('aria-modal', true);\n      this._element.setAttribute('role', 'dialog');\n      this._element.classList.add(CLASS_NAME_SHOWING);\n      const completeCallBack = () => {\n        if (!this._config.scroll || this._config.backdrop) {\n          this._focustrap.activate();\n        }\n        this._element.classList.add(CLASS_NAME_SHOW);\n        this._element.classList.remove(CLASS_NAME_SHOWING);\n        EventHandler.trigger(this._element, EVENT_SHOWN, {\n          relatedTarget\n        });\n      };\n      this._queueCallback(completeCallBack, this._element, true);\n    }\n    hide() {\n      if (!this._isShown) {\n        return;\n      }\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n      this._focustrap.deactivate();\n      this._element.blur();\n      this._isShown = false;\n      this._element.classList.add(CLASS_NAME_HIDING);\n      this._backdrop.hide();\n      const completeCallback = () => {\n        this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING);\n        this._element.removeAttribute('aria-modal');\n        this._element.removeAttribute('role');\n        if (!this._config.scroll) {\n          new ScrollBarHelper().reset();\n        }\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      };\n      this._queueCallback(completeCallback, this._element, true);\n    }\n    dispose() {\n      this._backdrop.dispose();\n      this._focustrap.deactivate();\n      super.dispose();\n    }\n\n    // Private\n    _initializeBackDrop() {\n      const clickCallback = () => {\n        if (this._config.backdrop === 'static') {\n          EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n          return;\n        }\n        this.hide();\n      };\n\n      // 'static' option will be translated to true, and booleans will keep their value\n      const isVisible = Boolean(this._config.backdrop);\n      return new Backdrop({\n        className: CLASS_NAME_BACKDROP,\n        isVisible,\n        isAnimated: true,\n        rootElement: this._element.parentNode,\n        clickCallback: isVisible ? clickCallback : null\n      });\n    }\n    _initializeFocusTrap() {\n      return new FocusTrap({\n        trapElement: this._element\n      });\n    }\n    _addEventListeners() {\n      EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n        if (event.key !== ESCAPE_KEY) {\n          return;\n        }\n        if (this._config.keyboard) {\n          this.hide();\n          return;\n        }\n        EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n      });\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Offcanvas.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config](this);\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    const target = SelectorEngine.getElementFromSelector(this);\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n    if (index_js.isDisabled(this)) {\n      return;\n    }\n    EventHandler.one(target, EVENT_HIDDEN, () => {\n      // focus on trigger when it is closed\n      if (index_js.isVisible(this)) {\n        this.focus();\n      }\n    });\n\n    // avoid conflict when clicking a toggler of an offcanvas, while another is open\n    const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n    if (alreadyOpen && alreadyOpen !== target) {\n      Offcanvas.getInstance(alreadyOpen).hide();\n    }\n    const data = Offcanvas.getOrCreateInstance(target);\n    data.toggle(this);\n  });\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n      Offcanvas.getOrCreateInstance(selector).show();\n    }\n  });\n  EventHandler.on(window, EVENT_RESIZE, () => {\n    for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n      if (getComputedStyle(element).position !== 'fixed') {\n        Offcanvas.getOrCreateInstance(element).hide();\n      }\n    }\n  });\n  componentFunctions_js.enableDismissTrigger(Offcanvas);\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Offcanvas);\n\n  return Offcanvas;\n\n}));\n//# sourceMappingURL=offcanvas.js.map\n", "/*!\n  * Bootstrap tooltip.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./util/index.js'), require('./util/sanitizer.js'), require('./util/template-factory.js')) :\n  typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './util/index', './util/sanitizer', './util/template-factory'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global.Popper, global.BaseComponent, global.EventHandler, global.Manipulator, global.Index, global.Sanitizer, global.TemplateFactory));\n})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, index_js, sanitizer_js, TemplateFactory) { 'use strict';\n\n  function _interopNamespaceDefault(e) {\n    const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });\n    if (e) {\n      for (const k in e) {\n        if (k !== 'default') {\n          const d = Object.getOwnPropertyDescriptor(e, k);\n          Object.defineProperty(n, k, d.get ? d : {\n            enumerable: true,\n            get: () => e[k]\n          });\n        }\n      }\n    }\n    n.default = e;\n    return Object.freeze(n);\n  }\n\n  const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap tooltip.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'tooltip';\n  const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_MODAL = 'modal';\n  const CLASS_NAME_SHOW = 'show';\n  const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\n  const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\n  const EVENT_MODAL_HIDE = 'hide.bs.modal';\n  const TRIGGER_HOVER = 'hover';\n  const TRIGGER_FOCUS = 'focus';\n  const TRIGGER_CLICK = 'click';\n  const TRIGGER_MANUAL = 'manual';\n  const EVENT_HIDE = 'hide';\n  const EVENT_HIDDEN = 'hidden';\n  const EVENT_SHOW = 'show';\n  const EVENT_SHOWN = 'shown';\n  const EVENT_INSERTED = 'inserted';\n  const EVENT_CLICK = 'click';\n  const EVENT_FOCUSIN = 'focusin';\n  const EVENT_FOCUSOUT = 'focusout';\n  const EVENT_MOUSEENTER = 'mouseenter';\n  const EVENT_MOUSELEAVE = 'mouseleave';\n  const AttachmentMap = {\n    AUTO: 'auto',\n    TOP: 'top',\n    RIGHT: index_js.isRTL() ? 'left' : 'right',\n    BOTTOM: 'bottom',\n    LEFT: index_js.isRTL() ? 'right' : 'left'\n  };\n  const Default = {\n    allowList: sanitizer_js.DefaultAllowlist,\n    animation: true,\n    boundary: 'clippingParents',\n    container: false,\n    customClass: '',\n    delay: 0,\n    fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n    html: false,\n    offset: [0, 6],\n    placement: 'top',\n    popperConfig: null,\n    sanitize: true,\n    sanitizeFn: null,\n    selector: false,\n    template: '<div class=\"tooltip\" role=\"tooltip\">' + '<div class=\"tooltip-arrow\"></div>' + '<div class=\"tooltip-inner\"></div>' + '</div>',\n    title: '',\n    trigger: 'hover focus'\n  };\n  const DefaultType = {\n    allowList: 'object',\n    animation: 'boolean',\n    boundary: '(string|element)',\n    container: '(string|element|boolean)',\n    customClass: '(string|function)',\n    delay: '(number|object)',\n    fallbackPlacements: 'array',\n    html: 'boolean',\n    offset: '(array|string|function)',\n    placement: '(string|function)',\n    popperConfig: '(null|object|function)',\n    sanitize: 'boolean',\n    sanitizeFn: '(null|function)',\n    selector: '(string|boolean)',\n    template: 'string',\n    title: '(string|element|function)',\n    trigger: 'string'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Tooltip extends BaseComponent {\n    constructor(element, config) {\n      if (typeof Popper__namespace === 'undefined') {\n        throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n      }\n      super(element, config);\n\n      // Private\n      this._isEnabled = true;\n      this._timeout = 0;\n      this._isHovered = null;\n      this._activeTrigger = {};\n      this._popper = null;\n      this._templateFactory = null;\n      this._newContent = null;\n\n      // Protected\n      this.tip = null;\n      this._setListeners();\n      if (!this._config.selector) {\n        this._fixTitle();\n      }\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    enable() {\n      this._isEnabled = true;\n    }\n    disable() {\n      this._isEnabled = false;\n    }\n    toggleEnabled() {\n      this._isEnabled = !this._isEnabled;\n    }\n    toggle() {\n      if (!this._isEnabled) {\n        return;\n      }\n      this._activeTrigger.click = !this._activeTrigger.click;\n      if (this._isShown()) {\n        this._leave();\n        return;\n      }\n      this._enter();\n    }\n    dispose() {\n      clearTimeout(this._timeout);\n      EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n      if (this._element.getAttribute('data-bs-original-title')) {\n        this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n      }\n      this._disposePopper();\n      super.dispose();\n    }\n    show() {\n      if (this._element.style.display === 'none') {\n        throw new Error('Please use show on visible elements');\n      }\n      if (!(this._isWithContent() && this._isEnabled)) {\n        return;\n      }\n      const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW));\n      const shadowRoot = index_js.findShadowRoot(this._element);\n      const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n      if (showEvent.defaultPrevented || !isInTheDom) {\n        return;\n      }\n\n      // TODO: v6 remove this or make it optional\n      this._disposePopper();\n      const tip = this._getTipElement();\n      this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n      const {\n        container\n      } = this._config;\n      if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n        container.append(tip);\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n      }\n      this._popper = this._createPopper(tip);\n      tip.classList.add(CLASS_NAME_SHOW);\n\n      // If this is a touch-enabled device we add extra\n      // empty mouseover listeners to the body's immediate children;\n      // only needed because of broken event delegation on iOS\n      // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.on(element, 'mouseover', index_js.noop);\n        }\n      }\n      const complete = () => {\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN));\n        if (this._isHovered === false) {\n          this._leave();\n        }\n        this._isHovered = false;\n      };\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n    hide() {\n      if (!this._isShown()) {\n        return;\n      }\n      const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE));\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n      const tip = this._getTipElement();\n      tip.classList.remove(CLASS_NAME_SHOW);\n\n      // If this is a touch-enabled device we remove the extra\n      // empty mouseover listeners we added for iOS support\n      if ('ontouchstart' in document.documentElement) {\n        for (const element of [].concat(...document.body.children)) {\n          EventHandler.off(element, 'mouseover', index_js.noop);\n        }\n      }\n      this._activeTrigger[TRIGGER_CLICK] = false;\n      this._activeTrigger[TRIGGER_FOCUS] = false;\n      this._activeTrigger[TRIGGER_HOVER] = false;\n      this._isHovered = null; // it is a trick to support manual triggering\n\n      const complete = () => {\n        if (this._isWithActiveTrigger()) {\n          return;\n        }\n        if (!this._isHovered) {\n          this._disposePopper();\n        }\n        this._element.removeAttribute('aria-describedby');\n        EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN));\n      };\n      this._queueCallback(complete, this.tip, this._isAnimated());\n    }\n    update() {\n      if (this._popper) {\n        this._popper.update();\n      }\n    }\n\n    // Protected\n    _isWithContent() {\n      return Boolean(this._getTitle());\n    }\n    _getTipElement() {\n      if (!this.tip) {\n        this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n      }\n      return this.tip;\n    }\n    _createTipElement(content) {\n      const tip = this._getTemplateFactory(content).toHtml();\n\n      // TODO: remove this check in v6\n      if (!tip) {\n        return null;\n      }\n      tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);\n      // TODO: v6 the following can be achieved with CSS only\n      tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n      const tipId = index_js.getUID(this.constructor.NAME).toString();\n      tip.setAttribute('id', tipId);\n      if (this._isAnimated()) {\n        tip.classList.add(CLASS_NAME_FADE);\n      }\n      return tip;\n    }\n    setContent(content) {\n      this._newContent = content;\n      if (this._isShown()) {\n        this._disposePopper();\n        this.show();\n      }\n    }\n    _getTemplateFactory(content) {\n      if (this._templateFactory) {\n        this._templateFactory.changeContent(content);\n      } else {\n        this._templateFactory = new TemplateFactory({\n          ...this._config,\n          // the `content` var has to be after `this._config`\n          // to override config.content in case of popover\n          content,\n          extraClass: this._resolvePossibleFunction(this._config.customClass)\n        });\n      }\n      return this._templateFactory;\n    }\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n      };\n    }\n    _getTitle() {\n      return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n    }\n\n    // Private\n    _initializeOnDelegatedTarget(event) {\n      return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n    }\n    _isAnimated() {\n      return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE);\n    }\n    _isShown() {\n      return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW);\n    }\n    _createPopper(tip) {\n      const placement = index_js.execute(this._config.placement, [this, tip, this._element]);\n      const attachment = AttachmentMap[placement.toUpperCase()];\n      return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));\n    }\n    _getOffset() {\n      const {\n        offset\n      } = this._config;\n      if (typeof offset === 'string') {\n        return offset.split(',').map(value => Number.parseInt(value, 10));\n      }\n      if (typeof offset === 'function') {\n        return popperData => offset(popperData, this._element);\n      }\n      return offset;\n    }\n    _resolvePossibleFunction(arg) {\n      return index_js.execute(arg, [this._element]);\n    }\n    _getPopperConfig(attachment) {\n      const defaultBsPopperConfig = {\n        placement: attachment,\n        modifiers: [{\n          name: 'flip',\n          options: {\n            fallbackPlacements: this._config.fallbackPlacements\n          }\n        }, {\n          name: 'offset',\n          options: {\n            offset: this._getOffset()\n          }\n        }, {\n          name: 'preventOverflow',\n          options: {\n            boundary: this._config.boundary\n          }\n        }, {\n          name: 'arrow',\n          options: {\n            element: `.${this.constructor.NAME}-arrow`\n          }\n        }, {\n          name: 'preSetPlacement',\n          enabled: true,\n          phase: 'beforeMain',\n          fn: data => {\n            // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n            // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n            this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n          }\n        }]\n      };\n      return {\n        ...defaultBsPopperConfig,\n        ...index_js.execute(this._config.popperConfig, [defaultBsPopperConfig])\n      };\n    }\n    _setListeners() {\n      const triggers = this._config.trigger.split(' ');\n      for (const trigger of triggers) {\n        if (trigger === 'click') {\n          EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n            context.toggle();\n          });\n        } else if (trigger !== TRIGGER_MANUAL) {\n          const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN);\n          const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT);\n          EventHandler.on(this._element, eventIn, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n            context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n            context._enter();\n          });\n          EventHandler.on(this._element, eventOut, this._config.selector, event => {\n            const context = this._initializeOnDelegatedTarget(event);\n            context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n            context._leave();\n          });\n        }\n      }\n      this._hideModalHandler = () => {\n        if (this._element) {\n          this.hide();\n        }\n      };\n      EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n    }\n    _fixTitle() {\n      const title = this._element.getAttribute('title');\n      if (!title) {\n        return;\n      }\n      if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n        this._element.setAttribute('aria-label', title);\n      }\n      this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n      this._element.removeAttribute('title');\n    }\n    _enter() {\n      if (this._isShown() || this._isHovered) {\n        this._isHovered = true;\n        return;\n      }\n      this._isHovered = true;\n      this._setTimeout(() => {\n        if (this._isHovered) {\n          this.show();\n        }\n      }, this._config.delay.show);\n    }\n    _leave() {\n      if (this._isWithActiveTrigger()) {\n        return;\n      }\n      this._isHovered = false;\n      this._setTimeout(() => {\n        if (!this._isHovered) {\n          this.hide();\n        }\n      }, this._config.delay.hide);\n    }\n    _setTimeout(handler, timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = setTimeout(handler, timeout);\n    }\n    _isWithActiveTrigger() {\n      return Object.values(this._activeTrigger).includes(true);\n    }\n    _getConfig(config) {\n      const dataAttributes = Manipulator.getDataAttributes(this._element);\n      for (const dataAttribute of Object.keys(dataAttributes)) {\n        if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n          delete dataAttributes[dataAttribute];\n        }\n      }\n      config = {\n        ...dataAttributes,\n        ...(typeof config === 'object' && config ? config : {})\n      };\n      config = this._mergeConfigObj(config);\n      config = this._configAfterMerge(config);\n      this._typeCheckConfig(config);\n      return config;\n    }\n    _configAfterMerge(config) {\n      config.container = config.container === false ? document.body : index_js.getElement(config.container);\n      if (typeof config.delay === 'number') {\n        config.delay = {\n          show: config.delay,\n          hide: config.delay\n        };\n      }\n      if (typeof config.title === 'number') {\n        config.title = config.title.toString();\n      }\n      if (typeof config.content === 'number') {\n        config.content = config.content.toString();\n      }\n      return config;\n    }\n    _getDelegateConfig() {\n      const config = {};\n      for (const [key, value] of Object.entries(this._config)) {\n        if (this.constructor.Default[key] !== value) {\n          config[key] = value;\n        }\n      }\n      config.selector = false;\n      config.trigger = 'manual';\n\n      // In the future can be replaced with:\n      // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n      // `Object.fromEntries(keysWithDifferentValues)`\n      return config;\n    }\n    _disposePopper() {\n      if (this._popper) {\n        this._popper.destroy();\n        this._popper = null;\n      }\n      if (this.tip) {\n        this.tip.remove();\n        this.tip = null;\n      }\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tooltip.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config]();\n      });\n    }\n  }\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Tooltip);\n\n  return Tooltip;\n\n}));\n//# sourceMappingURL=tooltip.js.map\n", "/*!\n  * Bootstrap popover.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./tooltip.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./tooltip', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Popover = factory(global.Tooltip, global.Index));\n})(this, (function (Tooltip, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap popover.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'popover';\n  const SELECTOR_TITLE = '.popover-header';\n  const SELECTOR_CONTENT = '.popover-body';\n  const Default = {\n    ...Tooltip.Default,\n    content: '',\n    offset: [0, 8],\n    placement: 'right',\n    template: '<div class=\"popover\" role=\"tooltip\">' + '<div class=\"popover-arrow\"></div>' + '<h3 class=\"popover-header\"></h3>' + '<div class=\"popover-body\"></div>' + '</div>',\n    trigger: 'click'\n  };\n  const DefaultType = {\n    ...Tooltip.DefaultType,\n    content: '(null|string|element|function)'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Popover extends Tooltip {\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Overrides\n    _isWithContent() {\n      return this._getTitle() || this._getContent();\n    }\n\n    // Private\n    _getContentForTemplate() {\n      return {\n        [SELECTOR_TITLE]: this._getTitle(),\n        [SELECTOR_CONTENT]: this._getContent()\n      };\n    }\n    _getContent() {\n      return this._resolvePossibleFunction(this._config.content);\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Popover.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (typeof data[config] === 'undefined') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config]();\n      });\n    }\n  }\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Popover);\n\n  return Popover;\n\n}));\n//# sourceMappingURL=popover.js.map\n", "/*!\n  * Bootstrap scrollspy.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrollSpy = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));\n})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap scrollspy.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'scrollspy';\n  const DATA_KEY = 'bs.scrollspy';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const DATA_API_KEY = '.data-api';\n  const EVENT_ACTIVATE = `activate${EVENT_KEY}`;\n  const EVENT_CLICK = `click${EVENT_KEY}`;\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;\n  const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\n  const CLASS_NAME_ACTIVE = 'active';\n  const SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\n  const SELECTOR_TARGET_LINKS = '[href]';\n  const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\n  const SELECTOR_NAV_LINKS = '.nav-link';\n  const SELECTOR_NAV_ITEMS = '.nav-item';\n  const SELECTOR_LIST_ITEMS = '.list-group-item';\n  const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\n  const SELECTOR_DROPDOWN = '.dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';\n  const Default = {\n    offset: null,\n    // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: '0px 0px -25%',\n    smoothScroll: false,\n    target: null,\n    threshold: [0.1, 0.5, 1]\n  };\n  const DefaultType = {\n    offset: '(number|null)',\n    // TODO v6 @deprecated, keep it for backwards compatibility reasons\n    rootMargin: 'string',\n    smoothScroll: 'boolean',\n    target: 'element',\n    threshold: 'array'\n  };\n\n  /**\n   * Class definition\n   */\n\n  class ScrollSpy extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n\n      // this._element is the observablesContainer and config.target the menu links wrapper\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n      this._activeTarget = null;\n      this._observer = null;\n      this._previousScrollData = {\n        visibleEntryTop: 0,\n        parentScrollTop: 0\n      };\n      this.refresh(); // initialize\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    refresh() {\n      this._initializeTargetsAndObservables();\n      this._maybeEnableSmoothScroll();\n      if (this._observer) {\n        this._observer.disconnect();\n      } else {\n        this._observer = this._getNewObserver();\n      }\n      for (const section of this._observableSections.values()) {\n        this._observer.observe(section);\n      }\n    }\n    dispose() {\n      this._observer.disconnect();\n      super.dispose();\n    }\n\n    // Private\n    _configAfterMerge(config) {\n      // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n      config.target = index_js.getElement(config.target) || document.body;\n\n      // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n      config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n      if (typeof config.threshold === 'string') {\n        config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n      }\n      return config;\n    }\n    _maybeEnableSmoothScroll() {\n      if (!this._config.smoothScroll) {\n        return;\n      }\n\n      // unregister any previous listeners\n      EventHandler.off(this._config.target, EVENT_CLICK);\n      EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n        const observableSection = this._observableSections.get(event.target.hash);\n        if (observableSection) {\n          event.preventDefault();\n          const root = this._rootElement || window;\n          const height = observableSection.offsetTop - this._element.offsetTop;\n          if (root.scrollTo) {\n            root.scrollTo({\n              top: height,\n              behavior: 'smooth'\n            });\n            return;\n          }\n\n          // Chrome 60 doesn't support `scrollTo`\n          root.scrollTop = height;\n        }\n      });\n    }\n    _getNewObserver() {\n      const options = {\n        root: this._rootElement,\n        threshold: this._config.threshold,\n        rootMargin: this._config.rootMargin\n      };\n      return new IntersectionObserver(entries => this._observerCallback(entries), options);\n    }\n\n    // The logic of selection\n    _observerCallback(entries) {\n      const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n      const activate = entry => {\n        this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n        this._process(targetElement(entry));\n      };\n      const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n      const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n      this._previousScrollData.parentScrollTop = parentScrollTop;\n      for (const entry of entries) {\n        if (!entry.isIntersecting) {\n          this._activeTarget = null;\n          this._clearActiveClass(targetElement(entry));\n          continue;\n        }\n        const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n        // if we are scrolling down, pick the bigger offsetTop\n        if (userScrollsDown && entryIsLowerThanPrevious) {\n          activate(entry);\n          // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n          if (!parentScrollTop) {\n            return;\n          }\n          continue;\n        }\n\n        // if we are scrolling up, pick the smallest offsetTop\n        if (!userScrollsDown && !entryIsLowerThanPrevious) {\n          activate(entry);\n        }\n      }\n    }\n    _initializeTargetsAndObservables() {\n      this._targetLinks = new Map();\n      this._observableSections = new Map();\n      const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n      for (const anchor of targetLinks) {\n        // ensure that the anchor has an id and is not disabled\n        if (!anchor.hash || index_js.isDisabled(anchor)) {\n          continue;\n        }\n        const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n        // ensure that the observableSection exists & is visible\n        if (index_js.isVisible(observableSection)) {\n          this._targetLinks.set(decodeURI(anchor.hash), anchor);\n          this._observableSections.set(anchor.hash, observableSection);\n        }\n      }\n    }\n    _process(target) {\n      if (this._activeTarget === target) {\n        return;\n      }\n      this._clearActiveClass(this._config.target);\n      this._activeTarget = target;\n      target.classList.add(CLASS_NAME_ACTIVE);\n      this._activateParents(target);\n      EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n        relatedTarget: target\n      });\n    }\n    _activateParents(target) {\n      // Activate dropdown parents\n      if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n        SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE);\n        return;\n      }\n      for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n        // Set triggered links parents as active\n        // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n        for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n          item.classList.add(CLASS_NAME_ACTIVE);\n        }\n      }\n    }\n    _clearActiveClass(parent) {\n      parent.classList.remove(CLASS_NAME_ACTIVE);\n      const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent);\n      for (const node of activeNodes) {\n        node.classList.remove(CLASS_NAME_ACTIVE);\n      }\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = ScrollSpy.getOrCreateInstance(this, config);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config]();\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n      ScrollSpy.getOrCreateInstance(spy);\n    }\n  });\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(ScrollSpy);\n\n  return ScrollSpy;\n\n}));\n//# sourceMappingURL=scrollspy.js.map\n", "/*!\n  * Bootstrap tab.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tab = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));\n})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap tab.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'tab';\n  const DATA_KEY = 'bs.tab';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`;\n  const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;\n  const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`;\n  const ARROW_LEFT_KEY = 'ArrowLeft';\n  const ARROW_RIGHT_KEY = 'ArrowRight';\n  const ARROW_UP_KEY = 'ArrowUp';\n  const ARROW_DOWN_KEY = 'ArrowDown';\n  const HOME_KEY = 'Home';\n  const END_KEY = 'End';\n  const CLASS_NAME_ACTIVE = 'active';\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_DROPDOWN = 'dropdown';\n  const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';\n  const SELECTOR_DROPDOWN_MENU = '.dropdown-menu';\n  const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`;\n  const SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]';\n  const SELECTOR_OUTER = '.nav-item, .list-group-item';\n  const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]'; // TODO: could only be `tab` in v6\n  const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;\n  const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`;\n\n  /**\n   * Class definition\n   */\n\n  class Tab extends BaseComponent {\n    constructor(element) {\n      super(element);\n      this._parent = this._element.closest(SELECTOR_TAB_PANEL);\n      if (!this._parent) {\n        return;\n        // TODO: should throw exception in v6\n        // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n      }\n\n      // Set up initial aria attributes\n      this._setInitialAttributes(this._parent, this._getChildren());\n      EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));\n    }\n\n    // Getters\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    show() {\n      // Shows this elem and deactivate the active sibling if exists\n      const innerElem = this._element;\n      if (this._elemIsActive(innerElem)) {\n        return;\n      }\n\n      // Search for active tab on same parent to deactivate it\n      const active = this._getActiveElem();\n      const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE, {\n        relatedTarget: innerElem\n      }) : null;\n      const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, {\n        relatedTarget: active\n      });\n      if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {\n        return;\n      }\n      this._deactivate(active, innerElem);\n      this._activate(innerElem, active);\n    }\n\n    // Private\n    _activate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n      element.classList.add(CLASS_NAME_ACTIVE);\n      this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.add(CLASS_NAME_SHOW);\n          return;\n        }\n        element.removeAttribute('tabindex');\n        element.setAttribute('aria-selected', true);\n        this._toggleDropDown(element, true);\n        EventHandler.trigger(element, EVENT_SHOWN, {\n          relatedTarget: relatedElem\n        });\n      };\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));\n    }\n    _deactivate(element, relatedElem) {\n      if (!element) {\n        return;\n      }\n      element.classList.remove(CLASS_NAME_ACTIVE);\n      element.blur();\n      this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too\n\n      const complete = () => {\n        if (element.getAttribute('role') !== 'tab') {\n          element.classList.remove(CLASS_NAME_SHOW);\n          return;\n        }\n        element.setAttribute('aria-selected', false);\n        element.setAttribute('tabindex', '-1');\n        this._toggleDropDown(element, false);\n        EventHandler.trigger(element, EVENT_HIDDEN, {\n          relatedTarget: relatedElem\n        });\n      };\n      this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));\n    }\n    _keydown(event) {\n      if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) {\n        return;\n      }\n      event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n      event.preventDefault();\n      const children = this._getChildren().filter(element => !index_js.isDisabled(element));\n      let nextActiveElement;\n      if ([HOME_KEY, END_KEY].includes(event.key)) {\n        nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1];\n      } else {\n        const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);\n        nextActiveElement = index_js.getNextActiveElement(children, event.target, isNext, true);\n      }\n      if (nextActiveElement) {\n        nextActiveElement.focus({\n          preventScroll: true\n        });\n        Tab.getOrCreateInstance(nextActiveElement).show();\n      }\n    }\n    _getChildren() {\n      // collection of inner elements\n      return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);\n    }\n    _getActiveElem() {\n      return this._getChildren().find(child => this._elemIsActive(child)) || null;\n    }\n    _setInitialAttributes(parent, children) {\n      this._setAttributeIfNotExists(parent, 'role', 'tablist');\n      for (const child of children) {\n        this._setInitialAttributesOnChild(child);\n      }\n    }\n    _setInitialAttributesOnChild(child) {\n      child = this._getInnerElement(child);\n      const isActive = this._elemIsActive(child);\n      const outerElem = this._getOuterElement(child);\n      child.setAttribute('aria-selected', isActive);\n      if (outerElem !== child) {\n        this._setAttributeIfNotExists(outerElem, 'role', 'presentation');\n      }\n      if (!isActive) {\n        child.setAttribute('tabindex', '-1');\n      }\n      this._setAttributeIfNotExists(child, 'role', 'tab');\n\n      // set attributes to the related panel too\n      this._setInitialAttributesOnTargetPanel(child);\n    }\n    _setInitialAttributesOnTargetPanel(child) {\n      const target = SelectorEngine.getElementFromSelector(child);\n      if (!target) {\n        return;\n      }\n      this._setAttributeIfNotExists(target, 'role', 'tabpanel');\n      if (child.id) {\n        this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`);\n      }\n    }\n    _toggleDropDown(element, open) {\n      const outerElem = this._getOuterElement(element);\n      if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n        return;\n      }\n      const toggle = (selector, className) => {\n        const element = SelectorEngine.findOne(selector, outerElem);\n        if (element) {\n          element.classList.toggle(className, open);\n        }\n      };\n      toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);\n      toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW);\n      outerElem.setAttribute('aria-expanded', open);\n    }\n    _setAttributeIfNotExists(element, attribute, value) {\n      if (!element.hasAttribute(attribute)) {\n        element.setAttribute(attribute, value);\n      }\n    }\n    _elemIsActive(elem) {\n      return elem.classList.contains(CLASS_NAME_ACTIVE);\n    }\n\n    // Try to get the inner element (usually the .nav-link)\n    _getInnerElement(elem) {\n      return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);\n    }\n\n    // Try to get the outer element (usually the .nav-item)\n    _getOuterElement(elem) {\n      return elem.closest(SELECTOR_OUTER) || elem;\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Tab.getOrCreateInstance(this);\n        if (typeof config !== 'string') {\n          return;\n        }\n        if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n          throw new TypeError(`No method named \"${config}\"`);\n        }\n        data[config]();\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n    if (['A', 'AREA'].includes(this.tagName)) {\n      event.preventDefault();\n    }\n    if (index_js.isDisabled(this)) {\n      return;\n    }\n    Tab.getOrCreateInstance(this).show();\n  });\n\n  /**\n   * Initialize on focus\n   */\n  EventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n    for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n      Tab.getOrCreateInstance(element);\n    }\n  });\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Tab);\n\n  return Tab;\n\n}));\n//# sourceMappingURL=tab.js.map\n", "/*!\n  * Bootstrap toast.js v5.3.3 (https://getbootstrap.com/)\n  * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n  */\n(function (global, factory) {\n  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js'), require('./util/index.js')) :\n  typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions', './util/index'], factory) :\n  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Toast = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions, global.Index));\n})(this, (function (BaseComponent, EventHandler, componentFunctions_js, index_js) { 'use strict';\n\n  /**\n   * --------------------------------------------------------------------------\n   * Bootstrap toast.js\n   * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n   * --------------------------------------------------------------------------\n   */\n\n\n  /**\n   * Constants\n   */\n\n  const NAME = 'toast';\n  const DATA_KEY = 'bs.toast';\n  const EVENT_KEY = `.${DATA_KEY}`;\n  const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`;\n  const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`;\n  const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;\n  const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`;\n  const EVENT_HIDE = `hide${EVENT_KEY}`;\n  const EVENT_HIDDEN = `hidden${EVENT_KEY}`;\n  const EVENT_SHOW = `show${EVENT_KEY}`;\n  const EVENT_SHOWN = `shown${EVENT_KEY}`;\n  const CLASS_NAME_FADE = 'fade';\n  const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility\n  const CLASS_NAME_SHOW = 'show';\n  const CLASS_NAME_SHOWING = 'showing';\n  const DefaultType = {\n    animation: 'boolean',\n    autohide: 'boolean',\n    delay: 'number'\n  };\n  const Default = {\n    animation: true,\n    autohide: true,\n    delay: 5000\n  };\n\n  /**\n   * Class definition\n   */\n\n  class Toast extends BaseComponent {\n    constructor(element, config) {\n      super(element, config);\n      this._timeout = null;\n      this._hasMouseInteraction = false;\n      this._hasKeyboardInteraction = false;\n      this._setListeners();\n    }\n\n    // Getters\n    static get Default() {\n      return Default;\n    }\n    static get DefaultType() {\n      return DefaultType;\n    }\n    static get NAME() {\n      return NAME;\n    }\n\n    // Public\n    show() {\n      const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);\n      if (showEvent.defaultPrevented) {\n        return;\n      }\n      this._clearTimeout();\n      if (this._config.animation) {\n        this._element.classList.add(CLASS_NAME_FADE);\n      }\n      const complete = () => {\n        this._element.classList.remove(CLASS_NAME_SHOWING);\n        EventHandler.trigger(this._element, EVENT_SHOWN);\n        this._maybeScheduleHide();\n      };\n      this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated\n      index_js.reflow(this._element);\n      this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING);\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n    hide() {\n      if (!this.isShown()) {\n        return;\n      }\n      const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);\n      if (hideEvent.defaultPrevented) {\n        return;\n      }\n      const complete = () => {\n        this._element.classList.add(CLASS_NAME_HIDE); // @deprecated\n        this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW);\n        EventHandler.trigger(this._element, EVENT_HIDDEN);\n      };\n      this._element.classList.add(CLASS_NAME_SHOWING);\n      this._queueCallback(complete, this._element, this._config.animation);\n    }\n    dispose() {\n      this._clearTimeout();\n      if (this.isShown()) {\n        this._element.classList.remove(CLASS_NAME_SHOW);\n      }\n      super.dispose();\n    }\n    isShown() {\n      return this._element.classList.contains(CLASS_NAME_SHOW);\n    }\n\n    // Private\n\n    _maybeScheduleHide() {\n      if (!this._config.autohide) {\n        return;\n      }\n      if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n        return;\n      }\n      this._timeout = setTimeout(() => {\n        this.hide();\n      }, this._config.delay);\n    }\n    _onInteraction(event, isInteracting) {\n      switch (event.type) {\n        case 'mouseover':\n        case 'mouseout':\n          {\n            this._hasMouseInteraction = isInteracting;\n            break;\n          }\n        case 'focusin':\n        case 'focusout':\n          {\n            this._hasKeyboardInteraction = isInteracting;\n            break;\n          }\n      }\n      if (isInteracting) {\n        this._clearTimeout();\n        return;\n      }\n      const nextElement = event.relatedTarget;\n      if (this._element === nextElement || this._element.contains(nextElement)) {\n        return;\n      }\n      this._maybeScheduleHide();\n    }\n    _setListeners() {\n      EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));\n      EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));\n      EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));\n    }\n    _clearTimeout() {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    }\n\n    // Static\n    static jQueryInterface(config) {\n      return this.each(function () {\n        const data = Toast.getOrCreateInstance(this, config);\n        if (typeof config === 'string') {\n          if (typeof data[config] === 'undefined') {\n            throw new TypeError(`No method named \"${config}\"`);\n          }\n          data[config](this);\n        }\n      });\n    }\n  }\n\n  /**\n   * Data API implementation\n   */\n\n  componentFunctions_js.enableDismissTrigger(Toast);\n\n  /**\n   * jQuery\n   */\n\n  index_js.defineJQueryPlugin(Toast);\n\n  return Toast;\n\n}));\n//# sourceMappingURL=toast.js.map\n", "/** @odoo-module **/\n\nimport { compensateScrollbar, getScrollingElement } from \"@web/core/utils/scrolling\";\n\n/**\n * The bootstrap library extensions and fixes should be done here to avoid\n * patching in place.\n */\n\n/**\n * Review Bootstrap Sanitization: leave it enabled by default but extend it to\n * accept more common tag names like tables and buttons, and common attributes\n * such as style or data-. If a specific tooltip or popover must accept custom\n * tags or attributes, they must be supplied through the whitelist BS\n * parameter explicitely.\n *\n * We cannot disable sanitization because bootstrap uses tooltip/popover\n * DOM attributes in an \"unsafe\" way.\n */\nconst bsSanitizeAllowList = Tooltip.Default.allowList;\n\nbsSanitizeAllowList[\"*\"].push(\"title\", \"style\", /^data-[\\w-]+/);\n\nbsSanitizeAllowList.header = [];\nbsSanitizeAllowList.main = [];\nbsSanitizeAllowList.footer = [];\n\nbsSanitizeAllowList.caption = [];\nbsSanitizeAllowList.col = [\"span\"];\nbsSanitizeAllowList.colgroup = [\"span\"];\nbsSanitizeAllowList.table = [];\nbsSanitizeAllowList.thead = [];\nbsSanitizeAllowList.tbody = [];\nbsSanitizeAllowList.tfooter = [];\nbsSanitizeAllowList.tr = [];\nbsSanitizeAllowList.th = [\"colspan\", \"rowspan\"];\nbsSanitizeAllowList.td = [\"colspan\", \"rowspan\"];\n\nbsSanitizeAllowList.address = [];\nbsSanitizeAllowList.article = [];\nbsSanitizeAllowList.aside = [];\nbsSanitizeAllowList.blockquote = [];\nbsSanitizeAllowList.section = [];\n\nbsSanitizeAllowList.button = [\"type\"];\nbsSanitizeAllowList.del = [];\n\n/* Bootstrap tooltip defaults overwrite */\nTooltip.Default.placement = \"auto\";\nTooltip.Default.fallbackPlacement = [\"bottom\", \"right\", \"left\", \"top\"];\nTooltip.Default.html = true;\nTooltip.Default.trigger = \"hover\";\nTooltip.Default.container = \"body\";\nTooltip.Default.boundary = \"window\";\nTooltip.Default.delay = { show: 1000, hide: 0 };\n\nconst bootstrapShowFunction = Tooltip.prototype.show;\nTooltip.prototype.show = function () {\n    // Overwrite bootstrap tooltip method to prevent showing 2 tooltip at the\n    // same time\n    document.querySelectorAll(\".tooltip\").forEach((el) => el.remove());\n    const errorsToIgnore = [\"Please use show on visible elements\"];\n    try {\n        return bootstrapShowFunction.call(this);\n    } catch (error) {\n        if (errorsToIgnore.includes(error.message)) {\n            return 0;\n        }\n        throw error;\n    }\n};\n\n/**\n * Bootstrap disables dynamic dropdown positioning when it is in a navbar. Here\n * we make this patch to activate this dynamic navbar's dropdown positioning\n * which is useful to avoid that the elements of the website sub-menus overflow\n * the page.\n */\nDropdown.prototype._detectNavbar = function () {\n    return false;\n};\n\n/* Bootstrap modal scrollbar compensation on non-body */\nconst bsAdjustDialogFunction = Modal.prototype._adjustDialog;\nModal.prototype._adjustDialog = function () {\n    const document = this._element.ownerDocument;\n\n    this._scrollBar.reset();\n    document.body.classList.remove(\"modal-open\");\n\n    const scrollable = getScrollingElement(document);\n    if (document.body.contains(scrollable)) {\n        compensateScrollbar(scrollable, true);\n    }\n\n    this._scrollBar.hide();\n    document.body.classList.add(\"modal-open\");\n\n    return bsAdjustDialogFunction.apply(this, arguments);\n};\n\nconst bsResetAdjustmentsFunction = Modal.prototype._resetAdjustments;\nModal.prototype._resetAdjustments = function () {\n    const document = this._element.ownerDocument;\n\n    this._scrollBar.reset();\n    document.body.classList.remove(\"modal-open\");\n\n    const scrollable = getScrollingElement(document);\n    if (document.body.contains(scrollable)) {\n        compensateScrollbar(scrollable, false);\n    }\n    return bsResetAdjustmentsFunction.apply(this, arguments);\n};\n", "/** @odoo-module **/\n\n/**\n * The jquery library extensions and fixes should be done here to avoid patching\n * in place.\n */\n\n// jQuery selectors extensions\n$.extend($.expr[':'], {\n    data: function (element, index, matches) {\n        return $(element).data(matches[3]);\n    },\n});\n\n// jQuery functions extensions\n$.fn.extend({\n    /**\n     * Makes DOM elements bounce the way Odoo decided it.\n     *\n     * @param {string} [extraClass]\n     */\n    odooBounce: function (extraClass) {\n        for (const el of this) {\n            el.classList.add('o_catch_attention', extraClass);\n            setTimeout(() => el.classList.remove('o_catch_attention', extraClass), 400);\n        }\n        return this;\n    },\n    /**\n     * Allows to bind events to a handler just as the standard `$.on` function\n     * but binds the handler so that it is executed before any already-attached\n     * handler for the same events.\n     *\n     * @see jQuery.on\n     */\n    prependEvent: function (events, selector, data, handler) {\n        this.on.apply(this, arguments);\n\n        events = events.split(' ');\n        return this.each(function () {\n            var el = this;\n            events.forEach((evNameNamespaced) => {\n                var evName = evNameNamespaced.split('.')[0];\n                var handler = $._data(el, 'events')[evName].pop();\n                $._data(el, 'events')[evName].unshift(handler);\n            });\n        });\n    },\n    /**\n     * @deprecated this will soon be removed: just rely on the fact that the\n     * scrollbar is at its natural position.\n     * @returns {jQuery}\n     */\n    getScrollingElement(document = window.document) {\n        const $baseScrollingElement = $(document.scrollingElement);\n        if ($baseScrollingElement.isScrollable()\n                && $baseScrollingElement.hasScrollableContent()) {\n            return $baseScrollingElement;\n        }\n        const bodyHeight = $(document.body).height();\n        for (const el of document.body.children) {\n            // Search for a body child which is at least as tall as the body\n            // and which has the ability to scroll if enough content in it. If\n            // found, suppose this is the top scrolling element.\n            if (bodyHeight - el.scrollHeight > 1.5) {\n                continue;\n            }\n            const $el = $(el);\n            if ($el.isScrollable()) {\n                return $el;\n            }\n        }\n        return $baseScrollingElement;\n    },\n    /**\n     * @deprecated this will soon be removed: just rely on the fact that the\n     * scrollbar is at its natural position.\n     * @returns {jQuery}\n     */\n    getScrollingTarget(contextItem = window.document) {\n        // Cannot use `instanceof` because of cross-frame issues.\n        const isElement = obj => obj && obj.nodeType === Node.ELEMENT_NODE;\n        const isJQuery = obj => obj && ('jquery' in obj);\n\n        const $scrollingElement = isElement(contextItem)\n            ? $(contextItem)\n            : isJQuery(contextItem)\n            ? contextItem\n            : $().getScrollingElement(contextItem);\n        const document = $scrollingElement[0].ownerDocument;\n        return $scrollingElement.is(document.scrollingElement)\n            ? $(document.defaultView)\n            : $scrollingElement;\n    },\n    /**\n     * @return {boolean}\n     */\n    hasScrollableContent() {\n        return this[0].scrollHeight > this[0].clientHeight;\n    },\n    /**\n     * @returns {boolean}\n     */\n    isScrollable() {\n        if (!this.length) {\n            return false;\n        }\n        const overflow = this.css('overflow-y');\n        const el = this[0];\n        return overflow === 'auto' || overflow === 'scroll'\n            || (overflow === 'visible' && el === el.ownerDocument.scrollingElement);\n    },\n});\n\n// jQuery functions monkey-patching\n\n// Some magic to ensure scrollTop and animate on html/body animate the top level\n// scrollable element even if not html or body. Note: we should consider\n// removing this as it was only really needed when the #wrapwrap was the one\n// with the scrollbar. Although the rest of the code still use\n// getScrollingElement to be generic so this is consistent. Maybe all of this\n// can live on as long as we continue using jQuery a lot. We can decide of the\n// fate of getScrollingElement and related code the moment we get rid of jQuery.\nconst originalScrollTop = $.fn.scrollTop;\n$.fn.scrollTop = function (value) {\n    if (value !== undefined && this.filter('html, body').length) {\n        // The caller wants to scroll a set of elements including html and/or\n        // body to a specific point -> do that but make sure to add the real\n        // top level element to that set of elements if any different is found.\n        const $withRealScrollable = this.not('html, body').add($().getScrollingElement(this[0].ownerDocument));\n        originalScrollTop.apply($withRealScrollable, arguments);\n        return this;\n    } else if (value === undefined && this.eq(0).is('html, body')) {\n        // The caller wants to get the scroll point of a set of elements, jQuery\n        // will return the scroll point of the first one, if it is html or body\n        // return the scroll point of the real top level element.\n        return originalScrollTop.apply($().getScrollingElement(this[0].ownerDocument), arguments);\n    }\n    return originalScrollTop.apply(this, arguments);\n};\nconst originalAnimate = $.fn.animate;\n$.fn.animate = function (properties, ...rest) {\n    const props = Object.assign({}, properties);\n    if ('scrollTop' in props && this.filter('html, body').length) {\n        // The caller wants to scroll a set of elements including html and/or\n        // body to a specific point -> do that but make sure to add the real\n        // top level element to that set of elements if any different is found.\n        const $withRealScrollable = this.not('html, body').add($().getScrollingElement(this[0].ownerDocument));\n        originalAnimate.call($withRealScrollable, {'scrollTop': props['scrollTop']}, ...rest);\n        delete props['scrollTop'];\n    }\n    if (!Object.keys(props).length) {\n        return this;\n    }\n    return originalAnimate.call(this, props, ...rest);\n};\n", "/**\n * Improved John Resig's inheritance, based on:\n *\n * Simple JavaScript Inheritance\n * By John Resig http://ejohn.org/\n * MIT Licensed.\n *\n * Adds \"include()\"\n *\n * Defines The Class object. That object can be used to define and inherit classes using\n * the extend() method.\n *\n * Example::\n *\n *     var Person = Class.extend({\n *      init: function(isDancing){\n *         this.dancing = isDancing;\n *       },\n *       dance: function(){\n *         return this.dancing;\n *       }\n *     });\n *\n * The init() method act as a constructor. This class can be instanced this way::\n *\n *     var person = new Person(true);\n *     person.dance();\n *\n *     The Person class can also be extended again:\n *\n *     var Ninja = Person.extend({\n *       init: function(){\n *         this._super( false );\n *       },\n *       dance: function(){\n *         // Call the inherited version of dance()\n *         return this._super();\n *       },\n *       swingSword: function(){\n *         return true;\n *       }\n *     });\n *\n * When extending a class, each re-defined method can use this._super() to call the previous\n * implementation of that method.\n *\n * @class Class\n */\nfunction OdooClass(){}\n\nvar initializing = false;\n// eslint-disable-next-line no-undef\nvar fnTest = /xyz/.test(function(){xyz();}) ? /\\b_super\\b/ : /.*/;\n\n/**\n * Subclass an existing class\n *\n * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class\n */\nOdooClass.extend = function() {\n    var _super = this.prototype;\n    // Support mixins arguments\n    var args = [...arguments];\n    args.unshift({});\n\n    const prop = {};\n    args.forEach((arg) => {\n        Object.assign(prop, arg);\n    });\n\n    // Instantiate a web class (but only create the instance,\n    // don't run the init constructor)\n    initializing = true;\n    var This = this;\n    var prototype = new This();\n    initializing = false;\n\n    // Copy the properties over onto the new prototype\n    Object.keys(prop).forEach((name) => {\n        // Check if we're overwriting an existing function\n        prototype[name] = typeof prop[name] == \"function\" &&\n                          fnTest.test(prop[name]) ?\n                (function(name, fn) {\n                    return function() {\n                        var tmp = this._super;\n\n                        // Add a new ._super() method that is the same\n                        // method but on the super-class\n                        this._super = _super[name];\n\n                        // The method only need to be bound temporarily, so\n                        // we remove it when we're done executing\n                        var ret = fn.apply(this, arguments);\n                        this._super = tmp;\n\n                        return ret;\n                    };\n                })(name, prop[name]) :\n                prop[name];\n    });\n\n    // The dummy class constructor\n    function Class() {\n        if(this.constructor !== OdooClass){\n            throw new Error(\"You can only instanciate objects with the 'new' operator\");\n        }\n        // All construction is actually done in the init method\n        this._super = null;\n        if (!initializing && this.init) {\n            var ret = this.init.apply(this, arguments);\n            if (ret) { return ret; }\n        }\n        return this;\n    }\n    Class.include = function (properties) {\n        Object.keys(properties).forEach((name) => {\n            if (typeof properties[name] !== 'function'\n                    || !fnTest.test(properties[name])) {\n                prototype[name] = properties[name];\n            } else if (typeof prototype[name] === 'function'\n                       && prototype.hasOwnProperty(name)) {\n                prototype[name] = (function (name, fn, previous) {\n                    return function () {\n                        var tmp = this._super;\n                        this._super = previous;\n                        var ret = fn.apply(this, arguments);\n                        this._super = tmp;\n                        return ret;\n                    };\n                })(name, properties[name], prototype[name]);\n            } else if (typeof _super[name] === 'function') {\n                prototype[name] = (function (name, fn) {\n                    return function () {\n                        var tmp = this._super;\n                        this._super = _super[name];\n                        var ret = fn.apply(this, arguments);\n                        this._super = tmp;\n                        return ret;\n                    };\n                })(name, properties[name]);\n            }\n        });\n    };\n\n    // Populate our constructed prototype object\n    Class.prototype = prototype;\n\n    // Enforce the constructor to be what we expect\n    Class.constructor = Class;\n\n    // And make this class extendable\n    Class.extend = this.extend;\n\n    return Class;\n};\n\nexport default OdooClass;\n", "import { App, EventBus } from \"@odoo/owl\";\nimport { SERVICES_METADATA } from \"@web/core/utils/hooks\";\nimport { registry } from \"@web/core/registry\";\nimport { getTemplate } from \"@web/core/templates\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { session } from \"@web/session\";\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @typedef {Object} OdooEnv\n * @property {import(\"services\").Services} services\n * @property {EventBus} bus\n * @property {string} debug\n * @property {(str: string) => string} _t\n * @property {boolean} [isSmall]\n */\n\n// -----------------------------------------------------------------------------\n// makeEnv\n// -----------------------------------------------------------------------------\n\n/**\n * Return a value Odoo Env object\n *\n * @returns {OdooEnv}\n */\nexport function makeEnv() {\n    return {\n        bus: new EventBus(),\n        services: {},\n        debug: odoo.debug,\n        get isSmall() {\n            throw new Error(\"UI service not initialized!\");\n        },\n    };\n}\n\n// -----------------------------------------------------------------------------\n// Service Launcher\n// -----------------------------------------------------------------------------\n\nconst serviceRegistry = registry.category(\"services\");\n\nserviceRegistry.addValidation({\n    start: Function,\n    dependencies: { type: Array, element: String, optional: true },\n    async: { type: [{ type: Array, element: String }, { value: true }], optional: true },\n    \"*\": true,\n});\n\nlet startServicesPromise = null;\n\n/**\n * Start all services registered in the service registry, while making sure\n * each service dependencies are properly fulfilled.\n *\n * @param {OdooEnv} env\n * @returns {Promise<void>}\n */\nexport async function startServices(env) {\n    // Wait for all synchronous code so that if new services that depend on\n    // one another are added to the registry, they're all present before we\n    // start them regardless of the order they're added to the registry.\n    await Promise.resolve();\n\n    const toStart = new Map();\n    serviceRegistry.addEventListener(\"UPDATE\", async (ev) => {\n        // Wait for all synchronous code so that if new services that depend on\n        // one another are added to the registry, they're all present before we\n        // start them regardless of the order they're added to the registry.\n        await Promise.resolve();\n        const { operation, key: name, value: service } = ev.detail;\n        if (operation === \"delete\") {\n            // We hardly see why it would be usefull to remove a service.\n            // Furthermore we could encounter problems with dependencies.\n            // Keep it simple!\n            return;\n        }\n        if (toStart.size) {\n            const namedService = Object.assign(Object.create(service), { name });\n            toStart.set(name, namedService);\n        } else {\n            await _startServices(env, toStart);\n        }\n    });\n    await _startServices(env, toStart);\n}\n\nasync function _startServices(env, toStart) {\n    if (startServicesPromise) {\n        return startServicesPromise.then(() => _startServices(env, toStart));\n    }\n    const services = env.services;\n    for (const [name, service] of serviceRegistry.getEntries()) {\n        if (!(name in services)) {\n            const namedService = Object.assign(Object.create(service), { name });\n            toStart.set(name, namedService);\n        }\n    }\n\n    // start as many services in parallel as possible\n    async function start() {\n        let service = null;\n        const proms = [];\n        while ((service = findNext())) {\n            const name = service.name;\n            toStart.delete(name);\n            const entries = (service.dependencies || []).map((dep) => [dep, services[dep]]);\n            const dependencies = Object.fromEntries(entries);\n            if (name in services) {\n                continue;\n            }\n            const value = service.start(env, dependencies);\n            if (\"async\" in service) {\n                SERVICES_METADATA[name] = service.async;\n            }\n            proms.push(\n                Promise.resolve(value).then((val) => {\n                    services[name] = val || null;\n                })\n            );\n        }\n        await Promise.all(proms);\n        if (proms.length) {\n            return start();\n        }\n    }\n    startServicesPromise = start().finally(() => {\n        startServicesPromise = null;\n    });\n    await startServicesPromise;\n    if (toStart.size) {\n        const missingDeps = new Set();\n        for (const service of toStart.values()) {\n            for (const dependency of service.dependencies) {\n                if (!(dependency in services) && !toStart.has(dependency)) {\n                    missingDeps.add(dependency);\n                }\n            }\n        }\n        const depNames = [...missingDeps].join(\", \");\n        throw new Error(\n            `Some services could not be started: ${[\n                ...toStart.keys(),\n            ]}. Missing dependencies: ${depNames}`\n        );\n    }\n\n    function findNext() {\n        for (const s of toStart.values()) {\n            if (s.dependencies) {\n                if (s.dependencies.every((d) => d in services)) {\n                    return s;\n                }\n            } else {\n                return s;\n            }\n        }\n        return null;\n    }\n}\n\n/**\n * Create an application with a given component as root and mount it. If no env\n * is provided, the application will be treated as a \"root\": an env will be\n * created and the services will be started, it will also be set as the root\n * in `__WOWL_DEBUG__`\n *\n * @param {import(\"@odoo/owl\").Component} component the component to mount\n * @param {HTMLElement} target the HTML element in which to mount the app\n * @param {Partial<ConstructorParameters<typeof App>[1]>} [appConfig] object\n *  containing a (partial) config for the app.\n */\nexport async function mountComponent(component, target, appConfig = {}) {\n    let { env } = appConfig;\n    const isRoot = !env;\n    if (isRoot) {\n        env = await makeEnv();\n        await startServices(env);\n    }\n    const app = new App(component, {\n        env,\n        getTemplate,\n        dev: env.debug || session.test_mode,\n        warnIfNoStaticProps: !session.test_mode,\n        name: component.constructor.name,\n        translatableAttributes: [\"data-tooltip\"],\n        translateFn: _t,\n        ...appConfig,\n    });\n    const root = await app.mount(target);\n    if (isRoot) {\n        odoo.__WOWL_DEBUG__ = { root };\n    }\n    return app;\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { localization } from \"@web/core/l10n/localization\";\nimport { clamp } from \"@web/core/utils/numbers\";\n\nimport { Component, onMounted, onWillUnmount, useRef, useState } from \"@odoo/owl\";\nimport { Deferred } from \"@web/core/utils/concurrency\";\n\nconst isScrollSwipable = (scrollables) => {\n    return {\n        left: !scrollables.filter((e) => e.scrollLeft !== 0).length,\n        right: !scrollables.filter(\n            (e) => e.scrollLeft + Math.round(e.getBoundingClientRect().width) !== e.scrollWidth\n        ).length,\n    };\n};\n\n/**\n * Action Swiper\n *\n * This component is intended to perform action once a user has completed a touch swipe.\n * You can choose the direction allowed for such behavior (left, right or both).\n * The action to perform must be passed as a props. It is possible to define a condition\n * to allow the swipe interaction conditionnally.\n * @extends Component\n */\nexport class ActionSwiper extends Component {\n    static template = \"web.ActionSwiper\";\n    static props = {\n        onLeftSwipe: {\n            type: Object,\n            args: {\n                action: Function,\n                icon: String,\n                bgColor: String,\n            },\n            optional: true,\n        },\n        onRightSwipe: {\n            type: Object,\n            args: {\n                action: Function,\n                icon: String,\n                bgColor: String,\n            },\n            optional: true,\n        },\n        slots: Object,\n        animationOnMove: { type: Boolean, optional: true },\n        animationType: { type: String, optional: true },\n        swipeDistanceRatio: { type: Number, optional: true },\n        swipeInvalid: { type: Function, optional: true },\n    };\n\n    static defaultProps = {\n        onLeftSwipe: undefined,\n        onRightSwipe: undefined,\n        animationOnMove: true,\n        animationType: \"bounce\",\n        swipeDistanceRatio: 2,\n    };\n\n    setup() {\n        this.actionTimeoutId = null;\n        this.resetTimeoutId = null;\n        this.defaultState = {\n            containerStyle: \"\",\n            isSwiping: false,\n            width: undefined,\n        };\n        this.root = useRef(\"root\");\n        this.targetContainer = useRef(\"targetContainer\");\n        this.state = useState({ ...this.defaultState });\n        this.scrollables = undefined;\n        this.startX = undefined;\n        this.swipedDistance = 0;\n        this.isScrollValidated = false;\n        onMounted(() => {\n            if (this.targetContainer.el) {\n                this.state.width = this.targetContainer.el.getBoundingClientRect().width;\n            }\n            // Forward classes set on component to slot, as we only want to wrap an\n            // existing component without altering the DOM structure any more than\n            // strictly necessary\n            if (this.props.onLeftSwipe || this.props.onRightSwipe) {\n                const classes = new Set(this.root.el.classList);\n                classes.delete(\"o_actionswiper\");\n                for (const className of classes) {\n                    this.targetContainer.el.firstChild.classList.add(className);\n                    this.root.el.classList.remove(className);\n                }\n            }\n        });\n        onWillUnmount(() => {\n            browser.clearTimeout(this.actionTimeoutId);\n            browser.clearTimeout(this.resetTimeoutId);\n        });\n    }\n    get localizedProps() {\n        return {\n            onLeftSwipe:\n                localization.direction === \"rtl\" ? this.props.onRightSwipe : this.props.onLeftSwipe,\n            onRightSwipe:\n                localization.direction === \"rtl\" ? this.props.onLeftSwipe : this.props.onRightSwipe,\n        };\n    }\n\n    /**\n     * @private\n     * @param {TouchEvent} ev\n     */\n    _onTouchEndSwipe() {\n        if (this.state.isSwiping) {\n            this.state.isSwiping = false;\n            if (\n                this.localizedProps.onRightSwipe &&\n                this.swipedDistance > this.state.width / this.props.swipeDistanceRatio\n            ) {\n                this.swipedDistance = this.state.width;\n                this.handleSwipe(this.localizedProps.onRightSwipe.action);\n            } else if (\n                this.localizedProps.onLeftSwipe &&\n                this.swipedDistance < -this.state.width / this.props.swipeDistanceRatio\n            ) {\n                this.swipedDistance = -this.state.width;\n                this.handleSwipe(this.localizedProps.onLeftSwipe.action);\n            } else {\n                this.state.containerStyle = \"\";\n            }\n        }\n    }\n    /**\n     * @private\n     * @param {TouchEvent} ev\n     */\n    _onTouchMoveSwipe(ev) {\n        if (this.state.isSwiping) {\n            if (this.props.swipeInvalid && this.props.swipeInvalid()) {\n                this.state.isSwiping = false;\n                return;\n            }\n            const { onLeftSwipe, onRightSwipe } = this.localizedProps;\n            this.swipedDistance = clamp(\n                ev.touches[0].clientX - this.startX,\n                onLeftSwipe ? -this.state.width : 0,\n                onRightSwipe ? this.state.width : 0\n            );\n            // Prevent the browser to navigate back/forward when using swipe\n            // gestures while still allowing to scroll vertically.\n            if (Math.abs(this.swipedDistance) > 40) {\n                ev.preventDefault();\n            }\n            // If there are scrollable elements under touch pressure,\n            // they must be at their limits to allow swiping.\n            if (\n                !this.isScrollValidated &&\n                this.scrollables &&\n                !isScrollSwipable(this.scrollables)[this.swipedDistance > 0 ? \"left\" : \"right\"]\n            ) {\n                return this._reset();\n            }\n            this.isScrollValidated = true;\n\n            if (this.props.animationOnMove) {\n                this.state.containerStyle = `transform: translateX(${this.swipedDistance}px)`;\n            }\n        }\n    }\n    /**\n     * @private\n     * @param {TouchEvent} ev\n     */\n    _onTouchStartSwipe(ev) {\n        this.scrollables = ev\n            .composedPath()\n            .filter(\n                (e) =>\n                    e.nodeType === 1 &&\n                    this.targetContainer.el.contains(e) &&\n                    e.scrollWidth > e.getBoundingClientRect().width &&\n                    [\"auto\", \"scroll\"].includes(window.getComputedStyle(e)[\"overflow-x\"])\n            );\n        if (!this.state.width) {\n            this.state.width =\n                this.targetContainer && this.targetContainer.el.getBoundingClientRect().width;\n        }\n        this.state.isSwiping = true;\n        this.isScrollValidated = false;\n        this.startX = ev.touches[0].clientX;\n    }\n\n    /**\n     * @private\n     */\n    _reset() {\n        Object.assign(this.state, { ...this.defaultState });\n        this.scrollables = undefined;\n        this.startX = undefined;\n        this.swipedDistance = 0;\n        this.isScrollValidated = false;\n    }\n\n    handleSwipe(action) {\n        if (this.props.animationType === \"bounce\") {\n            this.state.containerStyle = `transform: translateX(${this.swipedDistance}px)`;\n            this.actionTimeoutId = browser.setTimeout(async () => {\n                await action(Promise.resolve());\n                this._reset();\n            }, 500);\n        } else if (this.props.animationType === \"forwards\") {\n            this.state.containerStyle = `transform: translateX(${this.swipedDistance}px)`;\n            this.actionTimeoutId = browser.setTimeout(async () => {\n                const prom = new Deferred();\n                await action(prom);\n                this.state.isSwiping = true;\n                this.state.containerStyle = `transform: translateX(${-this.swipedDistance}px)`;\n                this.resetTimeoutId = browser.setTimeout(() => {\n                    prom.resolve();\n                    this._reset();\n                }, 100);\n            }, 100);\n        } else {\n            return action(Promise.resolve());\n        }\n    }\n}\n", "import { browser } from \"./browser/browser\";\n\nbrowser.addEventListener(\"click\", (ev) => {\n    const href = ev.target.closest(\"a\")?.getAttribute(\"href\");\n    if (href && href === \"#\") {\n        ev.preventDefault(); // single hash in href are just a way to activate A-tags node\n        return;\n    }\n});\n", "import { Component, onWillStart, whenReady, xml } from \"@odoo/owl\";\nimport { session } from \"@web/session\";\nimport { registry } from \"./registry\";\n\n/**\n * @typedef {{\n *  cssLibs: string[];\n *  jsLibs: string[];\n * }} BundleFileNames\n */\n\nconst computeCacheMap = () => {\n    for (const script of document.head.querySelectorAll(\"script[src]\")) {\n        cacheMap.set(script.src, Promise.resolve());\n    }\n    for (const link of document.head.querySelectorAll(\"link[rel=stylesheet][href]\")) {\n        cacheMap.set(link.href, Promise.resolve());\n    }\n};\n\n/**\n * @param {HTMLLinkElement | HTMLScriptElement} el\n * @param {(event: Event) => any} onLoad\n * @param {(error: Error) => any} onError\n */\nconst onLoadAndError = (el, onLoad, onError) => {\n    const onLoadListener = (event) => {\n        removeListeners();\n        onLoad(event);\n    };\n\n    const onErrorListener = (error) => {\n        removeListeners();\n        onError(error);\n    };\n\n    const removeListeners = () => {\n        el.removeEventListener(\"load\", onLoadListener);\n        el.removeEventListener(\"error\", onErrorListener);\n    };\n\n    el.addEventListener(\"load\", onLoadListener);\n    el.addEventListener(\"error\", onErrorListener);\n};\n\n/** @type {Map<string, Promise<BundleFileNames | void>>} */\nconst cacheMap = new Map();\n\nwhenReady(computeCacheMap);\n\n/** @type {typeof assets[\"getBundle\"]} */\nexport function getBundle() {\n    return assets.getBundle(...arguments);\n}\n\n/** @type {typeof assets[\"loadBundle\"]} */\nexport function loadBundle() {\n    return assets.loadBundle(...arguments);\n}\n\n/** @type {typeof assets[\"loadJS\"]} */\nexport function loadJS() {\n    return assets.loadJS(...arguments);\n}\n\n/** @type {typeof assets[\"loadCSS\"]} */\nexport function loadCSS() {\n    return assets.loadCSS(...arguments);\n}\n\nexport class AssetsLoadingError extends Error {}\n\n/**\n * Utility component that loads an asset bundle before instanciating a component\n */\nexport class LazyComponent extends Component {\n    static template = xml`<t t-component=\"Component\" t-props=\"props.props\"/>`;\n    static props = {\n        Component: String,\n        bundle: String,\n        props: { type: Object, optional: true },\n    };\n    setup() {\n        onWillStart(async () => {\n            await loadBundle(this.props.bundle);\n            this.Component = registry.category(\"lazy_components\").get(this.props.Component);\n        });\n    }\n}\n\n/**\n * This export is done only in order to modify the behavior of the exported\n * functions. This is done in order to be able to make a test environment.\n * Modules should only use the methods exported below.\n */\nexport const assets = {\n    retries: {\n        count: 3,\n        delay: 5000,\n        extraDelay: 2500,\n    },\n\n    /**\n     * Get the files information as descriptor object from a public asset template.\n     *\n     * @param {string} bundleName Name of the bundle containing the list of files\n     * @returns {Promise<BundleFileNames>}\n     */\n    getBundle(bundleName) {\n        if (cacheMap.has(bundleName)) {\n            return cacheMap.get(bundleName);\n        }\n        const url = new URL(`/web/bundle/${bundleName}`, location.origin);\n        for (const [key, value] of Object.entries(session.bundle_params || {})) {\n            url.searchParams.set(key, value);\n        }\n        const promise = fetch(url)\n            .then(async (response) => {\n                const cssLibs = [];\n                const jsLibs = [];\n                if (!response.bodyUsed) {\n                    const result = await response.json();\n                    for (const { src, type } of Object.values(result)) {\n                        if (type === \"link\" && src) {\n                            cssLibs.push(src);\n                        } else if (type === \"script\" && src) {\n                            jsLibs.push(src);\n                        }\n                    }\n                }\n                return { cssLibs, jsLibs };\n            })\n            .catch((reason) => {\n                cacheMap.delete(bundleName);\n                throw reason;\n            });\n        cacheMap.set(bundleName, promise);\n        return promise;\n    },\n\n    /**\n     * Loads the given js/css libraries and asset bundles. Note that no library or\n     * asset will be loaded if it was already done before.\n     *\n     * @param {string} bundleName\n     * @returns {Promise<void[]>}\n     */\n    loadBundle(bundleName) {\n        if (typeof bundleName !== \"string\") {\n            throw new Error(\n                `loadBundle(bundleName:string) accepts only bundleName argument as a string ! Not ${JSON.stringify(\n                    bundleName\n                )} as ${typeof bundleName}`\n            );\n        }\n        return getBundle(bundleName).then(({ cssLibs, jsLibs }) =>\n            Promise.all([...cssLibs.map(loadCSS), ...jsLibs.map(loadJS)])\n        );\n    },\n\n    /**\n     * Loads the given url as a stylesheet.\n     *\n     * @param {string} url the url of the stylesheet\n     * @param {number} [retryCount]\n     * @returns {Promise<void>} resolved when the stylesheet has been loaded\n     */\n    loadCSS(url, retryCount = 0) {\n        if (cacheMap.has(url)) {\n            return cacheMap.get(url);\n        }\n        const linkEl = document.createElement(\"link\");\n        linkEl.type = \"text/css\";\n        linkEl.rel = \"stylesheet\";\n        linkEl.href = url;\n        const promise = new Promise((resolve, reject) =>\n            onLoadAndError(linkEl, resolve, async () => {\n                cacheMap.delete(url);\n                if (retryCount < assets.retries.count) {\n                    const delay = assets.retries.delay + assets.retries.extraDelay * retryCount;\n                    await new Promise((res) => setTimeout(res, delay));\n                    linkEl.remove();\n                    loadCSS(url, retryCount + 1)\n                        .then(resolve)\n                        .catch((reason) => {\n                            cacheMap.delete(url);\n                            reject(reason);\n                        });\n                } else {\n                    reject(new AssetsLoadingError(`The loading of ${url} failed`));\n                }\n            })\n        );\n        cacheMap.set(url, promise);\n        document.head.appendChild(linkEl);\n        return promise;\n    },\n\n    /**\n     * Loads the given url inside a script tag.\n     *\n     * @param {string} url the url of the script\n     * @returns {Promise<void>} resolved when the script has been loaded\n     */\n    loadJS(url) {\n        if (cacheMap.has(url)) {\n            return cacheMap.get(url);\n        }\n        const scriptEl = document.createElement(\"script\");\n        scriptEl.type = url.includes(\"web/static/lib/pdfjs/\") ? \"module\" : \"text/javascript\";\n        scriptEl.src = url;\n        const promise = new Promise((resolve, reject) =>\n            onLoadAndError(scriptEl, resolve, () => {\n                cacheMap.delete(url);\n                reject(new AssetsLoadingError(`The loading of ${url} failed`));\n            })\n        );\n        cacheMap.set(url, promise);\n        document.head.appendChild(scriptEl);\n        return promise;\n    },\n};\n", "import { Deferred } from \"@web/core/utils/concurrency\";\nimport { useAutofocus, useForwardRefToParent, useService } from \"@web/core/utils/hooks\";\nimport { useDebounced } from \"@web/core/utils/timing\";\nimport { getActiveHotkey } from \"@web/core/hotkeys/hotkey_service\";\nimport { usePosition } from \"@web/core/position/position_hook\";\nimport { Component, onWillUpdateProps, useExternalListener, useRef, useState } from \"@odoo/owl\";\n\nexport class AutoComplete extends Component {\n    static template = \"web.AutoComplete\";\n    static props = {\n        value: { type: String, optional: true },\n        id: { type: String, optional: true },\n        onSelect: { type: Function },\n        sources: {\n            type: Array,\n            element: {\n                type: Object,\n                shape: {\n                    placeholder: { type: String, optional: true },\n                    optionTemplate: { type: String, optional: true },\n                    options: [Array, Function],\n                },\n            },\n        },\n        placeholder: { type: String, optional: true },\n        autoSelect: { type: Boolean, optional: true },\n        resetOnSelect: { type: Boolean, optional: true },\n        onInput: { type: Function, optional: true },\n        onCancel: { type: Function, optional: true },\n        onChange: { type: Function, optional: true },\n        onBlur: { type: Function, optional: true },\n        onFocus: { type: Function, optional: true },\n        input: { type: Function, optional: true },\n        dropdown: { type: Boolean, optional: true },\n        autofocus: { type: Boolean, optional: true },\n        class: { type: String, optional: true },\n    };\n    static defaultProps = {\n        value: \"\",\n        placeholder: \"\",\n        autoSelect: false,\n        dropdown: true,\n        onInput: () => {},\n        onCancel: () => {},\n        onChange: () => {},\n        onBlur: () => {},\n        onFocus: () => {},\n    };\n\n    setup() {\n        this.nextSourceId = 0;\n        this.nextOptionId = 0;\n        this.sources = [];\n        this.inEdition = false;\n        this.timeout = 250;\n\n        this.state = useState({\n            navigationRev: 0,\n            optionsRev: 0,\n            open: false,\n            activeSourceOption: null,\n            value: this.props.value,\n        });\n\n        this.inputRef = useForwardRefToParent(\"input\");\n        if (this.props.autofocus) {\n            useAutofocus({ refName: \"input\" });\n        }\n        this.root = useRef(\"root\");\n\n        this.debouncedProcessInput = useDebounced(async () => {\n            const currentPromise = this.pendingPromise;\n            this.pendingPromise = null;\n            this.props.onInput({\n                inputValue: this.inputRef.el.value,\n            });\n            try {\n                await this.open(true);\n                currentPromise.resolve();\n            } catch {\n                currentPromise.reject();\n            } finally {\n                if (currentPromise === this.loadingPromise) {\n                    this.loadingPromise = null;\n                }\n            }\n        }, this.timeout);\n\n        useExternalListener(window, \"scroll\", this.externalClose, true);\n        useExternalListener(window, \"pointerdown\", this.externalClose, true);\n\n        this.hotkey = useService(\"hotkey\");\n        this.hotkeysToRemove = [];\n\n        onWillUpdateProps((nextProps) => {\n            if (this.props.value !== nextProps.value || this.forceValFromProp) {\n                this.forceValFromProp = false;\n                if (!this.inEdition) {\n                    this.state.value = nextProps.value;\n                    this.inputRef.el.value = nextProps.value;\n                }\n                this.close();\n            }\n        });\n\n        // position and size\n        if (this.props.dropdown) {\n            usePosition(\"sourcesList\", () => this.targetDropdown, this.dropdownOptions);\n        } else {\n            this.open(false);\n        }\n    }\n\n    get targetDropdown() {\n        return this.inputRef.el;\n    }\n\n    get activeSourceOptionId() {\n        if (!this.isOpened || !this.state.activeSourceOption) {\n            return undefined;\n        }\n        const [sourceIndex, optionIndex] = this.state.activeSourceOption;\n        const source = this.sources[sourceIndex];\n        return `${this.props.id || \"autocomplete\"}_${sourceIndex}_${\n            source.isLoading ? \"loading\" : optionIndex\n        }`;\n    }\n\n    get dropdownOptions() {\n        return {\n            position: \"bottom-start\",\n        };\n    }\n\n    get isOpened() {\n        return this.state.open;\n    }\n\n    get hasOptions() {\n        for (const source of this.sources) {\n            if (source.isLoading || source.options.length) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    open(useInput = false) {\n        this.state.open = true;\n        return this.loadSources(useInput);\n    }\n\n    close() {\n        this.state.open = false;\n        this.state.activeSourceOption = null;\n    }\n\n    cancel() {\n        if (this.inputRef.el.value.length) {\n            if (this.props.autoSelect) {\n                this.inputRef.el.value = this.props.value;\n                this.props.onCancel();\n            }\n        }\n        this.close();\n    }\n\n    async loadSources(useInput) {\n        this.sources = [];\n        this.state.activeSourceOption = null;\n        const proms = [];\n        for (const pSource of this.props.sources) {\n            const source = this.makeSource(pSource);\n            this.sources.push(source);\n\n            const options = this.loadOptions(\n                pSource.options,\n                useInput ? this.inputRef.el.value.trim() : \"\"\n            );\n            if (options instanceof Promise) {\n                source.isLoading = true;\n                const prom = options.then((options) => {\n                    source.options = options.map((option) => this.makeOption(option));\n                    source.isLoading = false;\n                    this.state.optionsRev++;\n                });\n                proms.push(prom);\n            } else {\n                source.options = options.map((option) => this.makeOption(option));\n            }\n        }\n\n        await Promise.all(proms);\n        this.navigate(0);\n    }\n    get displayOptions() {\n        return !this.props.dropdown || (this.isOpened && this.hasOptions);\n    }\n    loadOptions(options, request) {\n        if (typeof options === \"function\") {\n            return options(request);\n        } else {\n            return options;\n        }\n    }\n    makeOption(option) {\n        return Object.assign(Object.create(option), {\n            id: ++this.nextOptionId,\n        });\n    }\n    makeSource(source) {\n        return {\n            id: ++this.nextSourceId,\n            options: [],\n            isLoading: false,\n            placeholder: source.placeholder,\n            optionTemplate: source.optionTemplate,\n        };\n    }\n\n    isActiveSourceOption([sourceIndex, optionIndex]) {\n        return (\n            this.state.activeSourceOption &&\n            this.state.activeSourceOption[0] === sourceIndex &&\n            this.state.activeSourceOption[1] === optionIndex\n        );\n    }\n    selectOption(indices, params = {}) {\n        const option = this.sources[indices[0]].options[indices[1]];\n        this.inEdition = false;\n        if (option.unselectable) {\n            this.inputRef.el.value = \"\";\n            this.close();\n            return;\n        }\n\n        if (this.props.resetOnSelect) {\n            this.inputRef.el.value = \"\";\n        }\n\n        this.forceValFromProp = true;\n        this.props.onSelect(option, {\n            ...params,\n            input: this.inputRef.el,\n        });\n        this.close();\n    }\n\n    navigate(direction) {\n        let step = Math.sign(direction);\n        if (!step) {\n            this.state.activeSourceOption = null;\n            step = 1;\n        } else {\n            this.state.navigationRev++;\n        }\n\n        if (this.state.activeSourceOption) {\n            let [sourceIndex, optionIndex] = this.state.activeSourceOption;\n            let source = this.sources[sourceIndex];\n\n            optionIndex += step;\n            if (0 > optionIndex || optionIndex >= source.options.length) {\n                sourceIndex += step;\n                source = this.sources[sourceIndex];\n\n                while (source && source.isLoading) {\n                    sourceIndex += step;\n                    source = this.sources[sourceIndex];\n                }\n\n                if (source) {\n                    optionIndex = step < 0 ? source.options.length - 1 : 0;\n                }\n            }\n\n            this.state.activeSourceOption = source ? [sourceIndex, optionIndex] : null;\n        } else {\n            let sourceIndex = step < 0 ? this.sources.length - 1 : 0;\n            let source = this.sources[sourceIndex];\n\n            while (source && source.isLoading) {\n                sourceIndex += step;\n                source = this.sources[sourceIndex];\n            }\n\n            if (source) {\n                const optionIndex = step < 0 ? source.options.length - 1 : 0;\n                if (optionIndex < source.options.length) {\n                    this.state.activeSourceOption = [sourceIndex, optionIndex];\n                }\n            }\n        }\n    }\n\n    onInputBlur() {\n        if (this.ignoreBlur) {\n            this.ignoreBlur = false;\n            return;\n        }\n        this.props.onBlur({\n            inputValue: this.inputRef.el.value,\n        });\n        this.inEdition = false;\n    }\n    onInputClick() {\n        if (!this.isOpened) {\n            this.open(this.inputRef.el.value.trim() !== this.props.value.trim());\n        } else {\n            this.close();\n        }\n    }\n    onInputChange(ev) {\n        if (this.ignoreBlur) {\n            ev.stopImmediatePropagation();\n        }\n        this.props.onChange({\n            inputValue: this.inputRef.el.value,\n        });\n    }\n    async onInput() {\n        this.inEdition = true;\n        this.pendingPromise = this.pendingPromise || new Deferred();\n        this.loadingPromise = this.pendingPromise;\n        this.debouncedProcessInput();\n    }\n\n    onInputFocus(ev) {\n        this.inputRef.el.setSelectionRange(0, this.inputRef.el.value.length);\n        this.props.onFocus(ev);\n    }\n\n    get autoCompleteRootClass() {\n        let classList = \"\";\n        if (this.props.class) {\n            classList += this.props.class;\n        }\n        if (this.props.dropdown) {\n            classList += \" dropdown\";\n        }\n        return classList;\n    }\n\n    get ulDropdownClass() {\n        let classList = \"\";\n        if (this.props.dropdown) {\n            classList += \" dropdown-menu ui-autocomplete\";\n        } else {\n            classList += \" list-group\";\n        }\n        return classList;\n    }\n\n    async onInputKeydown(ev) {\n        const hotkey = getActiveHotkey(ev);\n        const isSelectKey = hotkey === \"enter\" || hotkey === \"tab\";\n\n        if (this.loadingPromise && isSelectKey) {\n            if (hotkey === \"enter\") {\n                ev.stopPropagation();\n                ev.preventDefault();\n            }\n\n            await this.loadingPromise;\n        }\n\n        switch (hotkey) {\n            case \"enter\":\n                if (!this.isOpened || !this.state.activeSourceOption) {\n                    return;\n                }\n                this.selectOption(this.state.activeSourceOption);\n                break;\n            case \"escape\":\n                if (!this.isOpened) {\n                    return;\n                }\n                this.cancel();\n                break;\n            case \"tab\":\n                if (!this.isOpened) {\n                    return;\n                }\n                if (\n                    this.props.autoSelect &&\n                    this.state.activeSourceOption &&\n                    (this.state.navigationRev > 0 || this.inputRef.el.value.length > 0)\n                ) {\n                    this.selectOption(this.state.activeSourceOption);\n                }\n                this.close();\n                return;\n            case \"arrowup\":\n                this.navigate(-1);\n                if (!this.isOpened) {\n                    this.open(true);\n                }\n                break;\n            case \"arrowdown\":\n                this.navigate(+1);\n                if (!this.isOpened) {\n                    this.open(true);\n                }\n                break;\n            default:\n                return;\n        }\n\n        ev.stopPropagation();\n        ev.preventDefault();\n    }\n\n    onOptionMouseEnter(indices) {\n        this.state.activeSourceOption = indices;\n    }\n    onOptionMouseLeave() {\n        this.state.activeSourceOption = null;\n    }\n    onOptionClick(indices) {\n        this.selectOption(indices);\n        this.inputRef.el.focus();\n    }\n\n    externalClose(ev) {\n        if (this.isOpened && !this.root.el.contains(ev.target)) {\n            this.cancel();\n        }\n    }\n}\n", "/**\n * Builder for BarcodeDetector-like polyfill class using ZXing library.\n *\n * @param {ZXing} ZXing Zxing library\n * @returns {class} ZxingBarcodeDetector class\n */\nexport function buildZXingBarcodeDetector(ZXing) {\n    const ZXingFormats = new Map([\n        [\"aztec\", ZXing.BarcodeFormat.AZTEC],\n        [\"code_39\", ZXing.BarcodeFormat.CODE_39],\n        [\"code_128\", ZXing.BarcodeFormat.CODE_128],\n        [\"data_matrix\", ZXing.BarcodeFormat.DATA_MATRIX],\n        [\"ean_8\", ZXing.BarcodeFormat.EAN_8],\n        [\"ean_13\", ZXing.BarcodeFormat.EAN_13],\n        [\"itf\", ZXing.BarcodeFormat.ITF],\n        [\"pdf417\", ZXing.BarcodeFormat.PDF_417],\n        [\"qr_code\", ZXing.BarcodeFormat.QR_CODE],\n        [\"upc_a\", ZXing.BarcodeFormat.UPC_A],\n        [\"upc_e\", ZXing.BarcodeFormat.UPC_E],\n    ]);\n\n    const allSupportedFormats = Array.from(ZXingFormats.keys());\n\n    /**\n     * Restore previous behavior of the lib because since https://github.com/zxing-js/library/commit/7644e279df9fd2e754e044c25f450576d2878e45\n     * the new behavior of the lib breaks it when the lib use the ZXing.DecodeHintType.TRY_HARDER at true\n     *\n     * @override\n     */\n    ZXing.HTMLCanvasElementLuminanceSource.toGrayscaleBuffer = function (\n        imageBuffer,\n        width,\n        height\n    ) {\n        const grayscaleBuffer = new Uint8ClampedArray(width * height);\n        for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) {\n            let gray;\n            const alpha = imageBuffer[i + 3];\n            // The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent\n            // black (0 alpha, and then 0 RGB). They are often used, of course as the \"white\" area in a\n            // barcode image. Force any such pixel to be white:\n            if (alpha === 0) {\n                gray = 0xff;\n            } else {\n                const pixelR = imageBuffer[i];\n                const pixelG = imageBuffer[i + 1];\n                const pixelB = imageBuffer[i + 2];\n                // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC),\n                // (306*R) >> 10 is approximately equal to R*0.299, and so on.\n                // 0x200 >> 10 is 0.5, it implements rounding.\n                gray = (306 * pixelR + 601 * pixelG + 117 * pixelB + 0x200) >> 10;\n            }\n            grayscaleBuffer[j] = gray;\n        }\n        return grayscaleBuffer;\n    };\n\n    /**\n     * ZXingBarcodeDetector class\n     *\n     * BarcodeDetector-like polyfill class using ZXing library.\n     * API follows the Shape Detection Web API (specifically Barcode Detection).\n     */\n    class ZXingBarcodeDetector {\n        /**\n         * @param {object} opts\n         * @param {Array} opts.formats list of codes' formats to detect\n         */\n        constructor(opts = {}) {\n            const formats = opts.formats || allSupportedFormats;\n            const hints = new Map([\n                [\n                    ZXing.DecodeHintType.POSSIBLE_FORMATS,\n                    formats.map((format) => ZXingFormats.get(format)),\n                ],\n                // Enable Scanning at 90 degrees rotation\n                // https://github.com/zxing-js/library/issues/291\n                [ZXing.DecodeHintType.TRY_HARDER, true],\n            ]);\n            this.reader = new ZXing.MultiFormatReader();\n            this.reader.setHints(hints);\n        }\n\n        /**\n         * Detect codes in image.\n         *\n         * @param {HTMLVideoElement} video source video element\n         * @returns {Promise<Array>} array of detected codes\n         */\n        async detect(video) {\n            if (!(video instanceof HTMLVideoElement)) {\n                throw new DOMException(\n                    \"imageDataFrom() requires an HTMLVideoElement\",\n                    \"InvalidArgumentError\"\n                );\n            }\n            if (!isVideoElementReady(video)) {\n                throw new DOMException(\"HTMLVideoElement is not ready\", \"InvalidStateError\");\n            }\n            const canvas = document.createElement(\"canvas\");\n\n            let barcodeArea;\n            if (this.cropArea && (this.cropArea.x || this.cropArea.y)) {\n                barcodeArea = this.cropArea;\n            } else {\n                barcodeArea = {\n                    x: 0,\n                    y: 0,\n                    width: video.videoWidth,\n                    height: video.videoHeight,\n                };\n            }\n            canvas.width = barcodeArea.width;\n            canvas.height = barcodeArea.height;\n\n            const ctx = canvas.getContext(\"2d\");\n\n            ctx.drawImage(\n                video,\n                barcodeArea.x,\n                barcodeArea.y,\n                barcodeArea.width,\n                barcodeArea.height,\n                0,\n                0,\n                barcodeArea.width,\n                barcodeArea.height\n            );\n\n            const luminanceSource = new ZXing.HTMLCanvasElementLuminanceSource(canvas);\n            const binaryBitmap = new ZXing.BinaryBitmap(new ZXing.HybridBinarizer(luminanceSource));\n            try {\n                const result = this.reader.decodeWithState(binaryBitmap);\n                const { resultPoints } = result;\n                const boundingBox = DOMRectReadOnly.fromRect({\n                    x: resultPoints[0].x,\n                    y: resultPoints[0].y,\n                    height: Math.max(1, Math.abs(resultPoints[1].y - resultPoints[0].y)),\n                    width: Math.max(1, Math.abs(resultPoints[1].x - resultPoints[0].x)),\n                });\n                const cornerPoints = resultPoints;\n                const format = Array.from(ZXingFormats).find(\n                    ([k, val]) => val === result.getBarcodeFormat()\n                );\n                const rawValue = result.getText();\n                return [\n                    {\n                        boundingBox,\n                        cornerPoints,\n                        format,\n                        rawValue,\n                    },\n                ];\n            } catch (err) {\n                if (err.name === \"NotFoundException\") {\n                    return [];\n                }\n                throw err;\n            }\n        }\n\n        setCropArea(cropArea) {\n            this.cropArea = cropArea;\n        }\n    }\n\n    /**\n     * Supported codes formats\n     *\n     * @static\n     * @returns {Promise<string[]>}\n     */\n    ZXingBarcodeDetector.getSupportedFormats = async () => allSupportedFormats;\n\n    return ZXingBarcodeDetector;\n}\n\n/**\n * Check for HTMLVideoElement readiness.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState\n */\nconst HAVE_NOTHING = 0;\nconst HAVE_METADATA = 1;\nexport function isVideoElementReady(video) {\n    return ![HAVE_NOTHING, HAVE_METADATA].includes(video.readyState);\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { Component, useState } from \"@odoo/owl\";\nimport { BarcodeVideoScanner, isBarcodeScannerSupported } from \"./barcode_video_scanner\";\n\nexport class BarcodeDialog extends Component {\n    static template = \"web.BarcodeDialog\";\n    static components = {\n        BarcodeVideoScanner,\n        Dialog,\n    };\n    static props = [\"facingMode\", \"close\", \"onResult\", \"onError\"];\n\n    setup() {\n        this.state = useState({\n            barcodeScannerSupported: isBarcodeScannerSupported(),\n            errorMessage: _t(\"Check your browser permissions\"),\n        });\n    }\n\n    /**\n     * Detection success handler\n     *\n     * @param {string} result found code\n     */\n    onResult(result) {\n        this.props.close();\n        this.props.onResult(result);\n    }\n\n    /**\n     * Detection error handler\n     *\n     * @param {Error} error\n     */\n    onError(error) {\n        this.state.barcodeScannerSupported = false;\n        this.state.errorMessage = error.message;\n    }\n}\n\n/**\n * Opens the BarcodeScanning dialog and begins code detection using the device's camera.\n *\n * @returns {Promise<string>} resolves when a {qr,bar}code has been detected\n */\nexport async function scanBarcode(env, facingMode = \"environment\") {\n    let res;\n    let rej;\n    const promise = new Promise((resolve, reject) => {\n        res = resolve;\n        rej = reject;\n    });\n    env.services.dialog.add(BarcodeDialog, {\n        facingMode,\n        onResult: (result) => res(result),\n        onError: (error) => rej(error),\n    });\n    return promise;\n}\n", "/* global BarcodeDetector */\n\nimport { browser } from \"@web/core/browser/browser\";\nimport { delay } from \"@web/core/utils/concurrency\";\nimport { loadJS } from \"@web/core/assets\";\nimport { isVideoElementReady, buildZXingBarcodeDetector } from \"./ZXingBarcodeDetector\";\nimport { CropOverlay } from \"./crop_overlay\";\nimport { Component, onMounted, onWillStart, onWillUnmount, useRef, useState } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { pick } from \"@web/core/utils/objects\";\n\nexport class BarcodeVideoScanner extends Component {\n    static template = \"web.BarcodeVideoScanner\";\n    static components = {\n        CropOverlay,\n    };\n    static props = {\n        cssClass: { type: String, optional: true },\n        facingMode: {\n            type: String,\n            validate: (fm) => [\"environment\", \"left\", \"right\", \"user\"].includes(fm),\n        },\n        close: { type: Function, optional: true },\n        onReady: { type: Function, optional: true },\n        onResult: Function,\n        onError: Function,\n        delayBetweenScan: { type: Number, optional: true },\n    };\n    static defaultProps = {\n        cssClass: \"w-100 h-100\",\n    };\n    /**\n     * @override\n     */\n    setup() {\n        this.videoPreviewRef = useRef(\"videoPreview\");\n        this.detectorTimeout = null;\n        this.stream = null;\n        this.detector = null;\n        this.overlayInfo = {};\n        this.zoomRatio = 1;\n        this.scanPaused = false;\n        this.state = useState({\n            isReady: false,\n        });\n\n        onWillStart(async () => {\n            let DetectorClass;\n            // Use Barcode Detection API if available.\n            // As support is still bleeding edge (mainly Chrome on Android),\n            // also provides a fallback using ZXing library.\n            if (\"BarcodeDetector\" in window) {\n                DetectorClass = BarcodeDetector;\n            } else {\n                await loadJS(\"/web/static/lib/zxing-library/zxing-library.js\");\n                DetectorClass = buildZXingBarcodeDetector(window.ZXing);\n            }\n            const formats = await DetectorClass.getSupportedFormats();\n            this.detector = new DetectorClass({ formats });\n        });\n\n        onMounted(async () => {\n            const constraints = {\n                video: { facingMode: this.props.facingMode },\n                audio: false,\n            };\n\n            try {\n                this.stream = await browser.navigator.mediaDevices.getUserMedia(constraints);\n            } catch (err) {\n                const errors = {\n                    NotFoundError: _t(\"No device can be found.\"),\n                    NotAllowedError: _t(\"Odoo needs your authorization first.\"),\n                };\n                const errorMessage = _t(\"Could not start scanning. %(message)s\", {\n                    message: errors[err.name] || err.message,\n                });\n                this.props.onError(new Error(errorMessage));\n                return;\n            }\n            if (!this.videoPreviewRef.el) {\n                this.cleanStreamAndTimeout();\n                const errorMessage = _t(\"Barcode Video Scanner could not be mounted properly.\");\n                this.props.onError(new Error(errorMessage));\n                return;\n            }\n            this.videoPreviewRef.el.srcObject = this.stream;\n            await this.isVideoReady();\n            const { height, width } = getComputedStyle(this.videoPreviewRef.el);\n            const divWidth = width.slice(0, -2);\n            const divHeight = height.slice(0, -2);\n            const tracks = this.stream.getVideoTracks();\n            if (tracks.length) {\n                const [track] = tracks;\n                const settings = track.getSettings();\n                this.zoomRatio = Math.min(divWidth / settings.width, divHeight / settings.height);\n            }\n            this.detectorTimeout = setTimeout(this.detectCode.bind(this), 100);\n        });\n\n        onWillUnmount(() => this.cleanStreamAndTimeout());\n    }\n\n    cleanStreamAndTimeout() {\n        clearTimeout(this.detectorTimeout);\n        this.detectorTimeout = null;\n        if (this.stream) {\n            this.stream.getTracks().forEach((track) => track.stop());\n            this.stream = null;\n        }\n    }\n\n    isZXingBarcodeDetector() {\n        return this.detector && this.detector.__proto__.constructor.name === \"ZXingBarcodeDetector\";\n    }\n\n    /**\n     * Check for camera preview element readiness\n     *\n     * @returns {Promise} resolves when the video element is ready\n     */\n    async isVideoReady() {\n        // FIXME: even if it shouldn't happened, a timeout could be useful here.\n        while (!isVideoElementReady(this.videoPreviewRef.el)) {\n            await delay(10);\n        }\n        this.state.isReady = true;\n        if (this.props.onReady) {\n            this.props.onReady();\n        }\n    }\n\n    onResize(overlayInfo) {\n        this.overlayInfo = overlayInfo;\n        if (this.isZXingBarcodeDetector()) {\n            // TODO need refactoring when ZXing will support multiple result in one scan\n            // https://github.com/zxing-js/library/issues/346\n            this.detector.setCropArea(this.adaptValuesWithRatio(this.overlayInfo, true));\n        }\n    }\n\n    /**\n     * Attempt to detect codes in the current camera preview's frame\n     */\n    async detectCode() {\n        let barcodeDetected = false;\n        let codes = [];\n        try {\n            codes = await this.detector.detect(this.videoPreviewRef.el);\n        } catch (err) {\n            this.props.onError(err);\n        }\n        for (const code of codes) {\n            if (\n                !this.isZXingBarcodeDetector() &&\n                this.overlayInfo.x !== undefined &&\n                this.overlayInfo.y !== undefined\n            ) {\n                const { x, y, width, height } = this.adaptValuesWithRatio(code.boundingBox);\n                if (\n                    x < this.overlayInfo.x ||\n                    x + width > this.overlayInfo.x + this.overlayInfo.width ||\n                    y < this.overlayInfo.y ||\n                    y + height > this.overlayInfo.y + this.overlayInfo.height\n                ) {\n                    continue;\n                }\n            }\n            barcodeDetected = true;\n            this.barcodeDetected(code.rawValue);\n            break;\n        }\n        if (this.stream && (!barcodeDetected || !this.props.delayBetweenScan)) {\n            this.detectorTimeout = setTimeout(this.detectCode.bind(this), 100);\n        }\n    }\n\n    barcodeDetected(barcode) {\n        if (this.props.delayBetweenScan && !this.scanPaused) {\n            this.scanPaused = true;\n            this.detectorTimeout = setTimeout(() => {\n                this.scanPaused = false;\n                this.detectorTimeout = setTimeout(this.detectCode.bind(this), 100);\n            }, this.props.delayBetweenScan);\n        }\n        this.props.onResult(barcode);\n    }\n\n    adaptValuesWithRatio(domRect, dividerRatio = false) {\n        const newObject = pick(domRect, \"x\", \"y\", \"width\", \"height\");\n        for (const key of Object.keys(newObject)) {\n            if (dividerRatio) {\n                newObject[key] /= this.zoomRatio;\n            } else {\n                newObject[key] *= this.zoomRatio;\n            }\n        }\n        return newObject;\n    }\n}\n\n/**\n * Check for BarcodeScanner support\n * @returns {boolean}\n */\nexport function isBarcodeScannerSupported() {\n    return Boolean(browser.navigator.mediaDevices && browser.navigator.mediaDevices.getUserMedia);\n}\n", "import { Component, useRef, onPatched } from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { clamp } from \"@web/core/utils/numbers\";\n\nexport class CropOverlay extends Component {\n    static template = \"web.CropOverlay\";\n    static props = {\n        onResize: Function,\n        isReady: Boolean,\n        slots: {\n            type: Object,\n            shape: {\n                default: {},\n            },\n        },\n    };\n\n    setup() {\n        this.localStorageKey = \"o-barcode-scanner-overlay\";\n        this.cropContainerRef = useRef(\"crop-container\");\n        this.isMoving = false;\n        this.boundaryOverlay = {};\n        this.relativePosition = {\n            x: 0,\n            y: 0,\n        };\n        onPatched(() => {\n            this.setupCropRect();\n        });\n    }\n\n    setupCropRect() {\n        if (!this.props.isReady) {\n            return;\n        }\n        this.computeDefaultPoint();\n        this.computeOverlayPosition();\n        this.calculateAndSetTransparentRect();\n        this.executeOnResizeCallback();\n    }\n\n    boundPoint(pointValue, boundaryRect) {\n        return {\n            x: clamp(pointValue.x, boundaryRect.left, boundaryRect.left + boundaryRect.width),\n            y: clamp(pointValue.y, boundaryRect.top, boundaryRect.top + boundaryRect.height),\n        };\n    }\n\n    calculateAndSetTransparentRect() {\n        const cropTransparentRect = this.getTransparentRec(\n            this.relativePosition,\n            this.boundaryOverlay\n        );\n        this.setCropValue(cropTransparentRect, this.relativePosition);\n    }\n\n    computeOverlayPosition() {\n        const cropOverlayElement = this.cropContainerRef.el.querySelector(\".o_crop_overlay\");\n        this.boundaryOverlay = cropOverlayElement.getBoundingClientRect();\n    }\n\n    executeOnResizeCallback() {\n        const transparentRec = this.getTransparentRec(this.relativePosition, this.boundaryOverlay);\n        browser.localStorage.setItem(this.localStorageKey, JSON.stringify(transparentRec));\n        this.props.onResize({\n            ...transparentRec,\n            width: this.boundaryOverlay.width - 2 * transparentRec.x,\n            height: this.boundaryOverlay.height - 2 * transparentRec.y,\n        });\n    }\n\n    computeDefaultPoint() {\n        const firstChildComputedStyle = getComputedStyle(this.cropContainerRef.el.firstChild);\n        const elementWidth = firstChildComputedStyle.width.slice(0, -2);\n        const elementHeight = firstChildComputedStyle.height.slice(0, -2);\n\n        const stringSavedPoint = browser.localStorage.getItem(this.localStorageKey);\n        if (stringSavedPoint) {\n            const savedPoint = JSON.parse(stringSavedPoint);\n            this.relativePosition = {\n                x: clamp(savedPoint.x, 0, elementWidth),\n                y: clamp(savedPoint.y, 0, elementHeight),\n            };\n        } else {\n            const stepWidth = elementWidth / 10;\n            const width = stepWidth * 8;\n            const height = width / 4;\n            const startY = elementHeight / 2 - height / 2;\n            this.relativePosition = {\n                x: stepWidth + width,\n                y: startY + height,\n            };\n        }\n    }\n    getTransparentRec(point, rect) {\n        const middleX = rect.width / 2;\n        const middleY = rect.height / 2;\n        const newDeltaX = Math.abs(point.x - middleX);\n        const newDeltaY = Math.abs(point.y - middleY);\n        return {\n            x: middleX - newDeltaX,\n            y: middleY - newDeltaY,\n        };\n    }\n\n    setCropValue(point, iconPoint) {\n        if (!iconPoint) {\n            iconPoint = point;\n        }\n        this.cropContainerRef.el.style.setProperty(\"--o-crop-x\", `${point.x}px`);\n        this.cropContainerRef.el.style.setProperty(\"--o-crop-y\", `${point.y}px`);\n        this.cropContainerRef.el.style.setProperty(\"--o-crop-icon-x\", `${iconPoint.x}px`);\n        this.cropContainerRef.el.style.setProperty(\"--o-crop-icon-y\", `${iconPoint.y}px`);\n    }\n\n    pointerDown(event) {\n        event.preventDefault();\n        if (event.target.matches(\".o_crop_icon\")) {\n            this.computeOverlayPosition();\n            this.isMoving = true;\n        }\n    }\n\n    pointerMove(event) {\n        if (!this.isMoving) {\n            return;\n        }\n        let eventPosition;\n        if (event.touches && event.touches.length) {\n            eventPosition = event.touches[0];\n        } else {\n            eventPosition = event;\n        }\n        const { clientX, clientY } = eventPosition;\n        const restrictedPosition = this.boundPoint(\n            {\n                x: clientX,\n                y: clientY,\n            },\n            this.boundaryOverlay\n        );\n        this.relativePosition = {\n            x: restrictedPosition.x - this.boundaryOverlay.left,\n            y: restrictedPosition.y - this.boundaryOverlay.top,\n        };\n        this.calculateAndSetTransparentRect(this.relativePosition);\n    }\n\n    pointerUp(event) {\n        this.isMoving = false;\n        this.executeOnResizeCallback();\n    }\n}\n", "/**\n * Browser\n *\n * This file exports an object containing common browser API. It may not look\n * incredibly useful, but it is very convenient when one needs to test code using\n * these methods. With this indirection, it is possible to patch the browser\n * object for a test.\n */\n\nlet sessionStorage;\nlet localStorage;\ntry {\n    sessionStorage = window.sessionStorage;\n    localStorage = window.localStorage;\n    // Safari crashes in Private Browsing\n    localStorage.setItem(\"__localStorage__\", \"true\");\n    localStorage.removeItem(\"__localStorage__\");\n} catch {\n    localStorage = makeRAMLocalStorage();\n    sessionStorage = makeRAMLocalStorage();\n}\n\nexport const browser = {\n    addEventListener: window.addEventListener.bind(window),\n    dispatchEvent: window.dispatchEvent.bind(window),\n    AnalyserNode: window.AnalyserNode,\n    Audio: window.Audio,\n    AudioBufferSourceNode: window.AudioBufferSourceNode,\n    AudioContext: window.AudioContext,\n    AudioWorkletNode: window.AudioWorkletNode,\n    BeforeInstallPromptEvent: window.BeforeInstallPromptEvent?.bind(window),\n    GainNode: window.GainNode,\n    MediaStreamAudioSourceNode: window.MediaStreamAudioSourceNode,\n    removeEventListener: window.removeEventListener.bind(window),\n    setTimeout: window.setTimeout.bind(window),\n    clearTimeout: window.clearTimeout.bind(window),\n    setInterval: window.setInterval.bind(window),\n    clearInterval: window.clearInterval.bind(window),\n    performance: window.performance,\n    requestAnimationFrame: window.requestAnimationFrame.bind(window),\n    cancelAnimationFrame: window.cancelAnimationFrame.bind(window),\n    console: window.console,\n    history: window.history,\n    matchMedia: window.matchMedia.bind(window),\n    navigator,\n    Notification: window.Notification,\n    open: window.open.bind(window),\n    SharedWorker: window.SharedWorker,\n    Worker: window.Worker,\n    XMLHttpRequest: window.XMLHttpRequest,\n    localStorage,\n    sessionStorage,\n    fetch: window.fetch.bind(window),\n    innerHeight: window.innerHeight,\n    innerWidth: window.innerWidth,\n    ontouchstart: window.ontouchstart,\n    BroadcastChannel: window.BroadcastChannel,\n};\n\nObject.defineProperty(browser, \"location\", {\n    set(val) {\n        window.location = val;\n    },\n    get() {\n        return window.location;\n    },\n    configurable: true,\n});\n\nObject.defineProperty(browser, \"innerHeight\", {\n    get: () => window.innerHeight,\n    configurable: true,\n});\nObject.defineProperty(browser, \"innerWidth\", {\n    get: () => window.innerWidth,\n    configurable: true,\n});\n\n// -----------------------------------------------------------------------------\n// memory localStorage\n// -----------------------------------------------------------------------------\n\n/**\n * @returns {typeof window[\"localStorage\"]}\n */\nexport function makeRAMLocalStorage() {\n    let store = {};\n    return {\n        setItem(key, value) {\n            const newValue = String(value);\n            store[key] = newValue;\n            window.dispatchEvent(new StorageEvent(\"storage\", { key, newValue }));\n        },\n        getItem(key) {\n            return store[key] ?? null;\n        },\n        clear() {\n            store = {};\n        },\n        removeItem(key) {\n            delete store[key];\n            window.dispatchEvent(new StorageEvent(\"storage\", { key, newValue: null }));\n        },\n        get length() {\n            return Object.keys(store).length;\n        },\n        key() {\n            return \"\";\n        },\n    };\n}\n", "import { browser } from \"./browser\";\n\n// -----------------------------------------------------------------------------\n// Feature detection\n// -----------------------------------------------------------------------------\n\n/**\n * True if the browser is based on Chromium (Google Chrome, Opera, Edge).\n */\nexport function isBrowserChrome() {\n    return /Chrome/i.test(browser.navigator.userAgent);\n}\n\nexport function isBrowserFirefox() {\n    return /Firefox/i.test(browser.navigator.userAgent);\n}\n\n/**\n * true if the browser is based on Safari (Safari, Epiphany)\n *\n * @returns {boolean}\n */\nexport function isBrowserSafari() {\n    return !isBrowserChrome() && browser.navigator.userAgent?.includes(\"Safari\");\n}\n\nexport function isAndroid() {\n    return /Android/i.test(browser.navigator.userAgent);\n}\n\nexport function isIOS() {\n    return (\n        /(iPad|iPhone|iPod)/i.test(browser.navigator.userAgent) ||\n        (browser.navigator.platform === \"MacIntel\" && maxTouchPoints() > 1)\n    );\n}\n\nexport function isOtherMobileOS() {\n    return /(webOS|BlackBerry|Windows Phone)/i.test(browser.navigator.userAgent);\n}\n\nexport function isMacOS() {\n    return /Mac/i.test(browser.navigator.userAgent);\n}\n\nexport function isMobileOS() {\n    return isAndroid() || isIOS() || isOtherMobileOS();\n}\n\nexport function isIosApp() {\n    return /OdooMobile \\(iOS\\)/i.test(browser.navigator.userAgent);\n}\n\nexport function isAndroidApp() {\n    return /OdooMobile.+Android/i.test(browser.navigator.userAgent);\n}\n\nexport function isDisplayStandalone() {\n    return browser.matchMedia(\"(display-mode: standalone)\").matches;\n}\n\nexport function hasTouch() {\n    return browser.ontouchstart !== undefined || browser.matchMedia(\"(pointer:coarse)\").matches;\n}\n\nexport function maxTouchPoints() {\n    return browser.navigator.maxTouchPoints || 1;\n}\n", "import { EventBus } from \"@odoo/owl\";\nimport { omit, pick } from \"../utils/objects\";\nimport { compareUrls, objectToUrlEncodedString } from \"../utils/urls\";\nimport { browser } from \"./browser\";\nimport { isDisplayStandalone } from \"@web/core/browser/feature_detection\";\nimport { slidingWindow } from \"@web/core/utils/arrays\";\nimport { isNumeric } from \"@web/core/utils/strings\";\n\n// Keys that are serialized in the URL as path segments instead of query string\nexport const PATH_KEYS = [\"resId\", \"action\", \"active_id\", \"model\"];\n\nexport const routerBus = new EventBus();\n\nfunction isScopedApp() {\n    return browser.location.href.includes(\"/scoped_app\") && isDisplayStandalone();\n}\n\n/**\n * Casts the given string to a number if possible.\n *\n * @param {string} value\n * @returns {string|number}\n */\nfunction cast(value) {\n    return !value || isNaN(value) ? value : Number(value);\n}\n\n/**\n * @typedef {{ [key: string]: string }} Query\n * @typedef {{ [key: string]: any }} Route\n */\n\nfunction parseString(str) {\n    const parts = str.split(\"&\");\n    const result = {};\n    for (const part of parts) {\n        const [key, value] = part.split(\"=\");\n        const decoded = decodeURIComponent(value || \"\");\n        result[key] = cast(decoded);\n    }\n    return result;\n}\n/**\n * @param {object} values An object with the values of the new state\n * @param {boolean} replace whether the values should replace the state or be\n *  layered on top of the current state\n * @returns {object} the next state of the router\n */\nfunction computeNextState(values, replace) {\n    const nextState = replace ? pick(state, ..._lockedKeys) : { ...state };\n    Object.assign(nextState, values);\n    // Update last entry in the actionStack\n    if (nextState.actionStack?.length) {\n        Object.assign(nextState.actionStack.at(-1), pick(nextState, ...PATH_KEYS));\n    }\n    return sanitizeSearch(nextState);\n}\n\nfunction sanitize(obj, valueToRemove) {\n    return Object.fromEntries(\n        Object.entries(obj)\n            .filter(([, v]) => v !== valueToRemove)\n            .map(([k, v]) => [k, cast(v)])\n    );\n}\n\nfunction sanitizeSearch(search) {\n    return sanitize(search);\n}\n\nfunction sanitizeHash(hash) {\n    return sanitize(hash, \"\");\n}\n\n/**\n * @param {string} hash\n * @returns {any}\n */\nexport function parseHash(hash) {\n    return hash && hash !== \"#\" ? parseString(hash.slice(1)) : {};\n}\n\n/**\n * @param {string} search\n * @returns {any}\n */\nexport function parseSearchQuery(search) {\n    return search ? parseString(search.slice(1)) : {};\n}\n\nfunction pathFromActionState(state) {\n    const path = [];\n    const { action, model, active_id, resId } = state;\n    if (active_id && typeof active_id === \"number\") {\n        path.push(active_id);\n    }\n    if (action) {\n        if (typeof action === \"number\" || action.includes(\".\")) {\n            path.push(`action-${action}`);\n        } else {\n            path.push(action);\n        }\n    } else if (model) {\n        if (model.includes(\".\")) {\n            path.push(model);\n        } else {\n            // A few models don't have a dot at all, we need to distinguish\n            // them from action paths (eg: website)\n            path.push(`m-${model}`);\n        }\n    }\n    if (resId && (typeof resId === \"number\" || resId === \"new\")) {\n        path.push(resId);\n    }\n    return path.join(\"/\");\n}\n\n/**\n * @param {{ [key: string]: any }} state\n * @returns\n */\nexport function stateToUrl(state) {\n    let path = \"\";\n    const pathKeysToOmit = [..._hiddenKeysFromUrl];\n    const actionStack = (state.actionStack || [state]).map((a) => ({ ...a }));\n    if (actionStack.at(-1)?.action !== \"menu\") {\n        for (const [prevAct, currentAct] of slidingWindow(actionStack, 2).reverse()) {\n            const { action: prevAction, resId: prevResId, active_id: prevActiveId } = prevAct;\n            const { action: currentAction, active_id: currentActiveId } = currentAct;\n            // actions would typically map to a path like `active_id/action/res_id`\n            if (currentActiveId === prevResId) {\n                // avoid doubling up when the active_id is the same as the previous action's res_id\n                delete currentAct.active_id;\n            }\n            if (prevAction === currentAction && !prevResId && currentActiveId === prevActiveId) {\n                //avoid doubling up the action and the active_id when a single-record action is preceded by a multi-record action\n                delete currentAct.action;\n                delete currentAct.active_id;\n            }\n        }\n        const pathSegments = actionStack.map(pathFromActionState).filter(Boolean);\n        if (pathSegments.length) {\n            path = `/${pathSegments.join(\"/\")}`;\n        }\n    }\n    if (state.active_id && typeof state.active_id !== \"number\") {\n        pathKeysToOmit.splice(pathKeysToOmit.indexOf(\"active_id\"), 1);\n    }\n    if (state.resId && typeof state.resId !== \"number\" && state.resId !== \"new\") {\n        pathKeysToOmit.splice(pathKeysToOmit.indexOf(\"resId\"), 1);\n    }\n    const search = objectToUrlEncodedString(omit(state, ...pathKeysToOmit));\n    const start_url = isScopedApp() ? \"scoped_app\" : \"odoo\";\n    return `/${start_url}${path}${search ? `?${search}` : \"\"}`;\n}\n\nexport function urlToState(urlObj) {\n    const { pathname, hash, search } = urlObj;\n    const state = parseSearchQuery(search);\n\n    // ** url-retrocompatibility **\n    // If the url contains a hash, it can be for two motives:\n    // 1. It is an anchor link, in that case, we ignore it, as it will not have a keys/values format\n    //    the sanitizeHash function will remove it from the hash object.\n    // 2. It has one or more keys/values, in that case, we merge it with the search.\n    if (pathname === \"/web\") {\n        const sanitizedHash = sanitizeHash(parseHash(hash));\n        // Old urls used \"id\", it is now resId for clarity. Remap to the new name.\n        if (sanitizedHash.id) {\n            sanitizedHash.resId = sanitizedHash.id;\n            delete sanitizedHash.id;\n            delete sanitizedHash.view_type;\n        } else if (sanitizedHash.view_type === \"form\") {\n            sanitizedHash.resId = \"new\";\n            delete sanitizedHash.view_type;\n        }\n        Object.assign(state, sanitizedHash);\n        const url = browser.location.origin + router.stateToUrl(state);\n        urlObj.href = url;\n    }\n\n    const [prefix, ...splitPath] = urlObj.pathname.split(\"/\").filter(Boolean);\n\n    if (prefix === \"odoo\" || isScopedApp()) {\n        const actionParts = [...splitPath.entries()].filter(\n            ([_, part]) => !isNumeric(part) && part !== \"new\"\n        );\n        const actions = [];\n        for (const [i, part] of actionParts) {\n            const action = {};\n            const [left, right] = [splitPath[i - 1], splitPath[i + 1]];\n            if (isNumeric(left)) {\n                action.active_id = parseInt(left);\n            }\n\n            if (right === \"new\") {\n                action.resId = \"new\";\n            } else if (isNumeric(right)) {\n                action.resId = parseInt(right);\n            }\n\n            if (part.startsWith(\"action-\")) {\n                // numeric id or xml_id\n                const actionId = part.slice(7);\n                action.action = isNumeric(actionId) ? parseInt(actionId) : actionId;\n            } else if (part.startsWith(\"m-\")) {\n                action.model = part.slice(2);\n            } else if (part.includes(\".\")) {\n                action.model = part;\n            } else {\n                // action tag or path\n                action.action = part;\n            }\n\n            if (action.resId && action.action) {\n                actions.push(omit(action, \"resId\"));\n            }\n            // Don't create actions for models without resId unless they're the last one.\n            // If the last one is a model but doesn't have a view_type, the action service will not mount it anyway.\n            if (action.action || action.resId || i === splitPath.length - 1) {\n                actions.push(action);\n            }\n        }\n        const activeAction = actions.at(-1);\n        if (activeAction) {\n            Object.assign(state, activeAction);\n            state.actionStack = actions;\n        }\n    }\n    return state;\n}\n\nlet state;\nlet pushTimeout;\nlet pushArgs;\nlet _lockedKeys;\nlet _hiddenKeysFromUrl = new Set();\n\nexport function startRouter() {\n    const url = new URL(browser.location);\n    state = router.urlToState(url);\n    // ** url-retrocompatibility **\n    if (browser.location.pathname === \"/web\") {\n        // Change the url of the current history entry to the canonical url.\n        // This change should be done only at the first load, and not when clicking on old style internal urls.\n        // Or when clicking back/forward on the browser.\n        browser.history.replaceState(browser.history.state, null, url.href);\n    }\n    pushTimeout = null;\n    pushArgs = {\n        replace: false,\n        reload: false,\n        state: {},\n    };\n    _lockedKeys = new Set([\"debug\", \"lang\"]);\n    _hiddenKeysFromUrl = new Set([...PATH_KEYS, \"actionStack\"]);\n}\n\n/**\n * When the user navigates history using the back/forward button, the browser\n * dispatches a popstate event with the state that was in the history for the\n * corresponding history entry. We just adopt that state so that the webclient\n * can use that previous state without forcing a full page reload.\n */\nbrowser.addEventListener(\"popstate\", (ev) => {\n    browser.clearTimeout(pushTimeout);\n    if (!ev.state) {\n        // We are coming from a click on an anchor.\n        // Add the current state to the history entry so that a future loadstate behaves as expected.\n        browser.history.replaceState({ nextState: state }, \"\", browser.location.href);\n        return;\n    }\n    state = ev.state?.nextState || router.urlToState(new URL(browser.location));\n    // Some client actions want to handle loading their own state. This is a ugly hack to allow not\n    // reloading the webclient's state when they manipulate history.\n    if (!ev.state?.skipRouteChange && !router.skipLoad) {\n        routerBus.trigger(\"ROUTE_CHANGE\");\n    }\n    router.skipLoad = false;\n});\n\n/**\n * When the user navigates the history using the back/forward button, some browsers (Safari iOS and\n * Safari MacOS) can restore the page using the `bfcache` (especially when we come back from an\n * external website). Unfortunately, Odoo wasn't designed to be compatible with this cache, which\n * leads to inconsistencies. When the `bfcache` is used to restore a page, we reload the current\n * page, to be sure that all the elements have been rendered correctly.\n */\nbrowser.addEventListener(\"pageshow\", (ev) => {\n    if (ev.persisted) {\n        browser.clearTimeout(pushTimeout);\n        routerBus.trigger(\"ROUTE_CHANGE\");\n    }\n});\n\n/**\n * When clicking internal links, do a loadState instead of a full page reload.\n * This also alows the mobile app to not open an in-app browser for them.\n */\nbrowser.addEventListener(\"click\", (ev) => {\n    if (ev.defaultPrevented || ev.target.closest(\"[contenteditable]\")) {\n        return;\n    }\n    const href = ev.target.closest(\"a\")?.getAttribute(\"href\");\n    if (href && !href.startsWith(\"#\")) {\n        let url;\n        try {\n            // ev.target.href is the full url including current path\n            url = new URL(ev.target.closest(\"a\").href);\n        } catch {\n            return;\n        }\n        if (\n            browser.location.host === url.host &&\n            browser.location.pathname.startsWith(\"/odoo\") &&\n            ([\"/web\", \"/odoo\"].includes(url.pathname) || url.pathname.startsWith(\"/odoo/\")) &&\n            ev.target.target !== \"_blank\"\n        ) {\n            ev.preventDefault();\n            state = router.urlToState(url);\n            if (url.pathname.startsWith(\"/odoo\") && url.hash) {\n                browser.history.pushState({}, \"\", url.href);\n            }\n            new Promise((res) => setTimeout(res, 0)).then(() => routerBus.trigger(\"ROUTE_CHANGE\"));\n        }\n    }\n});\n\n/**\n * @param {string} mode\n */\nfunction makeDebouncedPush(mode) {\n    function doPush() {\n        // Calculates new route based on aggregated search and options\n        const nextState = computeNextState(pushArgs.state, pushArgs.replace);\n        const url = browser.location.origin + router.stateToUrl(nextState);\n        if (!compareUrls(url + browser.location.hash, browser.location.href)) {\n            // If the route changed: pushes or replaces browser state\n            if (mode === \"push\") {\n                // Because doPush is delayed, the history entry will have the wrong name.\n                // We set the document title to what it was at the time of the pushState\n                // call, then push, which generates the history entry with the right title\n                // then restore the title to what it's supposed to be\n                const originalTitle = document.title;\n                document.title = pushArgs.title;\n                browser.history.pushState({ nextState }, \"\", url);\n                document.title = originalTitle;\n            } else {\n                browser.history.replaceState({ nextState }, \"\", url);\n            }\n        } else {\n            // URL didn't change but state might have, update it in place\n            browser.history.replaceState({ nextState }, \"\", browser.location.href);\n        }\n        state = nextState;\n        if (pushArgs.reload) {\n            browser.location.reload();\n        }\n    }\n    /**\n     * @param {object} state\n     * @param {object} options\n     */\n    return function pushOrReplaceState(state, options = {}) {\n        pushArgs.replace ||= options.replace;\n        pushArgs.reload ||= options.reload;\n        pushArgs.title = document.title;\n        Object.assign(pushArgs.state, state);\n        browser.clearTimeout(pushTimeout);\n        const push = () => {\n            doPush();\n            pushTimeout = null;\n            pushArgs = {\n                replace: false,\n                reload: false,\n                state: {},\n            };\n        };\n        if (options.sync) {\n            push();\n        } else {\n            pushTimeout = browser.setTimeout(() => {\n                push();\n            });\n        }\n    };\n}\n\nexport const router = {\n    get current() {\n        return state;\n    },\n    // state <-> url conversions can be patched if needed in a custom webclient.\n    stateToUrl,\n    urlToState,\n    // TODO: stop debouncing these and remove the ugly hack to have the correct title for history entries\n    pushState: makeDebouncedPush(\"push\"),\n    replaceState: makeDebouncedPush(\"replace\"),\n    cancelPushes: () => browser.clearTimeout(pushTimeout),\n    addLockedKey: (key) => _lockedKeys.add(key),\n    hideKeyFromUrl: (key) => _hiddenKeysFromUrl.add(key),\n    skipLoad: false,\n};\n\nstartRouter();\n\nexport function objectToQuery(obj) {\n    const query = {};\n    Object.entries(obj).forEach(([k, v]) => {\n        query[k] = v ? String(v) : v;\n    });\n    return query;\n}\n", "import { registry } from \"../registry\";\n\nexport const titleService = {\n    start() {\n        const titleCounters = {};\n        const titleParts = {};\n\n        function getParts() {\n            return Object.assign({}, titleParts);\n        }\n\n        function setCounters(counters) {\n            for (const key in counters) {\n                const val = counters[key];\n                if (!val) {\n                    delete titleCounters[key];\n                } else {\n                    titleCounters[key] = val;\n                }\n            }\n            updateTitle();\n        }\n\n        function setParts(parts) {\n            for (const key in parts) {\n                const val = parts[key];\n                if (!val) {\n                    delete titleParts[key];\n                } else {\n                    titleParts[key] = val;\n                }\n            }\n            updateTitle();\n        }\n\n        function updateTitle() {\n            const counter = Object.values(titleCounters).reduce((acc, count) => acc + count, 0);\n            const name = Object.values(titleParts).join(\" - \") || \"Odoo\";\n            if (!counter) {\n                document.title = name;\n            } else {\n                document.title = `(${counter}) ${name}`;\n            }\n        }\n\n        return {\n            /**\n             * @returns {string}\n             */\n            get current() {\n                return document.title;\n            },\n            getParts,\n            setCounters,\n            setParts,\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"title\", titleService);\n", "import { useHotkey } from \"../hotkeys/hotkey_hook\";\n\nimport { Component, useRef } from \"@odoo/owl\";\n\n/**\n * Custom checkbox\n *\n * <CheckBox\n *    value=\"boolean\"\n *    disabled=\"boolean\"\n *    onChange=\"_onValueChange\"\n * >\n *    Change the label text\n * </CheckBox>\n *\n * @extends Component\n */\n\nexport class CheckBox extends Component {\n    static template = \"web.CheckBox\";\n    static nextId = 1;\n    static defaultProps = {\n        onChange: () => {},\n    };\n    static props = {\n        id: {\n            type: true,\n            optional: true,\n        },\n        disabled: {\n            type: Boolean,\n            optional: true,\n        },\n        value: {\n            type: Boolean,\n            optional: true,\n        },\n        slots: {\n            type: Object,\n            optional: true,\n        },\n        onChange: {\n            type: Function,\n            optional: true,\n        },\n        className: {\n            type: String,\n            optional: true,\n        },\n        name: {\n            type: String,\n            optional: true,\n        },\n    };\n\n    setup() {\n        this.id = `checkbox-comp-${CheckBox.nextId++}`;\n        this.rootRef = useRef(\"root\");\n\n        // Make it toggleable through the Enter hotkey\n        // when the focus is inside the root element\n        useHotkey(\n            \"Enter\",\n            ({ area }) => {\n                const oldValue = area.querySelector(\"input\").checked;\n                this.props.onChange(!oldValue);\n            },\n            { area: () => this.rootRef.el, bypassEditableProtection: true }\n        );\n    }\n\n    onClick(ev) {\n        if (ev.composedPath().find((el) => [\"INPUT\", \"LABEL\"].includes(el.tagName))) {\n            // The onChange will handle these cases.\n            ev.stopPropagation();\n            return;\n        }\n\n        // Reproduce the click event behavior as if it comes from the input element.\n        const input = this.rootRef.el.querySelector(\"input\");\n        input.focus();\n        if (!this.props.disabled) {\n            ev.stopPropagation();\n            input.checked = !input.checked;\n            this.props.onChange(input.checked);\n        }\n    }\n\n    onChange(ev) {\n        if (!this.props.disabled) {\n            this.props.onChange(ev.target.checked);\n        }\n    }\n}\n", "import { Component, onWillDestroy, onWillStart, useEffect, useRef, useState } from \"@odoo/owl\";\nimport { loadBundle } from \"@web/core/assets\";\nimport { useDebounced } from \"@web/core/utils/timing\";\n\nfunction onResized(ref, callback) {\n    const _ref = typeof ref === \"string\" ? useRef(ref) : ref;\n    const resizeObserver = new ResizeObserver(callback);\n\n    useEffect(\n        (el) => {\n            if (el) {\n                resizeObserver.observe(el);\n                return () => resizeObserver.unobserve(el);\n            }\n        },\n        () => [_ref.el]\n    );\n\n    onWillDestroy(() => {\n        resizeObserver.disconnect();\n    });\n}\n\nexport class CodeEditor extends Component {\n    static template = \"web.CodeEditor\";\n    static components = {};\n    static props = {\n        mode: {\n            type: String,\n            optional: true,\n            validate: (mode) => CodeEditor.MODES.includes(mode),\n        },\n        value: { validate: (v) => typeof v === \"string\", optional: true },\n        readonly: { type: Boolean, optional: true },\n        onChange: { type: Function, optional: true },\n        onBlur: { type: Function, optional: true },\n        class: { type: String, optional: true },\n        theme: {\n            type: String,\n            optional: true,\n            validate: (theme) => CodeEditor.THEMES.includes(theme),\n        },\n        maxLines: { type: Number, optional: true },\n        sessionId: { type: [Number, String], optional: true },\n    };\n    static defaultProps = {\n        readonly: false,\n        value: \"\",\n        onChange: () => {},\n        class: \"\",\n        theme: \"\",\n        sessionId: 1,\n    };\n\n    static MODES = [\"javascript\", \"xml\", \"qweb\", \"scss\", \"python\"];\n    static THEMES = [\"\", \"monokai\"];\n\n    setup() {\n        this.editorRef = useRef(\"editorRef\");\n        this.state = useState({\n            activeMode: undefined,\n        });\n\n        onWillStart(async () => await loadBundle(\"web.ace_lib\"));\n\n        const sessions = {};\n        // The ace library triggers the \"change\" event even if the change is\n        // programmatic. Even worse, it triggers 2 \"change\" events in that case,\n        // one with the empty string, and one with the new value. We only want\n        // to notify the parent of changes done by the user, in the UI, so we\n        // use this flag to filter out noisy \"change\" events.\n        let ignoredAceChange = false;\n        useEffect(\n            (el) => {\n                if (!el) {\n                    return;\n                }\n\n                // keep in closure\n                const aceEditor = window.ace.edit(el);\n                this.aceEditor = aceEditor;\n\n                this.aceEditor.setOptions({\n                    maxLines: this.props.maxLines,\n                    showPrintMargin: false,\n                    useWorker: false,\n                });\n                this.aceEditor.$blockScrolling = true;\n\n                this.aceEditor.on(\"changeMode\", () => {\n                    this.state.activeMode = this.aceEditor.getSession().$modeId.split(\"/\").at(-1);\n                });\n\n                const session = aceEditor.getSession();\n                if (!sessions[this.props.sessionId]) {\n                    sessions[this.props.sessionId] = session;\n                }\n                session.setValue(this.props.value);\n                session.on(\"change\", () => {\n                    if (this.props.onChange && !ignoredAceChange) {\n                        this.props.onChange(this.aceEditor.getValue());\n                    }\n                });\n                this.aceEditor.on(\"blur\", () => {\n                    if (this.props.onBlur) {\n                        this.props.onBlur();\n                    }\n                });\n\n                return () => {\n                    aceEditor.destroy();\n                };\n            },\n            () => [this.editorRef.el]\n        );\n\n        useEffect(\n            (theme) => this.aceEditor.setTheme(theme ? `ace/theme/${theme}` : \"\"),\n            () => [this.props.theme]\n        );\n\n        useEffect(\n            (readonly) => {\n                this.aceEditor.setOptions({\n                    readOnly: readonly,\n                    highlightActiveLine: !readonly,\n                    highlightGutterLine: !readonly,\n                });\n\n                this.aceEditor.renderer.setOptions({\n                    displayIndentGuides: !readonly,\n                    showGutter: !readonly,\n                });\n\n                this.aceEditor.renderer.$cursorLayer.element.style.display = readonly\n                    ? \"none\"\n                    : \"block\";\n            },\n            () => [this.props.readonly]\n        );\n\n        useEffect(\n            (sessionId, mode, value) => {\n                let session = sessions[sessionId];\n                if (session) {\n                    if (session.getValue() !== value) {\n                        ignoredAceChange = true;\n                        session.setValue(value);\n                        ignoredAceChange = false;\n                    }\n                } else {\n                    session = new window.ace.EditSession(value);\n                    session.setUndoManager(new window.ace.UndoManager());\n                    session.setOptions({\n                        useWorker: false,\n                        tabSize: 2,\n                        useSoftTabs: true,\n                    });\n                    session.on(\"change\", () => {\n                        if (this.props.onChange && !ignoredAceChange) {\n                            this.props.onChange(this.aceEditor.getValue());\n                        }\n                    });\n                    sessions[sessionId] = session;\n                }\n                session.setMode(mode ? `ace/mode/${mode}` : \"\");\n                this.aceEditor.setSession(session);\n            },\n            () => [this.props.sessionId, this.props.mode, this.props.value]\n        );\n\n        const debouncedResize = useDebounced(() => {\n            if (this.aceEditor) {\n                this.aceEditor.resize();\n            }\n        }, 250);\n\n        onResized(this.editorRef, debouncedResize);\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\n\nimport { Component, useRef, useState, useExternalListener } from \"@odoo/owl\";\n\nexport class ColorList extends Component {\n    static COLORS = [\n        _t(\"No color\"),\n        _t(\"Red\"),\n        _t(\"Orange\"),\n        _t(\"Yellow\"),\n        _t(\"Cyan\"),\n        _t(\"Purple\"),\n        _t(\"Almond\"),\n        _t(\"Teal\"),\n        _t(\"Blue\"),\n        _t(\"Raspberry\"),\n        _t(\"Green\"),\n        _t(\"Violet\"),\n    ];\n    static template = \"web.ColorList\";\n    static defaultProps = {\n        forceExpanded: false,\n        isExpanded: false,\n    };\n    static props = {\n        canToggle: { type: Boolean, optional: true },\n        colors: Array,\n        forceExpanded: { type: Boolean, optional: true },\n        isExpanded: { type: Boolean, optional: true },\n        onColorSelected: Function,\n        selectedColor: { type: Number, optional: true },\n    };\n\n    setup() {\n        this.colorlistRef = useRef(\"colorlist\");\n        this.state = useState({ isExpanded: this.props.isExpanded });\n        useExternalListener(window, \"click\", this.onOutsideClick);\n    }\n    get colors() {\n        return this.constructor.COLORS;\n    }\n    onColorSelected(id) {\n        this.props.onColorSelected(id);\n        if (!this.props.forceExpanded) {\n            this.state.isExpanded = false;\n        }\n    }\n    onOutsideClick(ev) {\n        if (this.colorlistRef.el.contains(ev.target) || this.props.forceExpanded) {\n            return;\n        }\n        this.state.isExpanded = false;\n    }\n    onToggle(ev) {\n        if (this.props.canToggle) {\n            ev.preventDefault();\n            ev.stopPropagation();\n            this.state.isExpanded = !this.state.isExpanded;\n            this.colorlistRef.el.firstElementChild.focus();\n        }\n    }\n}\n", "import {\n    convertCSSColorToRgba,\n    convertHslToRgb,\n    convertRgbaToCSSColor,\n    convertRgbToHsl,\n} from \"@web/core/utils/colors\";\nimport { uniqueId } from \"@web/core/utils/functions\";\nimport { clamp } from \"@web/core/utils/numbers\";\nimport { debounce, useThrottleForAnimation } from \"@web/core/utils/timing\";\n\nimport { Component, onMounted, onWillUpdateProps, useExternalListener, useRef } from \"@odoo/owl\";\n\nexport class Colorpicker extends Component {\n    static template = \"web.Colorpicker\";\n    static props = {\n        document: { type: true, optional: true },\n        defaultColor: { type: String, optional: true },\n        selectedColor: { type: String, optional: true },\n        noTransparency: { type: Boolean, optional: true },\n        stopClickPropagation: { type: Boolean, optional: true },\n        onColorSelect: { type: Function, optional: true },\n        onColorPreview: { type: Function, optional: true },\n        onInputEnter: { type: Function, optional: true },\n    };\n    static defaultProps = {\n        document: window.document,\n        defaultColor: \"#FF0000\",\n        noTransparency: false,\n        stopClickPropagation: false,\n        onColorSelect: () => {},\n        onColorPreview: () => {},\n        onInputEnter: () => {},\n    };\n\n    setup() {\n        this.pickerFlag = false;\n        this.sliderFlag = false;\n        this.opacitySliderFlag = false;\n        this.colorComponents = {};\n        this.uniqueId = uniqueId(\"colorpicker\");\n        this.selectedHexValue = \"\";\n\n        this.debouncedOnChangeInputs = debounce(this.onChangeInputs.bind(this), 10, true);\n\n        this.elRef = useRef(\"el\");\n        this.colorPickerAreaRef = useRef(\"colorPickerArea\");\n        this.colorPickerPointerRef = useRef(\"colorPickerPointer\");\n        this.colorSliderRef = useRef(\"colorSlider\");\n        this.colorSliderPointerRef = useRef(\"colorSliderPointer\");\n        this.opacitySliderRef = useRef(\"opacitySlider\");\n        this.opacitySliderPointerRef = useRef(\"opacitySliderPointer\");\n\n        // Need to be bound on all documents to work in all possible cases (we\n        // have to be able to start dragging/moving from the colorpicker to\n        // anywhere on the screen, crossing iframes).\n        const documents = [\n            window.top,\n            ...Array.from(window.top.frames).filter((frame) => {\n                try {\n                    const document = frame.document;\n                    return !!document;\n                } catch {\n                    // We cannot access the document (cross origin).\n                    return false;\n                }\n            }),\n        ].map((w) => w.document);\n        this.throttleOnMouseMove = useThrottleForAnimation((ev) => {\n            this.onMouseMovePicker(ev);\n            this.onMouseMoveSlider(ev);\n            this.onMouseMoveOpacitySlider(ev);\n        });\n\n        for (const doc of documents) {\n            useExternalListener(doc, \"mousemove\", this.throttleOnMouseMove);\n            useExternalListener(doc, \"mouseup\", this.onMouseUp.bind(this));\n        }\n        onMounted(async () => {\n            const defaultCssColor = this.props.selectedColor\n                ? this.props.selectedColor\n                : this.props.defaultColor;\n            const rgba = convertCSSColorToRgba(defaultCssColor);\n            if (rgba) {\n                this._updateRgba(rgba.red, rgba.green, rgba.blue, rgba.opacity);\n            }\n\n            this.previewActive = true;\n            this._updateUI();\n        });\n        onWillUpdateProps((newProps) => {\n            const newSelectedColor = newProps.selectedColor\n                ? newProps.selectedColor\n                : newProps.defaultColor;\n            this.setSelectedColor(newSelectedColor);\n        });\n    }\n\n    /**\n     * Sets the currently selected color\n     *\n     * @param {string} color rgb[a]\n     */\n    setSelectedColor(color) {\n        const rgba = convertCSSColorToRgba(color);\n        if (rgba) {\n            const oldPreviewActive = this.previewActive;\n            this.previewActive = false;\n            this._updateRgba(rgba.red, rgba.green, rgba.blue, rgba.opacity);\n            this.previewActive = oldPreviewActive;\n            this._updateUI();\n        }\n    }\n\n    get el() {\n        return this.elRef.el;\n    }\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * Updates input values, color preview, picker and slider pointer positions.\n     *\n     * @private\n     */\n    _updateUI() {\n        // Update inputs\n        for (const [color, value] of Object.entries(this.colorComponents)) {\n            const input = this.el.querySelector(`.o_${color}_input`);\n            if (input) {\n                input.value = value;\n            }\n        }\n\n        // Update picker area and picker pointer position\n        const colorPickerArea = this.colorPickerAreaRef.el;\n        colorPickerArea.style.backgroundColor = `hsl(${this.colorComponents.hue}, 100%, 50%)`;\n        const top = ((100 - this.colorComponents.lightness) * colorPickerArea.clientHeight) / 100;\n        const left = (this.colorComponents.saturation * colorPickerArea.clientWidth) / 100;\n\n        const colorpickerPointer = this.colorPickerPointerRef.el;\n        colorpickerPointer.style.top = top - 5 + \"px\";\n        colorpickerPointer.style.left = left - 5 + \"px\";\n\n        // Update color slider position\n        const colorSlider = this.colorSliderRef.el;\n        const height = colorSlider.clientHeight;\n        const y = (this.colorComponents.hue * height) / 360;\n        this.colorSliderPointerRef.el.style.top = `${Math.round(y - 2)}px`;\n\n        if (!this.props.noTransparency) {\n            // Update opacity slider position\n            const opacitySlider = this.opacitySliderRef.el;\n            const heightOpacity = opacitySlider.clientHeight;\n            const z = heightOpacity * (1 - this.colorComponents.opacity / 100.0);\n            this.opacitySliderPointerRef.el.style.top = `${Math.round(z - 2)}px`;\n\n            // Add gradient color on opacity slider\n            opacitySlider.style.background = `linear-gradient(${this.colorComponents.hex} 0%, transparent 100%)`;\n        }\n    }\n    /**\n     * Updates colors according to given hex value. Opacity is left unchanged.\n     *\n     * @private\n     * @param {string} hex - hexadecimal code\n     */\n    _updateHex(hex) {\n        const rgb = convertCSSColorToRgba(hex);\n        if (!rgb) {\n            return;\n        }\n        Object.assign(\n            this.colorComponents,\n            { hex: hex },\n            rgb,\n            convertRgbToHsl(rgb.red, rgb.green, rgb.blue)\n        );\n        this._updateCssColor();\n    }\n    /**\n     * Updates colors according to given RGB values.\n     *\n     * @private\n     * @param {integer} r\n     * @param {integer} g\n     * @param {integer} b\n     * @param {integer} [a]\n     */\n    _updateRgba(r, g, b, a) {\n        // Remove full transparency in case some lightness is added\n        const opacity = a || this.colorComponents.opacity;\n        if (opacity < 0.1 && (r > 0.1 || g > 0.1 || b > 0.1)) {\n            a = 100;\n        }\n\n        // We update the hexadecimal code by transforming into a css color and\n        // ignoring the opacity (we don't display opacity component in hexa as\n        // not supported on all browsers)\n        const hex = convertRgbaToCSSColor(r, g, b);\n        if (!hex) {\n            return;\n        }\n        Object.assign(\n            this.colorComponents,\n            { red: r, green: g, blue: b },\n            a === undefined ? {} : { opacity: a },\n            { hex: hex },\n            convertRgbToHsl(r, g, b)\n        );\n        this._updateCssColor();\n    }\n    /**\n     * Updates colors according to given HSL values.\n     *\n     * @private\n     * @param {integer} h\n     * @param {integer} s\n     * @param {integer} l\n     */\n    _updateHsl(h, s, l) {\n        // Remove full transparency in case some lightness is added\n        let a = this.colorComponents.opacity;\n        if (a < 0.1 && l > 0.1) {\n            a = 100;\n        }\n\n        const rgb = convertHslToRgb(h, s, l);\n        if (!rgb) {\n            return;\n        }\n        // We receive an hexa as we ignore the opacity\n        const hex = convertRgbaToCSSColor(rgb.red, rgb.green, rgb.blue);\n        Object.assign(\n            this.colorComponents,\n            { hue: h, saturation: s, lightness: l },\n            rgb,\n            { hex: hex },\n            { opacity: a }\n        );\n        this._updateCssColor();\n    }\n    /**\n     * Updates color opacity.\n     *\n     * @private\n     * @param {integer} a\n     */\n    _updateOpacity(a) {\n        if (a < 0 || a > 100) {\n            return;\n        }\n        Object.assign(this.colorComponents, { opacity: a });\n        this._updateCssColor();\n    }\n    /**\n     * Trigger an event to annonce that the widget value has changed\n     *\n     * @private\n     */\n    _colorSelected() {\n        this.props.onColorSelect(this.colorComponents);\n    }\n    /**\n     * Updates css color representation.\n     *\n     * @private\n     */\n    _updateCssColor() {\n        const r = this.colorComponents.red;\n        const g = this.colorComponents.green;\n        const b = this.colorComponents.blue;\n        const a = this.colorComponents.opacity;\n        Object.assign(this.colorComponents, { cssColor: convertRgbaToCSSColor(r, g, b, a) });\n        if (this.previewActive) {\n            this.props.onColorPreview(this.colorComponents);\n        }\n    }\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     * @param {Event} ev\n     */\n    onKeydown(ev) {\n        if (ev.key === \"Enter\") {\n            if (ev.target.tagName === \"INPUT\") {\n                this.onChangeInputs(ev);\n            }\n            ev.preventDefault();\n            this.props.onInputEnter(ev);\n        }\n    }\n    /**\n     * @param {Event} ev\n     */\n    onClick(ev) {\n        if (this.props.stopClickPropagation) {\n            ev.stopPropagation();\n        }\n        //TODO: we should remove it with legacy web_editor\n        ev.__isColorpickerClick = true;\n\n        if (ev.target.dataset.colorMethod === \"hex\" && !this.selectedHexValue) {\n            ev.target.select();\n            this.selectedHexValue = ev.target.value;\n            return;\n        }\n        this.selectedHexValue = \"\";\n    }\n    onMouseUp() {\n        if (this.pickerFlag || this.sliderFlag || this.opacitySliderFlag) {\n            this._colorSelected();\n        }\n        this.pickerFlag = false;\n        this.sliderFlag = false;\n        this.opacitySliderFlag = false;\n    }\n    /**\n     * Updates color when the user starts clicking on the picker.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseDownPicker(ev) {\n        this.pickerFlag = true;\n        ev.preventDefault();\n        this.onMouseMovePicker(ev);\n    }\n    /**\n     * Updates saturation and lightness values on mouse drag over picker.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseMovePicker(ev) {\n        if (!this.pickerFlag) {\n            return;\n        }\n\n        const colorPickerArea = this.colorPickerAreaRef.el;\n        const rect = colorPickerArea.getClientRects()[0];\n        const top = ev.pageY - rect.top;\n        const left = ev.pageX - rect.left;\n        let saturation = Math.round((100 * left) / colorPickerArea.clientWidth);\n        let lightness = Math.round(\n            (100 * (colorPickerArea.clientHeight - top)) / colorPickerArea.clientHeight\n        );\n        saturation = clamp(saturation, 0, 100);\n        lightness = clamp(lightness, 0, 100);\n\n        this._updateHsl(this.colorComponents.hue, saturation, lightness);\n        this._updateUI();\n    }\n    /**\n     * Updates color when user starts clicking on slider.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseDownSlider(ev) {\n        this.sliderFlag = true;\n        ev.preventDefault();\n        this.onMouseMoveSlider(ev);\n    }\n    /**\n     * Updates hue value on mouse drag over slider.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseMoveSlider(ev) {\n        if (!this.sliderFlag) {\n            return;\n        }\n\n        const colorSlider = this.colorSliderRef.el;\n        const y = ev.pageY - colorSlider.getClientRects()[0].top;\n        let hue = Math.round((360 * y) / colorSlider.clientHeight);\n        hue = clamp(hue, 0, 360);\n\n        this._updateHsl(hue, this.colorComponents.saturation, this.colorComponents.lightness);\n        this._updateUI();\n    }\n    /**\n     * Updates opacity when user starts clicking on opacity slider.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseDownOpacitySlider(ev) {\n        this.opacitySliderFlag = true;\n        ev.preventDefault();\n        this.onMouseMoveOpacitySlider(ev);\n    }\n    /**\n     * Updates opacity value on mouse drag over opacity slider.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onMouseMoveOpacitySlider(ev) {\n        if (!this.opacitySliderFlag || this.props.noTransparency) {\n            return;\n        }\n\n        const opacitySlider = this.opacitySliderRef.el;\n        const y = ev.pageY - opacitySlider.getClientRects()[0].top;\n        let opacity = Math.round(100 * (1 - y / opacitySlider.clientHeight));\n        opacity = clamp(opacity, 0, 100);\n\n        this._updateOpacity(opacity);\n        this._updateUI();\n    }\n    /**\n     * Called when input value is changed -> Updates UI: Set picker and slider\n     * position and set colors.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onChangeInputs(ev) {\n        switch (ev.target.dataset.colorMethod) {\n            case \"hex\":\n                // Handled by the \"input\" event (see \"onHexColorInput\").\n                return;\n            case \"rgb\":\n                this._updateRgba(\n                    parseInt(this.el.querySelector(\".o_red_input\").value),\n                    parseInt(this.el.querySelector(\".o_green_input\").value),\n                    parseInt(this.el.querySelector(\".o_blue_input\").value)\n                );\n                break;\n            case \"hsl\":\n                this._updateHsl(\n                    parseInt(this.el.querySelector(\".o_hue_input\").value),\n                    parseInt(this.el.querySelector(\".o_saturation_input\").value),\n                    parseInt(this.el.querySelector(\".o_lightness_input\").value)\n                );\n                break;\n            case \"opacity\":\n                this._updateOpacity(parseInt(this.el.querySelector(\".o_opacity_input\").value));\n                break;\n        }\n        this._updateUI();\n        this._colorSelected();\n    }\n    /**\n     * Called when the hex color input's input event is triggered.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    onHexColorInput(ev) {\n        const hexColorValue = ev.target.value.replaceAll(\"#\", \"\");\n        if (hexColorValue.length === 6) {\n            this._updateHex(`#${hexColorValue}`);\n            this._updateUI();\n            this._colorSelected();\n        }\n    }\n}\n", "import { clamp } from \"@web/core/utils/numbers\";\n/**\n * Lists of colors that contrast well with each other to be used in various\n * visualizations (eg. graphs/charts), both in bright and dark themes.\n */\n\nconst COLORS_ENT_BRIGHT = [\"#875A7B\", \"#A5D8D7\", \"#DCD0D9\"];\nconst COLORS_ENT_DARK = [\"#6B3E66\", \"#147875\", \"#5A395A\"];\nconst COLORS_SM = [\n    \"#4EA7F2\", // Blue\n    \"#EA6175\", // Red\n    \"#43C5B1\", // Teal\n    \"#F4A261\", // Orange\n    \"#8481DD\", // Purple\n    \"#FFD86D\", // Yellow\n];\nconst COLORS_MD = [\n    \"#4EA7F2\", // Blue #1\n    \"#3188E6\", // Blue #2\n    \"#43C5B1\", // Teal #1\n    \"#00A78D\", // Teal #2\n    \"#EA6175\", // Red #1\n    \"#CE4257\", // Red #2\n    \"#F4A261\", // Orange #1\n    \"#F48935\", // Orange #2\n    \"#8481DD\", // Purple #1\n    \"#5752D1\", // Purple #2\n    \"#FFD86D\", // Yellow #1\n    \"#FFBC2C\", // Yellow #2\n];\nconst COLORS_LG = [\n    \"#4EA7F2\", // Blue #1\n    \"#3188E6\", // Blue #2\n    \"#056BD9\", // Blue #3\n    \"#A76DBC\", // Violet #1\n    \"#7F4295\", // Violet #2\n    \"#6D2387\", // Violet #3\n    \"#EA6175\", // Red #1\n    \"#CE4257\", // Red #2\n    \"#982738\", // Red #3\n    \"#43C5B1\", // Teal #1\n    \"#00A78D\", // Teal #2\n    \"#0E8270\", // Teal #3\n    \"#F4A261\", // Orange #1\n    \"#F48935\", // Orange #2\n    \"#BE5D10\", // Orange #3\n    \"#8481DD\", // Purple #1\n    \"#5752D1\", // Purple #2\n    \"#3A3580\", // Purple #3\n    \"#A4A8B6\", // Gray #1\n    \"#7E8290\", // Gray #2\n    \"#545B70\", // Gray #3\n    \"#FFD86D\", // Yellow #1\n    \"#FFBC2C\", // Yellow #2\n    \"#C08A16\", // Yellow #3\n];\nconst COLORS_XL = [\n    \"#4EA7F2\", // Blue #1\n    \"#3188E6\", // Blue #2\n    \"#056BD9\", // Blue #3\n    \"#155193\", // Blue #4\n    \"#A76DBC\", // Violet #1\n    \"#7F4295\", // Violet #1\n    \"#6D2387\", // Violet #1\n    \"#4F1565\", // Violet #1\n    \"#EA6175\", // Red #1\n    \"#CE4257\", // Red #2\n    \"#982738\", // Red #3\n    \"#791B29\", // Red #4\n    \"#43C5B1\", // Teal #1\n    \"#00A78D\", // Teal #2\n    \"#0E8270\", // Teal #3\n    \"#105F53\", // Teal #4\n    \"#F4A261\", // Orange #1\n    \"#F48935\", // Orange #2\n    \"#BE5D10\", // Orange #3\n    \"#7D380D\", // Orange #4\n    \"#8481DD\", // Purple #1\n    \"#5752D1\", // Purple #2\n    \"#3A3580\", // Purple #3\n    \"#26235F\", // Purple #4\n    \"#A4A8B6\", // Grey #1\n    \"#7E8290\", // Grey #2\n    \"#545B70\", // Grey #3\n    \"#3F4250\", // Grey #4\n    \"#FFD86D\", // Yellow #1\n    \"#FFBC2C\", // Yellow #2\n    \"#C08A16\", // Yellow #3\n    \"#936A12\", // Yellow #4\n];\n\n/**\n * @param {string} colorScheme\n * @param {string} paletteName\n * @returns {array}\n */\nexport function getColors(colorScheme, paletteName) {\n    switch (paletteName) {\n        case \"odoo\":\n            return colorScheme === \"dark\" ? COLORS_ENT_DARK : COLORS_ENT_BRIGHT;\n        case \"sm\":\n            return COLORS_SM;\n        case \"md\":\n            return COLORS_MD;\n        case \"lg\":\n            return COLORS_LG;\n        default:\n            return COLORS_XL;\n    }\n}\n\n/**\n * @param {number} index\n * @param {string} colorScheme\n * @returns {string}\n */\nexport function getColor(index, colorScheme, paletteSizeOrName) {\n    let paletteName;\n    if (paletteSizeOrName === \"odoo\") {\n        paletteName = \"odoo\";\n    } else if (paletteSizeOrName <= 6 || paletteSizeOrName === \"sm\") {\n        paletteName = \"sm\";\n    } else if (paletteSizeOrName <= 12 || paletteSizeOrName === \"md\") {\n        paletteName = \"md\";\n    } else if (paletteSizeOrName <= 24 || paletteSizeOrName === \"lg\") {\n        paletteName = \"lg\";\n    } else {\n        paletteName = \"xl\";\n    }\n    const colors = getColors(colorScheme, paletteName);\n    return colors[index % colors.length];\n}\n\nexport const DEFAULT_BG = \"#d3d3d3\";\n\nexport function getBorderWhite(colorScheme) {\n    return colorScheme === \"dark\" ? \"rgba(38, 42, 54, .2)\" : \"rgba(249,250,251, .2)\";\n}\n\nconst RGB_REGEX = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i;\n\n/**\n * @param {string} hex\n * @param {number} opacity\n * @returns {string}\n */\nexport function hexToRGBA(hex, opacity) {\n    const rgb = RGB_REGEX.exec(hex)\n        .slice(1, 4)\n        .map((n) => parseInt(n, 16))\n        .join(\",\");\n    return `rgba(${rgb},${opacity})`;\n}\n\n/**\n * Used to return custom colors depending on the color scheme\n * @param {string} colorScheme\n * @param {string} brightModeColor\n * @param {string} darkModeColor\n * @returns {string|Number|Boolean}\n */\n\nexport function getCustomColor(colorScheme, brightModeColor, darkModeColor) {\n    if (darkModeColor === undefined) {\n        return brightModeColor;\n    } else {\n        return colorScheme === \"dark\" ? darkModeColor : brightModeColor;\n    }\n}\n\n/**\n * Used to lighten a color\n * @param {string} color\n * @param {number} factor\n * @returns {string}\n */\nexport function lightenColor(color, factor) {\n    factor = clamp(factor, 0, 1);\n\n    let r = parseInt(color.substring(1, 3), 16);\n    let g = parseInt(color.substring(3, 5), 16);\n    let b = parseInt(color.substring(5, 7), 16);\n\n    r = Math.round(r + (255 - r) * factor);\n    g = Math.round(g + (255 - g) * factor);\n    b = Math.round(b + (255 - b) * factor);\n\n    r = r.toString(16).padStart(2, \"0\");\n    g = g.toString(16).padStart(2, \"0\");\n    b = b.toString(16).padStart(2, \"0\");\n\n    return `#${r}${g}${b}`;\n}\n\n/**\n * Used to darken a color\n * @param {string} color\n * @param {number} factor\n * @returns {string}\n */\nexport function darkenColor(color, factor) {\n    factor = clamp(factor, 0, 1);\n\n    let r = parseInt(color.substring(1, 3), 16);\n    let g = parseInt(color.substring(3, 5), 16);\n    let b = parseInt(color.substring(5, 7), 16);\n\n    r = Math.round(r * (1 - factor));\n    g = Math.round(g * (1 - factor));\n    b = Math.round(b * (1 - factor));\n\n    r = r.toString(16).padStart(2, \"0\");\n    g = g.toString(16).padStart(2, \"0\");\n    b = b.toString(16).padStart(2, \"0\");\n\n    return `#${r}${g}${b}`;\n}\n", "import { Dialog } from \"../dialog/dialog\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { useChildRef } from \"@web/core/utils/hooks\";\n\nimport { Component } from \"@odoo/owl\";\n\nexport const deleteConfirmationMessage = _t(\n    `Ready to make your record disappear into thin air? Are you sure?\nIt will be gone forever!\n\nThink twice before you click that 'Delete' button!`\n);\n\nexport class ConfirmationDialog extends Component {\n    static template = \"web.ConfirmationDialog\";\n    static components = { Dialog };\n    static props = {\n        close: Function,\n        title: {\n            validate: (m) => {\n                return (\n                    typeof m === \"string\" ||\n                    (typeof m === \"object\" && typeof m.toString === \"function\")\n                );\n            },\n            optional: true,\n        },\n        body: { type: String, optional: true },\n        confirm: { type: Function, optional: true },\n        confirmLabel: { type: String, optional: true },\n        confirmClass: { type: String, optional: true },\n        cancel: { type: Function, optional: true },\n        cancelLabel: { type: String, optional: true },\n        dismiss: { type: Function, optional: true },\n    };\n    static defaultProps = {\n        confirmLabel: _t(\"Ok\"),\n        cancelLabel: _t(\"Cancel\"),\n        confirmClass: \"btn-primary\",\n        title: _t(\"Confirmation\"),\n    };\n\n    setup() {\n        this.env.dialogData.dismiss = () => this._dismiss();\n        this.modalRef = useChildRef();\n        this.isProcess = false;\n    }\n\n    async _cancel() {\n        return this.execButton(this.props.cancel);\n    }\n\n    async _confirm() {\n        return this.execButton(this.props.confirm);\n    }\n\n    async _dismiss() {\n        return this.execButton(this.props.dismiss || this.props.cancel);\n    }\n\n    setButtonsDisabled(disabled) {\n        this.isProcess = disabled;\n        if (!this.modalRef.el) {\n            return; // safety belt for stable versions\n        }\n        for (const button of [...this.modalRef.el.querySelectorAll(\".modal-footer button\")]) {\n            button.disabled = disabled;\n        }\n    }\n\n    async execButton(callback) {\n        if (this.isProcess) {\n            return;\n        }\n        this.setButtonsDisabled(true);\n        if (callback) {\n            let shouldClose;\n            try {\n                shouldClose = await callback();\n            } catch (e) {\n                this.props.close();\n                throw e;\n            }\n            if (shouldClose === false) {\n                this.setButtonsDisabled(false);\n                return;\n            }\n        }\n        this.props.close();\n    }\n}\n\nexport class AlertDialog extends ConfirmationDialog {\n    static template = \"web.AlertDialog\";\n    static props = {\n        ...ConfirmationDialog.props,\n        contentClass: { type: String, optional: true },\n    };\n    static defaultProps = {\n        ...ConfirmationDialog.defaultProps,\n        title: _t(\"Alert\"),\n    };\n}\n", "import { evaluateExpr, parseExpr } from \"./py_js/py\";\nimport { BUILTINS } from \"./py_js/py_builtin\";\nimport { evaluate } from \"./py_js/py_interpreter\";\n\n/**\n * @typedef {{[key: string]: any}} Context\n * @typedef {Context | string | undefined} ContextDescription\n */\n\n/**\n * Create an evaluated context from an arbitrary list of context representations.\n * The evaluated context in construction is used along the way to evaluate further parts.\n *\n * @param {ContextDescription[]} contexts\n * @param {Context} [initialEvaluationContext] optional evaluation context to start from.\n * @returns {Context}\n */\nexport function makeContext(contexts, initialEvaluationContext) {\n    const evaluationContext = Object.assign({}, initialEvaluationContext);\n    const context = {};\n    for (let ctx of contexts) {\n        if (ctx !== \"\") {\n            ctx = typeof ctx === \"string\" ? evaluateExpr(ctx, evaluationContext) : ctx;\n            Object.assign(context, ctx);\n            Object.assign(evaluationContext, context); // is this behavior really wanted ?\n        }\n    }\n    return context;\n}\n\n/**\n * Extract a partial list of variable names found in the AST.\n * Note that it is not complete. It is used as an heuristic to avoid\n * evaluating expressions that we know for sure will fail.\n *\n * @param {AST} ast\n * @returns string[]\n */\nfunction getPartialNames(ast) {\n    if (ast.type === 5) {\n        return [ast.value];\n    }\n    if (ast.type === 6) {\n        return getPartialNames(ast.right);\n    }\n    if (ast.type === 14 || ast.type === 7) {\n        return getPartialNames(ast.left).concat(getPartialNames(ast.right));\n    }\n    if (ast.type === 15) {\n        return getPartialNames(ast.obj);\n    }\n    return [];\n}\n\n/**\n * Allow to evaluate a context with an incomplete evaluation context. The evaluated context only\n * contains keys whose values are static or can be evaluated with the given evaluation context.\n *\n * @param {string} context\n * @param {Object} [evaluationContext={}]\n * @returns {Context}\n */\nexport function evalPartialContext(_context, evaluationContext = {}) {\n    const ast = parseExpr(_context);\n    const context = {};\n    for (const key in ast.value) {\n        const value = ast.value[key];\n        if (\n            getPartialNames(value).some((name) => !(name in evaluationContext || name in BUILTINS))\n        ) {\n            continue;\n        }\n        try {\n            context[key] = evaluate(value, evaluationContext);\n        } catch {\n            // ignore this key as we can't evaluate its value\n        }\n    }\n    return context;\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { Tooltip } from \"@web/core/tooltip/tooltip\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\nimport { Component, useRef } from \"@odoo/owl\";\n\nexport class CopyButton extends Component {\n    static template = \"web.CopyButton\";\n    static props = {\n        className: { type: String, optional: true },\n        copyText: { type: String, optional: true },\n        disabled: { type: Boolean, optional: true },\n        successText: { type: String, optional: true },\n        icon: { type: String, optional: true },\n        content: { type: [String, Object], optional: true },\n    };\n\n    setup() {\n        this.button = useRef(\"button\");\n        this.popover = usePopover(Tooltip);\n    }\n\n    showTooltip() {\n        this.popover.open(this.button.el, { tooltip: this.props.successText });\n        browser.setTimeout(this.popover.close, 800);\n    }\n\n    async onClick() {\n        let write;\n        // any kind of content can be copied into the clipboard using\n        // the appropriate native methods\n        if (typeof this.props.content === \"string\" || this.props.content instanceof String) {\n            write = (value) => browser.navigator.clipboard.writeText(value);\n        } else {\n            write = (value) => browser.navigator.clipboard.write(value);\n        }\n        try {\n            await write(this.props.content);\n        } catch (error) {\n            return browser.console.warn(error);\n        }\n        this.showTooltip();\n    }\n}\n", "import { formatFloat, humanNumber } from \"@web/core/utils/numbers\";\nimport { session } from \"@web/session\";\nimport { nbsp } from \"@web/core/utils/strings\";\n\nexport const currencies = session.currencies || {};\n// to make sure code is reading currencies from here\ndelete session.currencies;\n\nexport function getCurrency(id) {\n    return currencies[id];\n}\n\n/**\n * Returns a string representing a monetary value. The result takes into account\n * the user settings (to display the correct decimal separator, currency, ...).\n *\n * @param {number} value the value that should be formatted\n * @param {number} [currencyId] the id of the 'res.currency' to use\n * @param {Object} [options]\n *   additional options to override the values in the python description of the\n *   field.\n * @param {Object} [options.data] a mapping of field names to field values,\n *   required with options.currencyField\n * @param {boolean} [options.noSymbol] this currency has not a sympbol\n * @param {boolean} [options.humanReadable] if true, large numbers are formatted\n *   to a human readable format.\n * @param {[number, number]} [options.digits] the number of digits that should\n *   be used, instead of the default digits precision in the field.  The first\n *   number is always ignored (legacy constraint)\n * @returns {string}\n */\nexport function formatCurrency(amount, currencyId, options = {}) {\n    const currency = getCurrency(currencyId);\n    const digits = options.digits || (currency && currency.digits);\n\n    let formattedAmount;\n    if (options.humanReadable) {\n        formattedAmount = humanNumber(amount, { decimals: digits ? digits[1] : 2 });\n    } else {\n        formattedAmount = formatFloat(amount, { digits });\n    }\n\n    if (!currency || options.noSymbol) {\n        return formattedAmount;\n    }\n    const formatted = [currency.symbol, formattedAmount];\n    if (currency.position === \"after\") {\n        formatted.reverse();\n    }\n    return formatted.join(nbsp);\n}\n", "import { onPatched, onWillRender, useEffect, useRef } from \"@odoo/owl\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\nimport { useService } from \"@web/core/utils/hooks\";\n\n/**\n * @param {import(\"./datetimepicker_service\").DateTimePickerHookParams} hookParams\n */\nexport function useDateTimePicker(hookParams) {\n    const datetimePicker = useService(\"datetime_picker\");\n    if (typeof hookParams.target === \"string\") {\n        const target = useRef(hookParams.target);\n        Object.defineProperty(hookParams, \"target\", {\n            get() {\n                return target.el;\n            },\n        });\n    }\n    const inputRefs = [useRef(\"start-date\"), useRef(\"end-date\")];\n    const createPopover = hookParams.createPopover ?? usePopover;\n    const getInputs = () => inputRefs.map((ref) => ref?.el);\n    const { computeBasePickerProps, state, open, focusIfNeeded, enable } = datetimePicker.create(\n        hookParams,\n        getInputs,\n        createPopover\n    );\n    onWillRender(computeBasePickerProps);\n    useEffect(enable, getInputs);\n\n    // Note: this `onPatched` callback must be called after the `useEffect` since\n    // the effect may change input values that will be selected by the patch callback.\n    onPatched(focusIfNeeded);\n    return { state, open };\n}\n", "import { Component } from \"@odoo/owl\";\nimport { omit } from \"../utils/objects\";\nimport { useDateTimePicker } from \"./datetime_hook\";\nimport { DateTimePicker } from \"./datetime_picker\";\n\n/**\n * @typedef {import(\"./datetime_picker\").DateTimePickerProps & {\n *  format?: string;\n *  id?: string;\n *  onApply?: (value: DateTime) => any;\n *  onChange?: (value: DateTime) => any;\n *  placeholder?: string;\n * }} DateTimeInputProps\n */\n\nconst dateTimeInputOwnProps = {\n    format: { type: String, optional: true },\n    id: { type: String, optional: true },\n    onChange: { type: Function, optional: true },\n    onApply: { type: Function, optional: true },\n    placeholder: { type: String, optional: true },\n};\n\n/** @extends {Component<DateTimeInputProps>} */\nexport class DateTimeInput extends Component {\n    static props = {\n        ...DateTimePicker.props,\n        ...dateTimeInputOwnProps,\n    };\n\n    static template = \"web.DateTimeInput\";\n\n    setup() {\n        const getPickerProps = () => omit(this.props, ...Object.keys(dateTimeInputOwnProps));\n\n        useDateTimePicker({\n            format: this.props.format,\n            get pickerProps() {\n                return getPickerProps();\n            },\n            onApply: (...args) => this.props.onApply?.(...args),\n            onChange: (...args) => this.props.onChange?.(...args),\n        });\n    }\n}\n", "import { Component, onWillRender, onWillUpdateProps, useState } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport {\n    MAX_VALID_DATE,\n    MIN_VALID_DATE,\n    clampDate,\n    is24HourFormat,\n    isInRange,\n    isMeridiemFormat,\n    today,\n} from \"../l10n/dates\";\nimport { localization } from \"../l10n/localization\";\nimport { ensureArray } from \"../utils/arrays\";\n\nconst { DateTime, Info } = luxon;\n\n/**\n * @typedef DateItem\n * @property {string} id\n * @property {boolean} includesToday\n * @property {boolean} isOutOfRange\n * @property {boolean} isValid\n * @property {string} label\n * @property {DateRange} range\n * @property {string} extraClass\n *\n * @typedef {\"today\" | NullableDateTime} DateLimit\n *\n * @typedef {[DateTime, DateTime]} DateRange\n *\n * @typedef {luxon[\"DateTime\"][\"prototype\"]} DateTime\n *\n * @typedef DateTimePickerProps\n * @property {number} [focusedDateIndex=0]\n * @property {boolean} [showWeekNumbers]\n * @property {DaysOfWeekFormat} [daysOfWeekFormat=\"short\"]\n * @property {DateLimit} [maxDate]\n * @property {PrecisionLevel} [maxPrecision=\"decades\"]\n * @property {DateLimit} [minDate]\n * @property {PrecisionLevel} [minPrecision=\"days\"]\n * @property {(value: DateTime | DateRange, unit: \"date\" | \"time\") => any} [onSelect]\n * @property {boolean} [range]\n * @property {number} [rounding=5] the rounding in minutes, pass 0 to show seconds, pass 1 to avoid\n *  rounding minutes without displaying seconds.\n * @property {{ buttons?: any }} [slots]\n * @property {\"date\" | \"datetime\"} [type]\n * @property {NullableDateTime | NullableDateRange} [value]\n * @property {(date: DateTime) => boolean} [isDateValid]\n * @property {(date: DateTime) => string} [dayCellClass]\n *\n * @typedef {DateItem | MonthItem} Item\n *\n * @typedef MonthItem\n * @property {[string, string][]} daysOfWeek\n * @property {string} id\n * @property {number} number\n * @property {WeekItem[]} weeks\n *\n * @typedef {import(\"@web/core/l10n/dates\").NullableDateTime} NullableDateTime\n *\n * @typedef {import(\"@web/core/l10n/dates\").NullableDateRange} NullableDateRange\n *\n * @typedef PrecisionInfo\n * @property {(date: DateTime, params: Partial<DateTimePickerProps>) => string} getTitle\n * @property {(date: DateTime, params: Partial<DateTimePickerProps>) => Item[]} getItems\n * @property {string} mainTitle\n * @property {string} nextTitle\n * @property {string} prevTitle\n * @property {Record<string, number>} step\n *\n * @typedef {\"days\" | \"months\" | \"years\" | \"decades\"} PrecisionLevel\n *\n * @typedef {\"short\" | \"narrow\"} DaysOfWeekFormat\n *\n * @typedef WeekItem\n * @property {DateItem[]} days\n * @property {number} number\n */\n\n/**\n * @param {DateTime} date\n */\nconst getStartOfDecade = (date) => Math.floor(date.year / 10) * 10;\n\n/**\n * @param {DateTime} date\n */\nconst getStartOfCentury = (date) => Math.floor(date.year / 100) * 100;\n\n/**\n * @param {DateTime} date\n */\nconst getStartOfWeek = (date) => {\n    const { weekStart } = localization;\n    return date.set({ weekday: date.weekday < weekStart ? weekStart - 7 : weekStart });\n};\n\n/**\n * @param {number} min\n * @param {number} max\n */\nconst numberRange = (min, max) => [...Array(max - min)].map((_, i) => i + min);\n\n/**\n * @param {NullableDateTime | \"today\"} value\n * @param {NullableDateTime | \"today\"} defaultValue\n */\nconst parseLimitDate = (value, defaultValue) =>\n    clampDate(value === \"today\" ? today() : value || defaultValue, MIN_VALID_DATE, MAX_VALID_DATE);\n\n/**\n * @param {Object} params\n * @param {boolean} [params.isOutOfRange=false]\n * @param {boolean} [params.isValid=true]\n * @param {keyof DateTime} params.label\n * @param {string} [params.extraClass]\n * @param {[DateTime, DateTime]} params.range\n * @returns {DateItem}\n */\nconst toDateItem = ({ isOutOfRange = false, isValid = true, label, range, extraClass }) => ({\n    id: range[0].toISODate(),\n    includesToday: isInRange(today(), range),\n    isOutOfRange,\n    isValid,\n    label: String(range[0][label]),\n    range,\n    extraClass,\n});\n\n/**\n * @param {DateItem[]} weekDayItems\n * @returns {WeekItem}\n */\nconst toWeekItem = (weekDayItems) => ({\n    number: weekDayItems[3].range[0].weekNumber,\n    days: weekDayItems,\n});\n\n// Time constants\nconst HOURS = numberRange(0, 24).map((hour) => [hour, String(hour)]);\nconst MINUTES = numberRange(0, 60).map((minute) => [minute, String(minute || 0).padStart(2, \"0\")]);\nconst SECONDS = [...MINUTES];\nconst MERIDIEMS = [\"AM\", \"PM\"];\n\n/**\n * Precision levels\n * @type {Map<PrecisionLevel, PrecisionInfo>}\n */\nconst PRECISION_LEVELS = new Map()\n    .set(\"days\", {\n        mainTitle: _t(\"Select month\"),\n        nextTitle: _t(\"Next month\"),\n        prevTitle: _t(\"Previous month\"),\n        step: { month: 1 },\n        getTitle: (date, { additionalMonth }) => {\n            const titles = [`${date.monthLong} ${date.year}`];\n            if (additionalMonth) {\n                const next = date.plus({ month: 1 });\n                titles.push(`${next.monthLong} ${next.year}`);\n            }\n            return titles;\n        },\n        getItems: (\n            date,\n            { additionalMonth, maxDate, minDate, showWeekNumbers, isDateValid, dayCellClass }\n        ) => {\n            const startDates = [date];\n            if (additionalMonth) {\n                startDates.push(date.plus({ month: 1 }));\n            }\n\n            /** @type {WeekItem[]} */\n            const lastWeeks = [];\n            let shouldAddLastWeek = false;\n\n            const dayItems = startDates.map((date, i) => {\n                const monthRange = [date.startOf(\"month\"), date.endOf(\"month\")];\n                /** @type {WeekItem[]} */\n                const weeks = [];\n\n                // Generate 6 weeks for current month\n                let startOfNextWeek = getStartOfWeek(monthRange[0]);\n                for (let w = 0; w < WEEKS_PER_MONTH; w++) {\n                    const weekDayItems = [];\n                    // Generate all days of the week\n                    for (let d = 0; d < DAYS_PER_WEEK; d++) {\n                        const day = startOfNextWeek.plus({ day: d });\n                        const range = [day, day.endOf(\"day\")];\n                        const dayItem = toDateItem({\n                            isOutOfRange: !isInRange(day, monthRange),\n                            isValid: isInRange(range, [minDate, maxDate]) && isDateValid?.(day),\n                            label: \"day\",\n                            range,\n                            extraClass: dayCellClass?.(day) || \"\",\n                        });\n                        weekDayItems.push(dayItem);\n                        if (d === DAYS_PER_WEEK - 1) {\n                            startOfNextWeek = day.plus({ day: 1 });\n                        }\n                        if (w === WEEKS_PER_MONTH - 1) {\n                            shouldAddLastWeek ||= !dayItem.isOutOfRange;\n                        }\n                    }\n\n                    const weekItem = toWeekItem(weekDayItems);\n                    if (w === WEEKS_PER_MONTH - 1) {\n                        lastWeeks.push(weekItem);\n                    } else {\n                        weeks.push(weekItem);\n                    }\n                }\n\n                // Generate days of week labels\n                const daysOfWeek = weeks[0].days.map((d) => [\n                    d.range[0].weekdayShort,\n                    d.range[0].weekdayLong,\n                    Info.weekdays(\"narrow\", { locale: d.range[0].locale })[d.range[0].weekday - 1],\n                ]);\n                if (showWeekNumbers) {\n                    daysOfWeek.unshift([\"#\", _t(\"Week numbers\"), \"#\"]);\n                }\n\n                return {\n                    id: `__month__${i}`,\n                    number: monthRange[0].month,\n                    daysOfWeek,\n                    weeks,\n                };\n            });\n\n            if (shouldAddLastWeek) {\n                // Add last empty week item if the other month has an extra week\n                for (let i = 0; i < dayItems.length; i++) {\n                    dayItems[i].weeks.push(lastWeeks[i]);\n                }\n            }\n\n            return dayItems;\n        },\n    })\n    .set(\"months\", {\n        mainTitle: _t(\"Select year\"),\n        nextTitle: _t(\"Next year\"),\n        prevTitle: _t(\"Previous year\"),\n        step: { year: 1 },\n        getTitle: (date) => String(date.year),\n        getItems: (date, { maxDate, minDate }) => {\n            const startOfYear = date.startOf(\"year\");\n            return numberRange(0, 12).map((i) => {\n                const startOfMonth = startOfYear.plus({ month: i });\n                const range = [startOfMonth, startOfMonth.endOf(\"month\")];\n                return toDateItem({\n                    isValid: isInRange(range, [minDate, maxDate]),\n                    label: \"monthShort\",\n                    range,\n                });\n            });\n        },\n    })\n    .set(\"years\", {\n        mainTitle: _t(\"Select decade\"),\n        nextTitle: _t(\"Next decade\"),\n        prevTitle: _t(\"Previous decade\"),\n        step: { year: 10 },\n        getTitle: (date) => `${getStartOfDecade(date) - 1} - ${getStartOfDecade(date) + 10}`,\n        getItems: (date, { maxDate, minDate }) => {\n            const startOfDecade = date.startOf(\"year\").set({ year: getStartOfDecade(date) });\n            return numberRange(-GRID_MARGIN, GRID_COUNT + GRID_MARGIN).map((i) => {\n                const startOfYear = startOfDecade.plus({ year: i });\n                const range = [startOfYear, startOfYear.endOf(\"year\")];\n                return toDateItem({\n                    isOutOfRange: i < 0 || i >= GRID_COUNT,\n                    isValid: isInRange(range, [minDate, maxDate]),\n                    label: \"year\",\n                    range,\n                });\n            });\n        },\n    })\n    .set(\"decades\", {\n        mainTitle: _t(\"Select century\"),\n        nextTitle: _t(\"Next century\"),\n        prevTitle: _t(\"Previous century\"),\n        step: { year: 100 },\n        getTitle: (date) => `${getStartOfCentury(date) - 10} - ${getStartOfCentury(date) + 100}`,\n        getItems: (date, { maxDate, minDate }) => {\n            const startOfCentury = date.startOf(\"year\").set({ year: getStartOfCentury(date) });\n            return numberRange(-GRID_MARGIN, GRID_COUNT + GRID_MARGIN).map((i) => {\n                const startOfDecade = startOfCentury.plus({ year: i * 10 });\n                const range = [startOfDecade, startOfDecade.plus({ year: 10, millisecond: -1 })];\n                return toDateItem({\n                    label: \"year\",\n                    isOutOfRange: i < 0 || i >= GRID_COUNT,\n                    isValid: isInRange(range, [minDate, maxDate]),\n                    range,\n                });\n            });\n        },\n    });\n\n// Other constants\nconst GRID_COUNT = 10;\nconst GRID_MARGIN = 1;\nconst NULLABLE_DATETIME_PROPERTY = [DateTime, { value: false }, { value: null }];\n\nconst DAYS_PER_WEEK = 7;\nconst WEEKS_PER_MONTH = 6;\n\n/** @extends {Component<DateTimePickerProps>} */\nexport class DateTimePicker extends Component {\n    static props = {\n        focusedDateIndex: { type: Number, optional: true },\n        showWeekNumbers: { type: Boolean, optional: true },\n        daysOfWeekFormat: { type: String, optional: true },\n        maxDate: { type: [NULLABLE_DATETIME_PROPERTY, { value: \"today\" }], optional: true },\n        maxPrecision: {\n            type: [...PRECISION_LEVELS.keys()].map((value) => ({ value })),\n            optional: true,\n        },\n        minDate: { type: [NULLABLE_DATETIME_PROPERTY, { value: \"today\" }], optional: true },\n        minPrecision: {\n            type: [...PRECISION_LEVELS.keys()].map((value) => ({ value })),\n            optional: true,\n        },\n        onSelect: { type: Function, optional: true },\n        range: { type: Boolean, optional: true },\n        rounding: { type: Number, optional: true },\n        slots: {\n            type: Object,\n            shape: {\n                bottom_left: { type: Object, optional: true },\n                buttons: { type: Object, optional: true },\n            },\n            optional: true,\n        },\n        type: { type: [{ value: \"date\" }, { value: \"datetime\" }], optional: true },\n        value: {\n            type: [\n                NULLABLE_DATETIME_PROPERTY,\n                { type: Array, element: NULLABLE_DATETIME_PROPERTY },\n            ],\n            optional: true,\n        },\n        isDateValid: { type: Function, optional: true },\n        dayCellClass: { type: Function, optional: true },\n        tz: { type: String, optional: true },\n    };\n\n    static defaultProps = {\n        focusedDateIndex: 0,\n        daysOfWeekFormat: \"short\",\n        maxPrecision: \"decades\",\n        minPrecision: \"days\",\n        rounding: 5,\n        type: \"datetime\",\n    };\n\n    static template = \"web.DateTimePicker\";\n\n    //-------------------------------------------------------------------------\n    // Getters\n    //-------------------------------------------------------------------------\n\n    get activePrecisionLevel() {\n        return PRECISION_LEVELS.get(this.state.precision);\n    }\n\n    get isLastPrecisionLevel() {\n        return (\n            this.allowedPrecisionLevels.indexOf(this.state.precision) ===\n            this.allowedPrecisionLevels.length - 1\n        );\n    }\n\n    get titles() {\n        return ensureArray(this.title);\n    }\n\n    //-------------------------------------------------------------------------\n    // Lifecycle\n    //-------------------------------------------------------------------------\n\n    setup() {\n        this.availableHours = HOURS;\n        this.availableMinutes = MINUTES;\n        /** @type {PrecisionLevel[]} */\n        this.allowedPrecisionLevels = [];\n        /** @type {Item[]} */\n        this.items = [];\n        this.title = \"\";\n        this.shouldAdjustFocusDate = false;\n\n        this.state = useState({\n            /** @type {DateTime | null} */\n            focusDate: null,\n            /** @type {DateTime | null} */\n            hoveredDate: null,\n            /** @type {[number, number, number][]} */\n            timeValues: [],\n            /** @type {PrecisionLevel} */\n            precision: this.props.minPrecision,\n        });\n\n        this.onPropsUpdated(this.props);\n        onWillUpdateProps((nextProps) => this.onPropsUpdated(nextProps));\n\n        onWillRender(() => this.onWillRender());\n    }\n\n    /**\n     * @param {DateTimePickerProps} props\n     */\n    onPropsUpdated(props) {\n        /** @type {[NullableDateTime] | NullableDateRange} */\n        this.values = ensureArray(props.value).map((value) =>\n            value && !value.isValid ? null : value\n        );\n        this.availableHours = HOURS;\n        this.availableMinutes = MINUTES.filter((minute) => !(minute[0] % props.rounding));\n        this.availableSeconds = props.rounding ? [] : SECONDS;\n        this.allowedPrecisionLevels = this.filterPrecisionLevels(\n            props.minPrecision,\n            props.maxPrecision\n        );\n\n        this.additionalMonth = props.range && !this.env.isSmall;\n        this.maxDate = parseLimitDate(props.maxDate, MAX_VALID_DATE);\n        this.minDate = parseLimitDate(props.minDate, MIN_VALID_DATE);\n        if (this.props.type === \"date\") {\n            this.maxDate = this.maxDate.endOf(\"day\");\n            this.minDate = this.minDate.startOf(\"day\");\n        }\n\n        if (this.maxDate < this.minDate) {\n            throw new Error(`DateTimePicker error: given \"maxDate\" comes before \"minDate\".`);\n        }\n\n        const timeValues = this.values.map((val, index) => [\n            index === 1 && !this.values[1]\n                ? (val || DateTime.local()).hour + 1\n                : (val || DateTime.local()).hour,\n            val?.minute || 0,\n            val?.second || 0,\n        ]);\n        if (props.range) {\n            this.state.timeValues = timeValues;\n        } else {\n            this.state.timeValues = [];\n            this.state.timeValues[props.focusedDateIndex] = timeValues[props.focusedDateIndex];\n        }\n\n        this.shouldAdjustFocusDate = !props.range;\n        this.adjustFocus(this.values, props.focusedDateIndex);\n        this.handle12HourSystem();\n        this.state.timeValues = this.state.timeValues.map((timeValue) => timeValue.map(String));\n    }\n\n    onWillRender() {\n        const { dayCellClass, focusedDateIndex, isDateValid, range, showWeekNumbers } = this.props;\n        const { focusDate, hoveredDate } = this.state;\n        const precision = this.activePrecisionLevel;\n        const getterParams = {\n            additionalMonth: this.additionalMonth,\n            maxDate: this.maxDate,\n            minDate: this.minDate,\n            showWeekNumbers: showWeekNumbers ?? !range,\n            isDateValid,\n            dayCellClass,\n        };\n\n        this.title = precision.getTitle(focusDate, getterParams);\n        this.items = precision.getItems(focusDate, getterParams);\n\n        this.selectedRange = [...this.values];\n        if (range && focusedDateIndex > 0 && (!this.values[1] || hoveredDate > this.values[0])) {\n            this.selectedRange[1] = hoveredDate;\n        }\n    }\n\n    //-------------------------------------------------------------------------\n    // Methods\n    //-------------------------------------------------------------------------\n\n    /**\n     * @param {NullableDateTime[]} values\n     * @param {number} focusedDateIndex\n     */\n    adjustFocus(values, focusedDateIndex) {\n        if (!this.shouldAdjustFocusDate && this.state.focusDate) {\n            return;\n        }\n\n        let dateToFocus =\n            values[focusedDateIndex] || values[focusedDateIndex === 1 ? 0 : 1] || today();\n\n        if (\n            this.additionalMonth &&\n            focusedDateIndex === 1 &&\n            values[0] &&\n            values[1] &&\n            values[0].month !== values[1].month\n        ) {\n            dateToFocus = dateToFocus.minus({ month: 1 });\n        }\n\n        this.shouldAdjustFocusDate = false;\n        this.state.focusDate = this.clamp(dateToFocus.startOf(\"month\"));\n    }\n\n    /**\n     * @param {DateTime} value\n     */\n    clamp(value) {\n        return clampDate(value, this.minDate, this.maxDate);\n    }\n\n    /**\n     * @param {PrecisionLevel} minPrecision\n     * @param {PrecisionLevel} maxPrecision\n     */\n    filterPrecisionLevels(minPrecision, maxPrecision) {\n        const levels = [...PRECISION_LEVELS.keys()];\n        return levels.slice(levels.indexOf(minPrecision), levels.indexOf(maxPrecision) + 1);\n    }\n\n    /**\n     * Returns various flags indicating what ranges the current date item belongs\n     * to. Note that these ranges are computed differently according to the current\n     * value mode (range or single date). This is done to simplify CSS selectors.\n     * - Selected Range:\n     *      > range: current values with hovered date applied\n     *      > single date: just the hovered date\n     * - Highlighted Range:\n     *      > range: union of selection range and current values\n     *      > single date: just the current value\n     * - Current Range (range only):\n     *      > range: current start date or current end date.\n     * @param {DateItem} item\n     */\n    getActiveRangeInfo({ isOutOfRange, range }) {\n        const result = {\n            isSelected: !isOutOfRange && isInRange(this.selectedRange, range),\n            isSelectStart: false,\n            isSelectEnd: false,\n            isHighlighted: isInRange(this.state.hoveredDate, range),\n            isCurrent: false,\n        };\n\n        if (this.props.range) {\n            if (result.isSelected) {\n                const [selectStart, selectEnd] = this.selectedRange;\n                result.isSelectStart = !selectStart || isInRange(selectStart, range);\n                result.isSelectEnd = !selectEnd || isInRange(selectEnd, range);\n            }\n            result.isCurrent =\n                !isOutOfRange &&\n                (isInRange(this.values[0], range) || isInRange(this.values[1], range));\n        } else {\n            result.isSelectStart = result.isSelectEnd = result.isSelected;\n        }\n\n        return result;\n    }\n\n    getTimeValues(valueIndex) {\n        let [hour, minute, second] = this.state.timeValues[valueIndex].map(Number);\n        if (\n            this.is12HourFormat &&\n            this.meridiems &&\n            this.state.timeValues[valueIndex][3] === \"PM\"\n        ) {\n            hour += 12;\n        }\n        return [hour, minute, second];\n    }\n\n    handle12HourSystem() {\n        if (isMeridiemFormat()) {\n            this.meridiems = MERIDIEMS.map((m) => [m, m]);\n            for (const timeValues of this.state.timeValues) {\n                if (timeValues) {\n                    timeValues.push(MERIDIEMS[Math.floor(timeValues[0] / 12) || 0]);\n                }\n            }\n        }\n        this.is12HourFormat = !is24HourFormat();\n        if (this.is12HourFormat) {\n            this.availableHours = [[0, HOURS[12][1]], ...HOURS.slice(1, 12)];\n            for (const timeValues of this.state.timeValues) {\n                if (timeValues) {\n                    timeValues[0] %= 12;\n                }\n            }\n        }\n    }\n\n    /**\n     * @param {DateItem} item\n     */\n    isSelectedDate({ range }) {\n        return this.values.some((value) => isInRange(value, range));\n    }\n\n    /**\n     * Goes to the next panel (e.g. next month if precision is \"days\").\n     * If an event is given it will be prevented.\n     * @param {PointerEvent} ev\n     */\n    next(ev) {\n        ev.preventDefault();\n        const { step } = this.activePrecisionLevel;\n        this.state.focusDate = this.clamp(this.state.focusDate.plus(step));\n    }\n\n    /**\n     * Goes to the previous panel (e.g. previous month if precision is \"days\").\n     * If an event is given it will be prevented.\n     * @param {PointerEvent} ev\n     */\n    previous(ev) {\n        ev.preventDefault();\n        const { step } = this.activePrecisionLevel;\n        this.state.focusDate = this.clamp(this.state.focusDate.minus(step));\n    }\n\n    /**\n     * Happens when an hour or a minute (or AM/PM if can apply) is selected.\n     * @param {number} valueIndex\n     */\n    selectTime(valueIndex) {\n        const value = this.values[valueIndex] || today();\n        this.validateAndSelect(value, valueIndex, \"time\");\n    }\n\n    /**\n     * @param {DateTime} value\n     * @param {number} valueIndex\n     * @param {\"date\" | \"time\"} unit\n     */\n    validateAndSelect(value, valueIndex, unit) {\n        if (!this.props.onSelect) {\n            // No onSelect handler\n            return false;\n        }\n\n        const result = [...this.values];\n        result[valueIndex] = value;\n\n        if (this.props.type === \"datetime\") {\n            // Adjusts result according to the current time values\n            const [hour, minute, second] = this.getTimeValues(valueIndex);\n            result[valueIndex] = result[valueIndex].set({ hour, minute, second });\n        }\n        if (!isInRange(result[valueIndex], [this.minDate, this.maxDate])) {\n            // Date is outside range defined by min and max dates\n            return false;\n        }\n        this.props.onSelect(result.length === 2 ? result : result[0], unit);\n        return true;\n    }\n\n    /**\n     * Returns whether the zoom has occurred\n     * @param {DateTime} date\n     */\n    zoomIn(date) {\n        const index = this.allowedPrecisionLevels.indexOf(this.state.precision) - 1;\n        if (index in this.allowedPrecisionLevels) {\n            this.state.focusDate = this.clamp(date);\n            this.state.precision = this.allowedPrecisionLevels[index];\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Returns whether the zoom has occurred\n     */\n    zoomOut() {\n        const index = this.allowedPrecisionLevels.indexOf(this.state.precision) + 1;\n        if (index in this.allowedPrecisionLevels) {\n            this.state.precision = this.allowedPrecisionLevels[index];\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Happens when a date item is selected:\n     * - first tries to zoom in on the item\n     * - if could not zoom in: date is considered as final value and triggers a hard select\n     * @param {DateItem} dateItem\n     */\n    zoomOrSelect(dateItem) {\n        if (!dateItem.isValid) {\n            // Invalid item\n            return;\n        }\n        if (this.zoomIn(dateItem.range[0])) {\n            // Zoom was successful\n            return;\n        }\n        const [value] = dateItem.range;\n        const valueIndex = this.props.focusedDateIndex;\n        const isValid = this.validateAndSelect(value, valueIndex, \"date\");\n        this.shouldAdjustFocusDate = isValid && !this.props.range;\n    }\n}\n", "import { Component } from \"@odoo/owl\";\nimport { useHotkey } from \"../hotkeys/hotkey_hook\";\nimport { DateTimePicker } from \"./datetime_picker\";\n\n/**\n * @typedef {import(\"./datetime_picker\").DateTimePickerProps} DateTimePickerProps\n *\n * @typedef DateTimePickerPopoverProps\n * @property {() => void} close\n * @property {DateTimePickerProps} pickerProps\n */\n\n/** @extends {Component<DateTimePickerPopoverProps>} */\nexport class DateTimePickerPopover extends Component {\n    static components = { DateTimePicker };\n\n    static props = {\n        close: Function, // Given by the Popover service\n        pickerProps: { type: Object, shape: DateTimePicker.props },\n    };\n\n    static template = \"web.DateTimePickerPopover\";\n\n    get isDateTimeRange() {\n        return (\n            this.props.pickerProps.type === \"datetime\" ||\n            Array.isArray(this.props.pickerProps.value)\n        );\n    }\n\n    //-------------------------------------------------------------------------\n    // Lifecycle\n    //-------------------------------------------------------------------------\n\n    setup() {\n        useHotkey(\"enter\", () => this.props.close());\n    }\n}\n", "import { markRaw, reactive } from \"@odoo/owl\";\nimport { areDatesEqual, formatDate, formatDateTime, parseDate, parseDateTime } from \"../l10n/dates\";\nimport { makePopover } from \"../popover/popover_hook\";\nimport { registry } from \"../registry\";\nimport { ensureArray, zip, zipWith } from \"../utils/arrays\";\nimport { deepCopy, shallowEqual } from \"../utils/objects\";\nimport { DateTimePicker } from \"./datetime_picker\";\nimport { DateTimePickerPopover } from \"./datetime_picker_popover\";\n\n/**\n * @typedef {luxon[\"DateTime\"][\"prototype\"]} DateTime\n *\n * @typedef DateTimePickerHookParams\n * @property {string} [format]\n * @property {(value: DateTimePickerProps[\"value\"]) => any} [onChange] callback\n *  invoked every time the hook updates the reactive value, either through the inputs\n *  or the picker.\n * @property {(value: DateTimePickerProps[\"value\"]) => any} [onApply] callback\n *  invoked once the value is committed: this is either when all inputs received\n *  a \"change\" event or when the datetime picker popover has been closed.\n * @property {DateTimePickerProps} pickerProps\n * @property {string | ReturnType<typeof import(\"@odoo/owl\").useRef>} [target]\n * @property {(component, options) => import(\"../popover/popover_hook\").PopoverHookReturnType} [createPopover]\n * @property {() => boolean} [ensureVisibility=() => env.isSmall]\n * @property {boolean} [showSeconds]\n *\n * @typedef {import(\"./datetime_picker\").DateTimePickerProps} DateTimePickerProps\n */\n\n/**\n * @template {HTMLElement} T\n * @typedef {{ el: T | null }} OwlRef\n */\n\n/** @type {typeof shallowEqual} */\nconst arePropsEqual = (obj1, obj2) =>\n    shallowEqual(obj1, obj2, (a, b) => areDatesEqual(a, b) || shallowEqual(a, b));\n\nconst FOCUS_CLASSNAME = \"text-primary\";\n\nconst formatters = {\n    date: formatDate,\n    datetime: formatDateTime,\n};\n\nconst listenedElements = new WeakSet();\n\nconst parsers = {\n    date: parseDate,\n    datetime: parseDateTime,\n};\n\nexport const datetimePickerService = {\n    dependencies: [\"popover\"],\n    start(env, { popover: popoverService }) {\n        return {\n            /**\n             * @param {DateTimePickerHookParams} hookParams\n             */\n            create: (hookParams, getInputs = () => [hookParams.target, null]) => {\n                const createPopover =\n                    hookParams.createPopover ??\n                    ((...args) => makePopover(popoverService.add, ...args));\n                const ensureVisibility = hookParams.ensureVisibility ?? (() => env.isSmall);\n                const popover = createPopover(DateTimePickerPopover, {\n                    onClose: () => {\n                        if (!allowOnClose) {\n                            return;\n                        }\n                        updateValueFromInputs();\n                        apply();\n                        setFocusClass(null);\n                        if (restoreTargetMargin) {\n                            restoreTargetMargin();\n                            restoreTargetMargin = null;\n                        }\n                    },\n                });\n                // Hook methods\n\n                /**\n                 * Wrapper method on the \"onApply\" callback to only call it when the\n                 * value has changed, and set other internal variables accordingly.\n                 */\n                const apply = () => {\n                    const valueCopy = deepCopy(pickerProps.value);\n                    if (areDatesEqual(lastAppliedValue, valueCopy)) {\n                        return;\n                    }\n\n                    inputsChanged = ensureArray(pickerProps.value).map(() => false);\n\n                    hookParams.onApply?.(pickerProps.value);\n                    lastAppliedValue = valueCopy;\n                };\n\n                const computeBasePickerProps = () => {\n                    const nextInitialProps = markValuesRaw(hookParams.pickerProps);\n                    const propsCopy = deepCopy(nextInitialProps);\n\n                    if (lastInitialProps && arePropsEqual(lastInitialProps, propsCopy)) {\n                        return;\n                    }\n\n                    lastInitialProps = propsCopy;\n                    lastAppliedValue = propsCopy.value;\n                    inputsChanged = ensureArray(lastInitialProps.value).map(() => false);\n\n                    for (const [key, value] of Object.entries(nextInitialProps)) {\n                        if (pickerProps[key] !== value && !areDatesEqual(pickerProps[key], value)) {\n                            pickerProps[key] = value;\n                        }\n                    }\n                };\n\n                /**\n                 * Ensures the current focused input (indicated by `pickerProps.focusedDateIndex`)\n                 * is actually focused.\n                 */\n                const focusActiveInput = () => {\n                    const inputEl = getInput(pickerProps.focusedDateIndex);\n                    if (!inputEl) {\n                        shouldFocus = true;\n                        return;\n                    }\n\n                    const { activeElement } = inputEl.ownerDocument;\n                    if (activeElement !== inputEl) {\n                        inputEl.focus();\n                    }\n\n                    setInputFocus(inputEl);\n                };\n\n                /**\n                 * @param {number} valueIndex\n                 * @returns {HTMLInputElement | null}\n                 */\n                const getInput = (valueIndex) => {\n                    const el = getInputs()[valueIndex];\n                    if (el && document.body.contains(el)) {\n                        return el;\n                    }\n                    return null;\n                };\n\n                /**\n                 * Returns the appropriate root element to attach the popover:\n                 * - if the value is a range: the closest common parent of the two inputs\n                 * - if not: the first input\n                 */\n                const getPopoverTarget = () => {\n                    if (hookParams.target) {\n                        return hookParams.target;\n                    }\n                    if (pickerProps.range) {\n                        let parentElement = getInput(0).parentElement;\n                        const inputEls = getInputs();\n                        while (\n                            parentElement &&\n                            !inputEls.every((inputEl) => parentElement.contains(inputEl))\n                        ) {\n                            parentElement = parentElement.parentElement;\n                        }\n                        return parentElement || getInput(0);\n                    } else {\n                        return getInput(0);\n                    }\n                };\n\n                /**\n                 * @template {object} T\n                 * @param {T} obj\n                 */\n                const markValuesRaw = (obj) => {\n                    /** @type {T} */\n                    const copy = {};\n                    for (const [key, value] of Object.entries(obj)) {\n                        if (value && typeof value === \"object\") {\n                            copy[key] = markRaw(value);\n                        } else {\n                            copy[key] = value;\n                        }\n                    }\n                    return copy;\n                };\n\n                /**\n                 * Inputs \"change\" event handler. This will trigger an \"onApply\" callback if\n                 * one of the following is true:\n                 * - there is only one input;\n                 * - the popover is closed;\n                 * - the other input has also changed.\n                 *\n                 * @param {Event} ev\n                 */\n                const onInputChange = (ev) => {\n                    updateValueFromInputs();\n                    inputsChanged[ev.target === getInput(1) ? 1 : 0] = true;\n                    if (!popover.isOpen || inputsChanged.every(Boolean)) {\n                        saveAndClose();\n                    }\n                };\n\n                /**\n                 * @param {PointerEvent} ev\n                 */\n                const onInputClick = ({ target }) => {\n                    openPicker(target === getInput(1) ? 1 : 0);\n                };\n\n                /**\n                 * @param {FocusEvent} ev\n                 */\n                const onInputFocus = ({ target }) => {\n                    pickerProps.focusedDateIndex = target === getInput(1) ? 1 : 0;\n                    setInputFocus(target);\n                };\n\n                /**\n                 * @param {KeyboardEvent} ev\n                 */\n                const onInputKeydown = (ev) => {\n                    if (ev.key == \"Enter\" && ev.ctrlKey) {\n                        ev.preventDefault();\n                        updateValueFromInputs();\n                        return openPicker(ev.target === getInput(1) ? 1 : 0);\n                    }\n                    switch (ev.key) {\n                        case \"Enter\":\n                        case \"Escape\": {\n                            return saveAndClose();\n                        }\n                        case \"Tab\": {\n                            if (\n                                !getInput(0) ||\n                                !getInput(1) ||\n                                ev.target !== getInput(ev.shiftKey ? 1 : 0)\n                            ) {\n                                return saveAndClose();\n                            }\n                        }\n                    }\n                };\n\n                /**\n                 * @param {number} inputIndex Input from which to open the picker\n                 */\n                const openPicker = (inputIndex) => {\n                    pickerProps.focusedDateIndex = inputIndex;\n\n                    if (!popover.isOpen) {\n                        const popoverTarget = getPopoverTarget();\n                        if (ensureVisibility()) {\n                            const { marginBottom } = popoverTarget.style;\n                            // Adds enough space for the popover to be displayed below the target\n                            // even on small screens.\n                            popoverTarget.style.marginBottom = `100vh`;\n                            popoverTarget.scrollIntoView(true);\n                            restoreTargetMargin = async () => {\n                                popoverTarget.style.marginBottom = marginBottom;\n                            };\n                        }\n                        popover.open(popoverTarget, { pickerProps });\n                    }\n\n                    focusActiveInput();\n                };\n\n                /**\n                 * @template {\"format\" | \"parse\"} T\n                 * @param {T} operation\n                 * @param {T extends \"format\" ? DateTime : string} value\n                 * @returns {[T extends \"format\" ? string : DateTime, null] | [null, Error]}\n                 */\n                const safeConvert = (operation, value) => {\n                    const { type } = pickerProps;\n                    const convertFn = (operation === \"format\" ? formatters : parsers)[type];\n                    const options = { tz: pickerProps.tz, format: hookParams.format };\n                    if (operation === \"format\") {\n                        options.showSeconds = hookParams.showSeconds ?? true;\n                        options.condensed = hookParams.condensed || false;\n                    }\n                    try {\n                        return [convertFn(value, options), null];\n                    } catch (error) {\n                        if (error?.name === \"ConversionError\") {\n                            return [null, error];\n                        } else {\n                            throw error;\n                        }\n                    }\n                };\n\n                /**\n                 * Wrapper method to ensure the \"onApply\" callback is called, either:\n                 * - by closing the popover (if any);\n                 * - or by directly calling \"apply\", without updating the values.\n                 */\n                const saveAndClose = () => {\n                    if (popover.isOpen) {\n                        // apply will be done in the \"onClose\" callback\n                        popover.close();\n                    } else {\n                        apply();\n                    }\n                };\n\n                /**\n                 * Updates class names on given inputs according to the currently selected input.\n                 *\n                 * @param {HTMLInputElement | null} input\n                 */\n                const setFocusClass = (input) => {\n                    for (const el of getInputs()) {\n                        if (el) {\n                            el.classList.toggle(FOCUS_CLASSNAME, popover.isOpen && el === input);\n                        }\n                    }\n                };\n\n                /**\n                 * Applies class names to all inputs according to whether they are focused or not.\n                 *\n                 * @param {HTMLInputElement} inputEl\n                 */\n                const setInputFocus = (inputEl) => {\n                    inputEl.selectionStart = 0;\n                    inputEl.selectionEnd = inputEl.value.length;\n\n                    setFocusClass(inputEl);\n\n                    shouldFocus = false;\n                };\n\n                /**\n                 * Synchronizes the given input with the given value.\n                 *\n                 * @param {HTMLInputElement} el\n                 * @param {DateTime} value\n                 */\n                const updateInput = (el, value) => {\n                    if (!el) {\n                        return;\n                    }\n                    const [formattedValue] = safeConvert(\"format\", value);\n                    el.value = formattedValue || \"\";\n                };\n\n                /**\n                 * @param {DateTimePickerProps[\"value\"]} value\n                 * @param {\"date\" | \"time\"} unit\n                 * @param {\"input\" | \"picker\"} source\n                 */\n                const updateValue = (value, unit, source) => {\n                    const previousValue = pickerProps.value;\n                    pickerProps.value = value;\n\n                    if (areDatesEqual(previousValue, pickerProps.value)) {\n                        return;\n                    }\n\n                    if (unit !== \"time\") {\n                        if (pickerProps.range && source === \"picker\") {\n                            if (\n                                pickerProps.focusedDateIndex === 0 ||\n                                (value[0] && value[1] && value[1] < value[0])\n                            ) {\n                                // If selecting either:\n                                // - the first value\n                                // - OR a second value before the first:\n                                // Then:\n                                // - Set the DATE (year + month + day) of all values\n                                // to the one that has been selected.\n                                const { year, month, day } = value[pickerProps.focusedDateIndex];\n                                for (let i = 0; i < value.length; i++) {\n                                    value[i] = value[i] && value[i].set({ year, month, day });\n                                }\n                                pickerProps.focusedDateIndex = 1;\n                            } else {\n                                // If selecting the second value after the first:\n                                // - simply toggle the focus index\n                                pickerProps.focusedDateIndex =\n                                    pickerProps.focusedDateIndex === 1 ? 0 : 1;\n                            }\n                        }\n                    }\n\n                    hookParams.onChange?.(value);\n                };\n\n                const updateValueFromInputs = () => {\n                    const values = zipWith(\n                        getInputs(),\n                        ensureArray(pickerProps.value),\n                        (el, currentValue) => {\n                            if (!el) {\n                                return currentValue;\n                            }\n                            const [parsedValue, error] = safeConvert(\"parse\", el.value);\n                            if (error) {\n                                updateInput(el, currentValue);\n                                return currentValue;\n                            } else {\n                                return parsedValue;\n                            }\n                        }\n                    );\n                    updateValue(values.length === 2 ? values : values[0], \"date\", \"input\");\n                };\n\n                // Hook variables\n\n                /** @type {DateTimePickerProps} */\n                const rawPickerProps = {\n                    ...DateTimePicker.defaultProps,\n                    onSelect: (value, unit) => {\n                        value &&= markRaw(value);\n                        updateValue(value, unit, \"picker\");\n                        if (!pickerProps.range && pickerProps.type === \"date\") {\n                            saveAndClose();\n                        }\n                    },\n                    ...markValuesRaw(hookParams.pickerProps),\n                };\n                const pickerProps = reactive(rawPickerProps, () => {\n                    // Resets the popover position when switching from single date to a range\n                    // or vice-versa\n                    const currentIsRange = pickerProps.range;\n                    if (popover.isOpen && lastIsRange !== currentIsRange) {\n                        allowOnClose = false;\n                        popover.open(getPopoverTarget(), { pickerProps });\n                        allowOnClose = true;\n                    }\n                    lastIsRange = currentIsRange;\n\n                    // Update inputs\n                    for (const [el, value] of zip(\n                        getInputs(),\n                        ensureArray(pickerProps.value),\n                        true\n                    )) {\n                        if (el) {\n                            updateInput(el, value);\n                        }\n                    }\n\n                    shouldFocus = true;\n                });\n\n                /** Decides whether the popover 'onClose' callback can be called */\n                let allowOnClose = true;\n                /** @type {boolean[]} */\n                let inputsChanged = [];\n                /** @type {DateTimePickerProps | null} */\n                let lastInitialProps = null;\n                /** @type {DateTimePickerProps[\"value\"] | null}*/\n                let lastAppliedValue = null;\n                let lastIsRange = pickerProps.range;\n                /** @type {(() => void) | null} */\n                let restoreTargetMargin = null;\n                let shouldFocus = false;\n\n                return {\n                    state: pickerProps,\n                    open: openPicker,\n                    computeBasePickerProps,\n                    focusIfNeeded() {\n                        if (popover.isOpen && shouldFocus) {\n                            focusActiveInput();\n                        }\n                    },\n                    enable() {\n                        let editableInputs = 0;\n                        for (const [el, value] of zip(\n                            getInputs(),\n                            ensureArray(pickerProps.value),\n                            true\n                        )) {\n                            updateInput(el, value);\n                            if (el && !el.disabled && !el.readOnly && !listenedElements.has(el)) {\n                                listenedElements.add(el);\n                                el.addEventListener(\"change\", onInputChange);\n                                el.addEventListener(\"click\", onInputClick);\n                                el.addEventListener(\"focus\", onInputFocus);\n                                el.addEventListener(\"keydown\", onInputKeydown);\n                                editableInputs++;\n                            }\n                        }\n                        const calendarIconGroupEl = getInput(0)?.parentElement.querySelector(\n                            \".o_input_group_date_icon\"\n                        );\n                        if (calendarIconGroupEl) {\n                            calendarIconGroupEl.classList.add(\"cursor-pointer\");\n                            calendarIconGroupEl.addEventListener(\"click\", () => openPicker(0));\n                        }\n                        if (!editableInputs && popover.isOpen) {\n                            saveAndClose();\n                        }\n                        return () => {};\n                    },\n                    get isOpen() {\n                        return popover.isOpen;\n                    },\n                };\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"datetime_picker\", datetimePickerService);\n", "import { user } from \"@web/core/user\";\nimport { registry } from \"../registry\";\n\nimport { useEffect, useEnv, useSubEnv } from \"@odoo/owl\";\nconst debugRegistry = registry.category(\"debug\");\n\nconst getAccessRights = async () => {\n    const rightsToCheck = {\n        \"ir.ui.view\": \"write\",\n        \"ir.rule\": \"read\",\n        \"ir.model.access\": \"read\",\n    };\n    const proms = Object.entries(rightsToCheck).map(([model, operation]) => {\n        return user.checkAccessRight(model, operation);\n    });\n    const [canEditView, canSeeRecordRules, canSeeModelAccess] = await Promise.all(proms);\n    const accessRights = { canEditView, canSeeRecordRules, canSeeModelAccess };\n    return accessRights;\n};\n\nclass DebugContext {\n    constructor(defaultCategories) {\n        this.categories = new Map(defaultCategories.map((cat) => [cat, [{}]]));\n    }\n\n    activateCategory(category, context) {\n        const contexts = this.categories.get(category) || new Set();\n        contexts.add(context);\n        this.categories.set(category, contexts);\n\n        return () => {\n            contexts.delete(context);\n            if (contexts.size === 0) {\n                this.categories.delete(category);\n            }\n        };\n    }\n\n    async getItems(env) {\n        const accessRights = await getAccessRights();\n        return [...this.categories.entries()]\n            .flatMap(([category, contexts]) => {\n                return debugRegistry\n                    .category(category)\n                    .getAll()\n                    .map((factory) => factory(Object.assign({ env, accessRights }, ...contexts)));\n            })\n            .filter(Boolean)\n            .sort((x, y) => {\n                const xSeq = x.sequence || 1000;\n                const ySeq = y.sequence || 1000;\n                return xSeq - ySeq;\n            });\n    }\n}\n\nconst debugContextSymbol = Symbol(\"debugContext\");\nexport function createDebugContext({ categories = [] } = {}) {\n    return { [debugContextSymbol]: new DebugContext(categories) };\n}\n\nexport function useOwnDebugContext({ categories = [] } = {}) {\n    useSubEnv(createDebugContext({ categories }));\n}\n\nexport function useEnvDebugContext() {\n    const debugContext = useEnv()[debugContextSymbol];\n    if (!debugContext) {\n        throw new Error(\"There is no debug context available in the current environment.\");\n    }\n    return debugContext;\n}\n\nexport function useDebugCategory(category, context = {}) {\n    const env = useEnv();\n    if (env.debug) {\n        const debugContext = useEnvDebugContext();\n        useEffect(\n            () => debugContext.activateCategory(category, context),\n            () => []\n        );\n    }\n}\n", "import { useEnvDebugContext } from \"./debug_context\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { groupBy, sortBy } from \"@web/core/utils/arrays\";\n\nimport { Component } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\n\nconst debugSectionRegistry = registry.category(\"debug_section\");\n\ndebugSectionRegistry\n    .add(\"record\", { label: _t(\"Record\"), sequence: 10 })\n    .add(\"records\", { label: _t(\"Records\"), sequence: 10 })\n    .add(\"ui\", { label: _t(\"User Interface\"), sequence: 20 })\n    .add(\"security\", { label: _t(\"Security\"), sequence: 30 })\n    .add(\"testing\", { label: _t(\"Testing\"), sequence: 40 })\n    .add(\"tools\", { label: _t(\"Tools\"), sequence: 50 });\n\nexport class DebugMenuBasic extends Component {\n    static template = \"web.DebugMenu\";\n    static components = {\n        Dropdown,\n        DropdownItem,\n    };\n    static props = {};\n\n    setup() {\n        this.debugContext = useEnvDebugContext();\n    }\n\n    async loadGroupedItems() {\n        const items = await this.debugContext.getItems(this.env);\n        const sections = groupBy(items, (item) => item.section || \"\");\n        this.sectionEntries = sortBy(\n            Object.entries(sections),\n            ([section]) => debugSectionRegistry.get(section, { sequence: 50 }).sequence\n        );\n    }\n\n    getSectionLabel(section) {\n        return debugSectionRegistry.get(section, { label: section }).label;\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { router } from \"@web/core/browser/router\";\nimport { registry } from \"@web/core/registry\";\nimport { user } from \"@web/core/user\";\n\nfunction activateTestsAssetsDebugging({ env }) {\n    if (String(router.current.debug).includes(\"tests\")) {\n        return;\n    }\n\n    return {\n        type: \"item\",\n        description: _t(\"Activate Test Mode\"),\n        callback: () => {\n            router.pushState({ debug: \"assets,tests\" }, { reload: true });\n        },\n        sequence: 580,\n        section: \"tools\",\n    };\n}\n\nexport function regenerateAssets({ env }) {\n    return {\n        type: \"item\",\n        description: _t(\"Regenerate Assets\"),\n        callback: async () => {\n            await env.services.orm.call(\"ir.attachment\", \"regenerate_assets_bundles\");\n            browser.location.reload();\n        },\n        sequence: 550,\n        section: \"tools\",\n    };\n}\n\nexport function becomeSuperuser({ env }) {\n    const becomeSuperuserURL = browser.location.origin + \"/web/become\";\n    if (!user.isAdmin) {\n        return false;\n    }\n    return {\n        type: \"item\",\n        description: _t(\"Become Superuser\"),\n        href: becomeSuperuserURL,\n        callback: () => {\n            browser.open(becomeSuperuserURL, \"_self\");\n        },\n        sequence: 560,\n        section: \"tools\",\n    };\n}\n\nfunction leaveDebugMode() {\n    return {\n        type: \"item\",\n        description: _t(\"Leave Debug Mode\"),\n        callback: () => {\n            router.pushState({ debug: 0 }, { reload: true });\n        },\n        sequence: 650,\n    };\n}\n\nregistry\n    .category(\"debug\")\n    .category(\"default\")\n    .add(\"regenerateAssets\", regenerateAssets)\n    .add(\"becomeSuperuser\", becomeSuperuser)\n    .add(\"activateTestsAssetsDebugging\", activateTestsAssetsDebugging)\n    .add(\"leaveDebugMode\", leaveDebugMode);\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"../registry\";\nimport { browser } from \"../browser/browser\";\nimport { router } from \"../browser/router\";\n\nconst commandProviderRegistry = registry.category(\"command_provider\");\n\ncommandProviderRegistry.add(\"debug\", {\n    provide: (env, options) => {\n        const result = [];\n        if (env.debug) {\n            if (!env.debug.includes(\"assets\")) {\n                result.push({\n                    action() {\n                        router.pushState({ debug: \"assets\" }, { reload: true });\n                    },\n                    category: \"debug\",\n                    name: _t(\"Activate debug mode (with assets)\"),\n                });\n            }\n            result.push({\n                action() {\n                    router.pushState({ debug: 0 }, { reload: true });\n                },\n                category: \"debug\",\n                name: _t(\"Deactivate debug mode\"),\n            });\n            result.push({\n                action() {\n                    browser.open(\"/web/tests?debug=assets\");\n                },\n                category: \"debug\",\n                name: _t(\"Run Unit Tests\"),\n            });\n        } else {\n            const debugKey = \"debug\";\n            if (options.searchValue.toLowerCase() === debugKey) {\n                result.push({\n                    action() {\n                        router.pushState({ debug: \"1\" }, { reload: true });\n                    },\n                    category: \"debug\",\n                    name: `${_t(\"Activate debug mode\")} (${debugKey})`,\n                });\n                result.push({\n                    action() {\n                        router.pushState({ debug: \"assets\" }, { reload: true });\n                    },\n                    category: \"debug\",\n                    name: `${_t(\"Activate debug mode (with assets)\")} (${debugKey})`,\n                });\n            }\n        }\n        return result;\n    },\n});\n", "export function editModelDebug(env, title, model, id) {\n    return env.services.action.doAction({\n        res_model: model,\n        res_id: id,\n        name: title,\n        type: \"ir.actions.act_window\",\n        views: [[false, \"form\"]],\n        view_mode: \"form\",\n        target: \"current\",\n    });\n}\n", "import { useHotkey } from \"@web/core/hotkeys/hotkey_hook\";\nimport { useActiveElement } from \"../ui/ui_service\";\nimport { useForwardRefToParent } from \"@web/core/utils/hooks\";\nimport { Component, onWillDestroy, useChildSubEnv, useExternalListener, useState } from \"@odoo/owl\";\nimport { throttleForAnimation } from \"@web/core/utils/timing\";\nimport { makeDraggableHook } from \"../utils/draggable_hook_builder_owl\";\n\nconst useDialogDraggable = makeDraggableHook({\n    name: \"useDialogDraggable\",\n    onWillStartDrag({ ctx, addCleanup, addStyle, getRect }) {\n        const { height, width } = getRect(ctx.current.element);\n        ctx.current.container = document.createElement(\"div\");\n        addStyle(ctx.current.container, {\n            position: \"fixed\",\n            top: \"0\",\n            bottom: `${70 - height}px`,\n            left: `${70 - width}px`,\n            right: `${70 - width}px`,\n        });\n        ctx.current.element.after(ctx.current.container);\n        addCleanup(() => ctx.current.container.remove());\n    },\n    onDrop({ ctx, getRect }) {\n        const { top, left } = getRect(ctx.current.element);\n        return {\n            left: left - ctx.current.elementRect.left,\n            top: top - ctx.current.elementRect.top,\n        };\n    },\n});\n\nexport class Dialog extends Component {\n    static template = \"web.Dialog\";\n    static props = {\n        contentClass: { type: String, optional: true },\n        bodyClass: { type: String, optional: true },\n        fullscreen: { type: Boolean, optional: true },\n        footer: { type: Boolean, optional: true },\n        header: { type: Boolean, optional: true },\n        size: {\n            type: String,\n            optional: true,\n            validate: (s) => [\"sm\", \"md\", \"lg\", \"xl\", \"fs\", \"fullscreen\"].includes(s),\n        },\n        technical: { type: Boolean, optional: true },\n        title: { type: String, optional: true },\n        modalRef: { type: Function, optional: true },\n        slots: {\n            type: Object,\n            shape: {\n                default: Object, // Content is not optional\n                header: { type: Object, optional: true },\n                footer: { type: Object, optional: true },\n            },\n        },\n        withBodyPadding: { type: Boolean, optional: true },\n        onExpand: { type: Function, optional: true },\n    };\n    static defaultProps = {\n        contentClass: \"\",\n        bodyClass: \"\",\n        fullscreen: false,\n        footer: true,\n        header: true,\n        size: \"lg\",\n        technical: true,\n        title: \"Odoo\",\n        withBodyPadding: true,\n    };\n\n    setup() {\n        this.modalRef = useForwardRefToParent(\"modalRef\");\n        useActiveElement(\"modalRef\");\n        this.data = useState(this.env.dialogData);\n        useHotkey(\"escape\", () => this.onEscape());\n        useHotkey(\n            \"control+enter\",\n            () => {\n                const btns = document.querySelectorAll(\n                    \".o_dialog:not(.o_inactive_modal) .modal-footer button\"\n                );\n                const firstVisibleBtn = Array.from(btns).find((btn) => {\n                    const styles = getComputedStyle(btn);\n                    return styles.display !== \"none\";\n                });\n                if (firstVisibleBtn) {\n                    firstVisibleBtn.click();\n                }\n            },\n            { bypassEditableProtection: true }\n        );\n        this.id = `dialog_${this.data.id}`;\n        useChildSubEnv({ inDialog: true, dialogId: this.id });\n        this.isMovable = this.props.header;\n        if (this.isMovable) {\n            this.position = useState({ left: 0, top: 0 });\n            useDialogDraggable({\n                enable: () => !this.env.isSmall,\n                ref: this.modalRef,\n                elements: \".modal-content\",\n                handle: \".modal-header\",\n                ignore: \"button, input\",\n                edgeScrolling: { enabled: false },\n                onDrop: ({ top, left }) => {\n                    this.position.left += left;\n                    this.position.top += top;\n                },\n            });\n            const throttledResize = throttleForAnimation(this.onResize.bind(this));\n            useExternalListener(window, \"resize\", throttledResize);\n        }\n        onWillDestroy(() => {\n            if (this.env.isSmall) {\n                this.data.scrollToOrigin();\n            }\n        });\n    }\n\n    get isFullscreen() {\n        return this.props.fullscreen || this.env.isSmall;\n    }\n\n    get contentStyle() {\n        if (this.isMovable) {\n            return `top: ${this.position.top}px; left: ${this.position.left}px;`;\n        }\n        return \"\";\n    }\n\n    onResize() {\n        this.position.left = 0;\n        this.position.top = 0;\n    }\n\n    onEscape() {\n        return this.dismiss();\n    }\n\n    async dismiss() {\n        if (this.data.dismiss) {\n            await this.data.dismiss();\n        }\n        return this.data.close();\n    }\n}\n", "import { Component, markRaw, reactive, useChildSubEnv, xml } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\n\nclass DialogWrapper extends Component {\n    static template = xml`<t t-component=\"props.subComponent\" t-props=\"props.subProps\" />`;\n    static props = [\"*\"];\n    setup() {\n        useChildSubEnv({ dialogData: this.props.subEnv });\n    }\n}\n\n/**\n *  @typedef {{\n *      onClose?(): void;\n *  }} DialogServiceInterfaceAddOptions\n */\n/**\n *  @typedef {{\n *      add(\n *          Component: typeof import(\"@odoo/owl\").Component,\n *          props: {},\n *          options?: DialogServiceInterfaceAddOptions\n *      ): () => void;\n *  }} DialogServiceInterface\n */\n\nexport const dialogService = {\n    dependencies: [\"overlay\"],\n    /** @returns {DialogServiceInterface} */\n    start(env, { overlay }) {\n        const stack = [];\n        let nextId = 0;\n\n        const deactivate = () => {\n            for (const subEnv of stack) {\n                subEnv.isActive = false;\n            }\n        };\n\n        const add = (dialogClass, props, options = {}) => {\n            const id = nextId++;\n            const close = () => remove();\n            const subEnv = reactive({\n                id,\n                close,\n                isActive: true,\n            });\n\n            deactivate();\n            stack.push(subEnv);\n            document.body.classList.add(\"modal-open\");\n\n            const scrollOrigin = { top: window.scrollY, left: window.scrollX };\n            subEnv.scrollToOrigin = () => {\n                if (!stack.length) {\n                    window.scrollTo(scrollOrigin);\n                }\n            };\n\n            const remove = overlay.add(\n                DialogWrapper,\n                {\n                    subComponent: dialogClass,\n                    subProps: markRaw({ ...props, close }),\n                    subEnv,\n                },\n                {\n                    onRemove: () => {\n                        stack.pop();\n                        deactivate();\n                        if (stack.length) {\n                            stack.at(-1).isActive = true;\n                        } else {\n                            document.body.classList.remove(\"modal-open\");\n                        }\n                        options.onClose?.();\n                    },\n                    rootId: options.context?.root?.el.getRootNode()?.host?.id,\n                }\n            );\n\n            return remove;\n        };\n\n        function closeAll() {\n            for (const dialog of [...stack].reverse()) {\n                dialog.close();\n            }\n        }\n\n        return { add, closeAll };\n    },\n};\n\nregistry.category(\"services\").add(\"dialog\", dialogService);\n", "import { shallowEqual } from \"@web/core/utils/arrays\";\nimport { evaluate, formatAST, parseExpr } from \"./py_js/py\";\nimport { toPyValue } from \"./py_js/py_utils\";\nimport { escapeRegExp } from \"@web/core/utils/strings\";\n\n/**\n * @typedef {import(\"./py_js/py_parser\").AST} AST\n * @typedef {[string | 0 | 1, string, any]} Condition\n * @typedef {(\"&\" | \"|\" | \"!\" | Condition)[]} DomainListRepr\n * @typedef {DomainListRepr | string | Domain} DomainRepr\n */\n\nexport class InvalidDomainError extends Error {}\n\n/**\n * Javascript representation of an Odoo domain\n */\nexport class Domain {\n    /**\n     * Combine various domains together with a given operator\n     * @param {DomainRepr[]} domains\n     * @param {\"AND\" | \"OR\"} operator\n     * @returns {Domain}\n     */\n    static combine(domains, operator) {\n        if (domains.length === 0) {\n            return new Domain([]);\n        }\n        const domain1 = domains[0] instanceof Domain ? domains[0] : new Domain(domains[0]);\n        if (domains.length === 1) {\n            return domain1;\n        }\n        const domain2 = Domain.combine(domains.slice(1), operator);\n        const result = new Domain([]);\n        const astValues1 = domain1.ast.value;\n        const astValues2 = domain2.ast.value;\n        const op = operator === \"AND\" ? \"&\" : \"|\";\n        const combinedAST = { type: 4 /* List */, value: astValues1.concat(astValues2) };\n        result.ast = normalizeDomainAST(combinedAST, op);\n        return result;\n    }\n\n    /**\n     * Combine various domains together with `AND` operator\n     * @param {DomainRepr} domains\n     * @returns {Domain}\n     */\n    static and(domains) {\n        return Domain.combine(domains, \"AND\");\n    }\n\n    /**\n     * Combine various domains together with `OR` operator\n     * @param {DomainRepr} domains\n     * @returns {Domain}\n     */\n    static or(domains) {\n        return Domain.combine(domains, \"OR\");\n    }\n\n    /**\n     * Return the negation of the domain\n     * @returns {Domain}\n     */\n    static not(domain) {\n        const result = new Domain(domain);\n        result.ast.value.unshift({ type: 1, value: \"!\" });\n        return result;\n    }\n\n    /**\n     * Return a new domain with `neutralized` leaves (for the leaves that are applied on the field that are part of\n     * keysToRemove).\n     * @param {DomainRepr} domain\n     * @param {string[]} keysToRemove\n     * @return {Domain}\n     */\n    static removeDomainLeaves(domain, keysToRemove) {\n        function processLeaf(elements, idx, operatorCtx, newDomain) {\n            const leaf = elements[idx];\n            if (leaf.type === 10) {\n                if (keysToRemove.includes(leaf.value[0].value)) {\n                    if (operatorCtx === \"&\") {\n                        newDomain.ast.value.push(...Domain.TRUE.ast.value);\n                    } else if (operatorCtx === \"|\") {\n                        newDomain.ast.value.push(...Domain.FALSE.ast.value);\n                    }\n                } else {\n                    newDomain.ast.value.push(leaf);\n                }\n                return 1;\n            } else if (leaf.type === 1) {\n                // Special case to avoid OR ('|') that can never resolve to true\n                if (\n                    leaf.value === \"|\" &&\n                    elements[idx + 1].type === 10 &&\n                    elements[idx + 2].type === 10 &&\n                    keysToRemove.includes(elements[idx + 1].value[0].value) &&\n                    keysToRemove.includes(elements[idx + 2].value[0].value)\n                ) {\n                    newDomain.ast.value.push(...Domain.TRUE.ast.value);\n                    return 3;\n                }\n                newDomain.ast.value.push(leaf);\n                if (leaf.value === \"!\") {\n                    return 1 + processLeaf(elements, idx + 1, \"&\", newDomain);\n                }\n                const firstLeafSkip = processLeaf(elements, idx + 1, leaf.value, newDomain);\n                const secondLeafSkip = processLeaf(\n                    elements,\n                    idx + 1 + firstLeafSkip,\n                    leaf.value,\n                    newDomain\n                );\n                return 1 + firstLeafSkip + secondLeafSkip;\n            }\n            return 0;\n        }\n\n        domain = new Domain(domain);\n        if (domain.ast.value.length === 0) {\n            return domain;\n        }\n        const newDomain = new Domain([]);\n        processLeaf(domain.ast.value, 0, \"&\", newDomain);\n        return newDomain;\n    }\n\n    /**\n     * @param {DomainRepr} [descr]\n     */\n    constructor(descr = []) {\n        if (descr instanceof Domain) {\n            /** @type {AST} */\n            return new Domain(descr.toString());\n        } else {\n            let rawAST;\n            try {\n                rawAST = typeof descr === \"string\" ? parseExpr(descr) : toAST(descr);\n            } catch (error) {\n                throw new InvalidDomainError(`Invalid domain representation: ${descr.toString()}`, {\n                    cause: error,\n                });\n            }\n            this.ast = normalizeDomainAST(rawAST);\n        }\n    }\n\n    /**\n     * Check if the set of records represented by a domain contains a record\n     *\n     * @param {Object} record\n     * @returns {boolean}\n     */\n    contains(record) {\n        const expr = evaluate(this.ast, record);\n        return matchDomain(record, expr);\n    }\n\n    /**\n     * @returns {string}\n     */\n    toString() {\n        return formatAST(this.ast);\n    }\n\n    /**\n     * @param {Object} context\n     * @returns {DomainListRepr}\n     */\n    toList(context) {\n        return evaluate(this.ast, context);\n    }\n\n    /**\n     * Converts the domain into a human-readable format for JSON representation.\n     * If the domain does not contain any contextual value, it is converted to a list.\n     * Otherwise, it is returned as a string.\n     *\n     * The string format is less readable due to escaped double quotes.\n     * Example: \"[\\\"&\\\",[\\\"user_id\\\",\\\"=\\\",uid],[\\\"team_id\\\",\\\"!=\\\",false]]\"\n     * @returns {DomainListRepr | string}\n     */\n    toJson() {\n        try {\n            // Attempt to evaluate the domain without context\n            const evaluatedAsList = this.toList({});\n            const evaluatedDomain = new Domain(evaluatedAsList);\n            if (evaluatedDomain.toString() === this.toString()) {\n                return evaluatedAsList;\n            }\n            return this.toString();\n        } catch {\n            // The domain couldn't be evaluated due to contextual values\n            return this.toString();\n        }\n    }\n}\n\n/**\n * @param {Array[] | boolean} modifier\n * @param {Object} evalContext\n * @returns {boolean}\n */\nexport function evalDomain(modifier, evalContext) {\n    if (modifier && typeof modifier !== \"boolean\") {\n        modifier = new Domain(modifier).contains(evalContext);\n    }\n    return Boolean(modifier);\n}\n\n/** @type {Condition} */\nconst TRUE_LEAF = [1, \"=\", 1];\n/** @type {Condition} */\nconst FALSE_LEAF = [0, \"=\", 1];\nconst TRUE_DOMAIN = new Domain([TRUE_LEAF]);\nconst FALSE_DOMAIN = new Domain([FALSE_LEAF]);\n\nDomain.TRUE = TRUE_DOMAIN;\nDomain.FALSE = FALSE_DOMAIN;\n\n// -----------------------------------------------------------------------------\n// Helpers\n// -----------------------------------------------------------------------------\n\n/**\n * @param {DomainListRepr} domain\n * @returns {AST}\n */\nfunction toAST(domain) {\n    const elems = domain.map((elem) => {\n        switch (elem) {\n            case \"!\":\n            case \"&\":\n            case \"|\":\n                return { type: 1 /* String */, value: elem };\n            default:\n                return {\n                    type: 10 /* Tuple */,\n                    value: elem.map(toPyValue),\n                };\n        }\n    });\n    return { type: 4 /* List */, value: elems };\n}\n\n/**\n * Normalizes a domain\n *\n * @param {AST} domain\n * @param {'&' | '|'} [op]\n * @returns {AST}\n */\n\nfunction normalizeDomainAST(domain, op = \"&\") {\n    if (domain.type !== 4 /* List */) {\n        if (domain.type === 10 /* Tuple */) {\n            const value = domain.value;\n            /* Tuple contains at least one Tuple and optionally string */\n            if (\n                value.findIndex((e) => e.type === 10) === -1 ||\n                !value.every((e) => e.type === 10 || e.type === 1)\n            ) {\n                throw new InvalidDomainError(\"Invalid domain AST\");\n            }\n        } else {\n            throw new InvalidDomainError(\"Invalid domain AST\");\n        }\n    }\n    if (domain.value.length === 0) {\n        return domain;\n    }\n    let expected = 1;\n    for (const child of domain.value) {\n        switch (child.type) {\n            case 1 /* String */:\n                if (child.value === \"&\" || child.value === \"|\") {\n                    expected++;\n                } else if (child.value !== \"!\") {\n                    throw new InvalidDomainError(\"Invalid domain AST\");\n                }\n                break;\n            case 4: /* list */\n            case 10 /* tuple */:\n                if (child.value.length === 3) {\n                    expected--;\n                    break;\n                }\n                throw new InvalidDomainError(\"Invalid domain AST\");\n            default:\n                throw new InvalidDomainError(\"Invalid domain AST\");\n        }\n    }\n    const values = domain.value.slice();\n    while (expected < 0) {\n        expected++;\n        values.unshift({ type: 1 /* String */, value: op });\n    }\n    if (expected > 0) {\n        throw new InvalidDomainError(\n            `invalid domain ${formatAST(domain)} (missing ${expected} segment(s))`\n        );\n    }\n    return { type: 4 /* List */, value: values };\n}\n\n/**\n * @param {Object} record\n * @param {Condition | boolean} condition\n * @returns {boolean}\n */\nfunction matchCondition(record, condition) {\n    if (typeof condition === \"boolean\") {\n        return condition;\n    }\n    const [field, operator, value] = condition;\n\n    if (typeof field === \"string\") {\n        const names = field.split(\".\");\n        if (names.length >= 2) {\n            return matchCondition(record[names[0]], [names.slice(1).join(\".\"), operator, value]);\n        }\n    }\n    let likeRegexp, ilikeRegexp;\n    if ([\"like\", \"not like\", \"ilike\", \"not ilike\"].includes(operator)) {\n        likeRegexp = new RegExp(`(.*)${escapeRegExp(value).replaceAll(\"%\", \"(.*)\")}(.*)`, \"g\");\n        ilikeRegexp = new RegExp(`(.*)${escapeRegExp(value).replaceAll(\"%\", \"(.*)\")}(.*)`, \"gi\");\n    }\n    const fieldValue = typeof field === \"number\" ? field : record[field];\n    switch (operator) {\n        case \"=?\":\n            if ([false, null].includes(value)) {\n                return true;\n            }\n        // eslint-disable-next-line no-fallthrough\n        case \"=\":\n        case \"==\":\n            if (Array.isArray(fieldValue) && Array.isArray(value)) {\n                return shallowEqual(fieldValue, value);\n            }\n            return fieldValue === value;\n        case \"!=\":\n        case \"<>\":\n            return !matchCondition(record, [field, \"==\", value]);\n        case \"<\":\n            return fieldValue < value;\n        case \"<=\":\n            return fieldValue <= value;\n        case \">\":\n            return fieldValue > value;\n        case \">=\":\n            return fieldValue >= value;\n        case \"in\": {\n            const val = Array.isArray(value) ? value : [value];\n            const fieldVal = Array.isArray(fieldValue) ? fieldValue : [fieldValue];\n            return fieldVal.some((fv) => val.includes(fv));\n        }\n        case \"not in\": {\n            const val = Array.isArray(value) ? value : [value];\n            const fieldVal = Array.isArray(fieldValue) ? fieldValue : [fieldValue];\n            return !fieldVal.some((fv) => val.includes(fv));\n        }\n        case \"like\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return Boolean(fieldValue.match(likeRegexp));\n        case \"not like\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return Boolean(!fieldValue.match(likeRegexp));\n        case \"=like\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return new RegExp(escapeRegExp(value).replace(/%/g, \".*\")).test(fieldValue);\n        case \"ilike\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return Boolean(fieldValue.match(ilikeRegexp));\n        case \"not ilike\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return Boolean(!fieldValue.match(ilikeRegexp));\n        case \"=ilike\":\n            if (fieldValue === false) {\n                return false;\n            }\n            return new RegExp(escapeRegExp(value).replace(/%/g, \".*\"), \"i\").test(fieldValue);\n        case \"any\":\n        case \"not_any\":\n            return true;\n    }\n    throw new InvalidDomainError(\"could not match domain\");\n}\n\n/**\n * @param {Object} record\n * @returns {Object}\n */\nfunction makeOperators(record) {\n    const match = matchCondition.bind(null, record);\n    return {\n        \"!\": (x) => !match(x),\n        \"&\": (a, b) => match(a) && match(b),\n        \"|\": (a, b) => match(a) || match(b),\n    };\n}\n\n/**\n *\n * @param {Object} record\n * @param {DomainListRepr} domain\n * @returns {boolean}\n */\nfunction matchDomain(record, domain) {\n    if (domain.length === 0) {\n        return true;\n    }\n    const operators = makeOperators(record);\n    const reversedDomain = Array.from(domain).reverse();\n    const condStack = [];\n    for (const item of reversedDomain) {\n        const operator = typeof item === \"string\" && operators[item];\n        if (operator) {\n            const operands = condStack.splice(-operator.length);\n            condStack.push(operator(...operands));\n        } else {\n            condStack.push(item);\n        }\n    }\n    return matchCondition(record, condStack.pop());\n}\n", "import { Component, onWillStart, onWillUpdateProps } from \"@odoo/owl\";\nimport { Domain } from \"@web/core/domain\";\nimport { TreeEditor } from \"@web/core/tree_editor/tree_editor\";\nimport {\n    domainFromTree,\n    treeFromDomain,\n    formatValue,\n    condition,\n} from \"@web/core/tree_editor/condition_tree\";\nimport { useLoadFieldInfo } from \"@web/core/model_field_selector/utils\";\nimport { CheckBox } from \"@web/core/checkbox/checkbox\";\nimport { deepEqual } from \"@web/core/utils/objects\";\nimport { getDomainDisplayedOperators } from \"@web/core/domain_selector/domain_selector_operator_editor\";\nimport { getOperatorEditorInfo } from \"@web/core/tree_editor/tree_editor_operator_editor\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { ModelFieldSelector } from \"@web/core/model_field_selector/model_field_selector\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { useMakeGetFieldDef } from \"@web/core/tree_editor/utils\";\nimport { getDefaultCondition } from \"./utils\";\n\nconst ARCHIVED_CONDITION = condition(\"active\", \"in\", [true, false]);\nconst ARCHIVED_DOMAIN = `[(\"active\", \"in\", [True, False])]`;\n\nexport class DomainSelector extends Component {\n    static template = \"web.DomainSelector\";\n    static components = { TreeEditor, CheckBox };\n    static props = {\n        domain: String,\n        resModel: String,\n        className: { type: String, optional: true },\n        defaultConnector: { type: [{ value: \"&\" }, { value: \"|\" }], optional: true },\n        isDebugMode: { type: Boolean, optional: true },\n        readonly: { type: Boolean, optional: true },\n        update: { type: Function, optional: true },\n        debugUpdate: { type: Function, optional: true },\n    };\n    static defaultProps = {\n        isDebugMode: false,\n        readonly: true,\n        update: () => {},\n    };\n\n    setup() {\n        this.fieldService = useService(\"field\");\n        this.loadFieldInfo = useLoadFieldInfo(this.fieldService);\n        this.makeGetFieldDef = useMakeGetFieldDef(this.fieldService);\n\n        this.tree = null;\n        this.showArchivedCheckbox = false;\n        this.includeArchived = false;\n\n        onWillStart(() => this.onPropsUpdated(this.props));\n        onWillUpdateProps((np) => this.onPropsUpdated(np));\n    }\n\n    async onPropsUpdated(p) {\n        let domain;\n        let isSupported = true;\n        try {\n            domain = new Domain(p.domain);\n        } catch {\n            isSupported = false;\n        }\n        if (!isSupported) {\n            this.tree = null;\n            this.showArchivedCheckbox = false;\n            this.includeArchived = false;\n            return;\n        }\n\n        const tree = treeFromDomain(domain);\n\n        const getFieldDef = await this.makeGetFieldDef(p.resModel, tree, [\"active\"]);\n\n        this.tree = treeFromDomain(domain, {\n            getFieldDef,\n            distributeNot: !p.isDebugMode,\n        });\n\n        this.showArchivedCheckbox = this.getShowArchivedCheckBox(Boolean(getFieldDef(\"active\")), p);\n        this.includeArchived = false;\n        if (this.showArchivedCheckbox) {\n            if (this.tree.value === \"&\") {\n                this.tree.children = this.tree.children.filter((child) => {\n                    if (deepEqual(child, ARCHIVED_CONDITION)) {\n                        this.includeArchived = true;\n                        return false;\n                    }\n                    return true;\n                });\n                if (this.tree.children.length === 1) {\n                    this.tree = this.tree.children[0];\n                }\n            } else if (deepEqual(this.tree, ARCHIVED_CONDITION)) {\n                this.includeArchived = true;\n                this.tree = treeFromDomain(`[]`);\n            }\n        }\n    }\n\n    getShowArchivedCheckBox(hasActiveField, props) {\n        return hasActiveField;\n    }\n\n    getDefaultCondition(fieldDefs) {\n        return getDefaultCondition(fieldDefs);\n    }\n\n    getDefaultOperator(fieldDef) {\n        return getDomainDisplayedOperators(fieldDef)[0];\n    }\n\n    getOperatorEditorInfo(fieldDef) {\n        const operators = getDomainDisplayedOperators(fieldDef);\n        return getOperatorEditorInfo(operators, fieldDef);\n    }\n\n    getPathEditorInfo(resModel, defaultCondition) {\n        const { isDebugMode } = this.props;\n        return {\n            component: ModelFieldSelector,\n            extractProps: ({ update, value: path }) => {\n                return {\n                    path,\n                    update,\n                    resModel,\n                    isDebugMode,\n                    readonly: false,\n                };\n            },\n            isSupported: (path) => [0, 1].includes(path) || typeof path === \"string\",\n            defaultValue: () => defaultCondition.path,\n            stringify: (path) => formatValue(path),\n            message: _t(\"Invalid field chain\"),\n        };\n    }\n\n    toggleIncludeArchived() {\n        this.includeArchived = !this.includeArchived;\n        this.update(this.tree);\n    }\n\n    resetDomain() {\n        this.props.update(\"[]\");\n    }\n\n    onDomainInput(domain) {\n        if (this.props.debugUpdate) {\n            this.props.debugUpdate(domain);\n        }\n    }\n\n    onDomainChange(domain) {\n        this.props.update(domain, true);\n    }\n    update(tree) {\n        const archiveDomain = this.includeArchived ? ARCHIVED_DOMAIN : `[]`;\n        const domain = tree\n            ? Domain.and([domainFromTree(tree), archiveDomain]).toString()\n            : archiveDomain;\n        this.props.update(domain);\n    }\n}\n", "export function getDomainDisplayedOperators(fieldDef) {\n    if (!fieldDef) {\n        fieldDef = {};\n    }\n    const { type, is_property } = fieldDef;\n\n    if (is_property) {\n        switch (type) {\n            case \"many2many\":\n            case \"tags\":\n                return [\"in\", \"not in\", \"set\", \"not_set\"];\n            case \"many2one\":\n            case \"selection\":\n                return [\"=\", \"!=\", \"set\", \"not_set\"];\n        }\n    }\n\n    switch (type) {\n        case \"boolean\":\n            return [\"is\", \"is_not\"];\n        case \"selection\":\n            return [\"=\", \"!=\", \"in\", \"not in\", \"set\", \"not_set\"];\n        case \"char\":\n        case \"text\":\n        case \"html\":\n            return [\n                \"=\",\n                \"!=\",\n                \"ilike\",\n                \"not ilike\",\n                \"in\",\n                \"not in\",\n                \"set\",\n                \"not_set\",\n                \"starts_with\",\n                \"ends_with\",\n            ];\n        case \"date\":\n        case \"datetime\":\n            return [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"between\", \"within\", \"set\", \"not_set\"];\n        case \"integer\":\n        case \"float\":\n        case \"monetary\":\n            return [\n                \"=\",\n                \"!=\",\n                \">\",\n                \">=\",\n                \"<\",\n                \"<=\",\n                \"between\",\n                \"ilike\",\n                \"not ilike\",\n                \"set\",\n                \"not_set\",\n            ];\n        case \"many2one\":\n        case \"many2many\":\n        case \"one2many\":\n            return [\n                \"in\",\n                \"not in\",\n                \"=\",\n                \"!=\",\n                \"ilike\",\n                \"not ilike\",\n                \"set\",\n                \"not_set\",\n                \"starts_with\",\n                \"ends_with\",\n                \"any\",\n                \"not any\",\n            ];\n        case \"json\":\n            return [\"=\", \"!=\", \"ilike\", \"not ilike\", \"set\", \"not_set\"];\n        case \"properties\":\n            return [\"set\", \"not_set\"];\n        case undefined:\n            return [\"=\"];\n        default:\n            return [\n                \"=\",\n                \"!=\",\n                \">\",\n                \">=\",\n                \"<\",\n                \"<=\",\n                \"ilike\",\n                \"not ilike\",\n                \"like\",\n                \"not like\",\n                \"=like\",\n                \"=ilike\",\n                \"child_of\",\n                \"parent_of\",\n                \"in\",\n                \"not in\",\n                \"set\",\n                \"not_set\",\n            ];\n    }\n}\n", "import { getDefaultValue } from \"@web/core/tree_editor/tree_editor_value_editors\";\nimport { getDomainDisplayedOperators } from \"@web/core/domain_selector/domain_selector_operator_editor\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { domainFromTree, condition } from \"@web/core/tree_editor/condition_tree\";\nimport { getDefaultPath } from \"@web/core/tree_editor/utils\";\n\nexport function getDefaultCondition(fieldDefs) {\n    const defaultPath = getDefaultPath(fieldDefs);\n    const fieldDef = fieldDefs[defaultPath];\n    const operator = getDomainDisplayedOperators(fieldDef)[0];\n    const value = getDefaultValue(fieldDef, operator);\n    return condition(fieldDef.name, operator, value);\n}\n\nexport function getDefaultDomain(fieldDefs) {\n    return domainFromTree(getDefaultCondition(fieldDefs));\n}\n\nexport function useGetDefaultLeafDomain() {\n    const fieldService = useService(\"field\");\n    return async (resModel) => {\n        const fieldDefs = await fieldService.loadFields(resModel);\n        return getDefaultDomain(fieldDefs);\n    };\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { Component, useRef, useState } from \"@odoo/owl\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { Domain } from \"@web/core/domain\";\nimport { DomainSelector } from \"@web/core/domain_selector/domain_selector\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { user } from \"@web/core/user\";\n\nexport class DomainSelectorDialog extends Component {\n    static template = \"web.DomainSelectorDialog\";\n    static components = {\n        Dialog,\n        DomainSelector,\n    };\n    static props = {\n        close: Function,\n        onConfirm: Function,\n        resModel: String,\n        className: { type: String, optional: true },\n        defaultConnector: { type: [{ value: \"&\" }, { value: \"|\" }], optional: true },\n        domain: String,\n        isDebugMode: { type: Boolean, optional: true },\n        readonly: { type: Boolean, optional: true },\n        text: { type: String, optional: true },\n        confirmButtonText: { type: String, optional: true },\n        disableConfirmButton: { type: Function, optional: true },\n        discardButtonText: { type: String, optional: true },\n        title: { type: String, optional: true },\n        context: { type: Object, optional: true },\n    };\n    static defaultProps = {\n        isDebugMode: false,\n        readonly: false,\n        context: {},\n    };\n\n    setup() {\n        this.notification = useService(\"notification\");\n        this.orm = useService(\"orm\");\n        this.state = useState({ domain: this.props.domain });\n        this.confirmButtonRef = useRef(\"confirm\");\n    }\n\n    get confirmButtonText() {\n        return this.props.confirmButtonText || _t(\"Confirm\");\n    }\n\n    get dialogTitle() {\n        return this.props.title || _t(\"Domain\");\n    }\n\n    get disabled() {\n        if (this.props.disableConfirmButton) {\n            return this.props.disableConfirmButton(this.state.domain);\n        }\n        return false;\n    }\n\n    get discardButtonText() {\n        return this.props.discardButtonText || _t(\"Discard\");\n    }\n\n    get domainSelectorProps() {\n        return {\n            className: this.props.className,\n            resModel: this.props.resModel,\n            readonly: this.props.readonly,\n            isDebugMode: this.props.isDebugMode,\n            defaultConnector: this.props.defaultConnector,\n            domain: this.state.domain,\n            update: (domain) => {\n                this.state.domain = domain;\n            },\n        };\n    }\n\n    async onConfirm() {\n        this.confirmButtonRef.el.disabled = true;\n        let domain;\n        let isValid;\n        try {\n            const evalContext = { ...user.context, ...this.props.context };\n            domain = new Domain(this.state.domain).toList(evalContext);\n        } catch {\n            isValid = false;\n        }\n        if (isValid === undefined) {\n            isValid = await rpc(\"/web/domain/validate\", {\n                model: this.props.resModel,\n                domain,\n            });\n        }\n        if (!isValid) {\n            if (this.confirmButtonRef.el) {\n                this.confirmButtonRef.el.disabled = false;\n            }\n            this.notification.add(_t(\"Domain is invalid. Please correct it\"), {\n                type: \"danger\",\n            });\n            return;\n        }\n        this.props.onConfirm(this.state.domain);\n        this.props.close();\n    }\n\n    onDiscard() {\n        this.props.close();\n    }\n}\n", "import { useComponent, useEffect, useEnv } from \"@odoo/owl\";\nimport { DROPDOWN_GROUP } from \"@web/core/dropdown/dropdown_group\";\n\n/**\n * @typedef DropdownGroupState\n * @property {boolean} isInGroup\n * @property {boolean} isOpen\n */\n\n/**\n * Will add (and remove) a dropdown from a parent\n * DropdownGroup component, allowing it to know\n * if it's in a group and if the group is open.\n *\n * @returns {DropdownGroupState}\n */\nexport function useDropdownGroup() {\n    const env = useEnv();\n\n    const group = {\n        isInGroup: DROPDOWN_GROUP in env,\n        get isOpen() {\n            return this.isInGroup && [...env[DROPDOWN_GROUP]].some((dropdown) => dropdown.isOpen);\n        },\n    };\n\n    if (group.isInGroup) {\n        const dropdown = useComponent();\n        useEffect(() => {\n            env[DROPDOWN_GROUP].add(dropdown.state);\n            return () => env[DROPDOWN_GROUP].delete(dropdown.state);\n        });\n    }\n\n    return group;\n}\n", "import { EventBus, onWillDestroy, useChildSubEnv, useEffect, useEnv } from \"@odoo/owl\";\nimport { localization } from \"@web/core/l10n/localization\";\nimport { useBus, useService } from \"@web/core/utils/hooks\";\nimport { effect } from \"@web/core/utils/reactive\";\n\nexport const DROPDOWN_NESTING = Symbol(\"dropdownNesting\");\nconst BUS = new EventBus();\n\nclass DropdownNestingState {\n    constructor({ parent, close }) {\n        this._isOpen = false;\n        this.parent = parent;\n        this.children = new Set();\n        this.close = close;\n\n        parent?.children.add(this);\n    }\n\n    set isOpen(value) {\n        this._isOpen = value;\n        if (this._isOpen) {\n            BUS.trigger(\"dropdown-opened\", this);\n        }\n    }\n\n    get isOpen() {\n        return this._isOpen;\n    }\n\n    remove() {\n        this.parent?.children.delete(this);\n    }\n\n    closeAllParents() {\n        this.close();\n        if (this.parent) {\n            this.parent.closeAllParents();\n        }\n    }\n\n    closeChildren() {\n        this.children.forEach((child) => child.close());\n    }\n\n    shouldIgnoreChanges(other) {\n        return (\n            other === this ||\n            other.activeEl !== this.activeEl ||\n            [...this.children].some((child) => child.shouldIgnoreChanges(other))\n        );\n    }\n\n    handleChange(other) {\n        // Prevents closing the dropdown when a change is coming from itself or from a children.\n        if (this.shouldIgnoreChanges(other)) {\n            return;\n        }\n\n        if (other.isOpen && this.isOpen) {\n            this.close();\n        }\n    }\n}\n\n/**\n * This hook is used to manage communication between dropdowns.\n *\n * When a dropdown is open, every other dropdown that is not a parent\n * is closed. It also uses the current's ui active element to only\n * close itself when the active element is the same as the current\n * dropdown to separate dropdowns in different dialogs.\n *\n * @param {import(\"@web/core/dropdown/dropdown\").DropdownState} state\n * @returns\n */\nexport function useDropdownNesting(state) {\n    const env = useEnv();\n    const current = new DropdownNestingState({\n        parent: env[DROPDOWN_NESTING],\n        close: () => state.close(),\n    });\n\n    // Set up UI active element related behavior ---------------------------\n    const uiService = useService(\"ui\");\n    useEffect(\n        () => {\n            Promise.resolve().then(() => {\n                current.activeEl = uiService.activeElement;\n            });\n        },\n        () => []\n    );\n\n    useChildSubEnv({ [DROPDOWN_NESTING]: current });\n    useBus(BUS, \"dropdown-opened\", ({ detail: other }) => current.handleChange(other));\n\n    effect(\n        (state) => {\n            current.isOpen = state.isOpen;\n        },\n        [state]\n    );\n\n    onWillDestroy(() => {\n        current.remove();\n    });\n\n    return {\n        get hasParent() {\n            return Boolean(current.parent);\n        },\n        /**@type {import(\"@web/core/navigation/navigation\").NavigationOptions} */\n        navigationOptions: {\n            onEnabled: (items) => {\n                if (current.parent) {\n                    items[0]?.focus();\n                }\n            },\n            onMouseEnter: (item) => {\n                if (item.target.classList.contains(\"o-dropdown\")) {\n                    item.select();\n                }\n            },\n            hotkeys: {\n                escape: () => current.close(),\n                arrowleft: (index, items) => {\n                    if (\n                        localization.direction === \"rtl\" &&\n                        items[index]?.target.classList.contains(\"o-dropdown\")\n                    ) {\n                        items[index]?.select();\n                    } else if (current.parent) {\n                        current.close();\n                    }\n                },\n                arrowright: (index, items) => {\n                    if (localization.direction === \"rtl\" && current.parent) {\n                        current.close();\n                    } else if (items[index]?.target.classList.contains(\"o-dropdown\")) {\n                        items[index]?.select();\n                    }\n                },\n            },\n        },\n    };\n}\n", "import { Component, onMounted, onRendered, onWillDestroy, onWillStart, xml } from \"@odoo/owl\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\n\nexport class DropdownPopover extends Component {\n    static components = { DropdownItem };\n    static template = xml`\n        <t t-if=\"this.props.items\">\n            <t t-foreach=\"this.props.items\" t-as=\"item\" t-key=\"this.getKey(item, item_index)\">\n                <DropdownItem class=\"item.class\" onSelected=\"() => item.onSelected()\" t-out=\"item.label\"/>\n            </t>\n        </t>\n        <t t-slot=\"content\" />\n    `;\n    static props = {\n        // Popover service\n        close: { type: Function, optional: true },\n\n        // Events & Handlers\n        beforeOpen: { type: Function, optional: true },\n        onOpened: { type: Function, optional: true },\n        onClosed: { type: Function, optional: true },\n\n        // Rendering & Context\n        refresher: Object,\n        slots: Object,\n        items: { type: Array, optional: true },\n    };\n\n    setup() {\n        onRendered(() => {\n            // Note that the Dropdown component and the DropdownPopover component\n            // are not in the same context.\n            // So when the Dropdown component is re-rendered, the DropdownPopover\n            // component must also re-render itself.\n            // This is why we subscribe to this reactive, which is changed when\n            // the Dropdown component is re-rendered.\n            this.props.refresher.token;\n        });\n\n        onWillStart(async () => {\n            await this.props.beforeOpen?.();\n        });\n\n        onMounted(() => {\n            this.props.onOpened?.();\n        });\n\n        onWillDestroy(() => {\n            this.props.onClosed?.();\n        });\n    }\n\n    getKey(item, index) {\n        return \"id\" in item ? item.id : index;\n    }\n}\n", "import { Component, onPatched, useState } from \"@odoo/owl\";\n\nexport const ACCORDION = Symbol(\"Accordion\");\nexport class AccordionItem extends Component {\n    static template = \"web.AccordionItem\";\n    static components = {};\n    static props = {\n        slots: {\n            type: Object,\n            shape: {\n                default: {},\n            },\n        },\n        description: String,\n        selected: {\n            type: Boolean,\n            optional: true,\n        },\n        class: {\n            type: String,\n            optional: true,\n        },\n    };\n    static defaultProps = {\n        class: \"\",\n        selected: false,\n    };\n\n    setup() {\n        this.state = useState({\n            open: false,\n        });\n        this.parentComponent = this.env[ACCORDION];\n        onPatched(() => {\n            this.parentComponent?.accordionStateChanged?.();\n        });\n    }\n}\n", "import { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\n\nexport class CheckboxItem extends DropdownItem {\n    static template = \"web.CheckboxItem\";\n    static props = {\n        ...DropdownItem.props,\n        checked: {\n            type: Boolean,\n            optional: false,\n        },\n    };\n}\n", "import {\n    Component,\n    onMounted,\n    onRendered,\n    onWillUpdateProps,\n    reactive,\n    status,\n    useEffect,\n    xml,\n} from \"@odoo/owl\";\nimport { useDropdownGroup } from \"@web/core/dropdown/_behaviours/dropdown_group_hook\";\nimport { useDropdownNesting } from \"@web/core/dropdown/_behaviours/dropdown_nesting\";\nimport { DropdownPopover } from \"@web/core/dropdown/_behaviours/dropdown_popover\";\nimport { useDropdownState } from \"@web/core/dropdown/dropdown_hooks\";\nimport { useNavigation } from \"@web/core/navigation/navigation\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\nimport { mergeClasses } from \"@web/core/utils/classname\";\nimport { useChildRef, useService } from \"@web/core/utils/hooks\";\nimport { deepMerge } from \"@web/core/utils/objects\";\nimport { effect } from \"@web/core/utils/reactive\";\n\nfunction getFirstElementOfNode(node) {\n    if (!node) {\n        return null;\n    }\n    if (node.el) {\n        return node.el.nodeType === Node.ELEMENT_NODE ? node.el : null;\n    }\n    if (node.bdom || node.child) {\n        return getFirstElementOfNode(node.bdom || node.child);\n    }\n    if (node.children) {\n        for (const child of node.children) {\n            const el = getFirstElementOfNode(child);\n            if (el) {\n                return el;\n            }\n        }\n    }\n    return null;\n}\n\n/**\n * The Dropdown component allows to define a menu that will\n * show itself when a target is toggled.\n *\n * Items are defined using DropdownItems. Dropdowns are\n * also allowed as items to be able to create nested\n * dropdown menus.\n */\nexport class Dropdown extends Component {\n    static template = xml`<t t-slot=\"default\"/>`;\n    static components = {};\n    static props = {\n        arrow: { optional: true },\n        menuClass: { optional: true },\n        position: { type: String, optional: true },\n        slots: {\n            type: Object,\n            shape: {\n                default: { optional: true },\n                content: { optional: true },\n            },\n        },\n\n        items: {\n            optional: true,\n            type: Array,\n            elements: {\n                type: Object,\n                shape: {\n                    label: String,\n                    onSelected: Function,\n                    class: { optional: true },\n                    \"*\": true,\n                },\n            },\n        },\n\n        menuRef: { type: Function, optional: true }, // to be used with useChildRef\n        disabled: { type: Boolean, optional: true },\n        holdOnHover: { type: Boolean, optional: true },\n\n        beforeOpen: { type: Function, optional: true },\n        onOpened: { type: Function, optional: true },\n        onStateChanged: { type: Function, optional: true },\n\n        /** Manual state handling, @see useDropdownState */\n        state: {\n            type: Object,\n            shape: {\n                isOpen: Boolean,\n                close: Function,\n                open: Function,\n                \"*\": true,\n            },\n            optional: true,\n        },\n        manual: { type: Boolean, optional: true },\n\n        /**\n         * Override the internal navigation hook options\n         * @type {import(\"@web/core/navigation/navigation\").NavigationOptions}\n         */\n        navigationOptions: { type: Object, optional: true },\n    };\n    static defaultProps = {\n        arrow: false,\n        disabled: false,\n        holdOnHover: false,\n        menuClass: \"\",\n        state: undefined,\n        navigationOptions: {},\n    };\n\n    setup() {\n        this.menuRef = this.props.menuRef || useChildRef();\n\n        this.state = this.props.state || useDropdownState();\n        this.nesting = useDropdownNesting(this.state);\n        this.group = useDropdownGroup();\n        this.navigation = useNavigation(this.menuRef, {\n            focusInitialElementOnDisabled: () => !this.group.isInGroup,\n            itemsSelector: \":scope .o-navigable, :scope .o-dropdown\",\n            // Using deepMerge allows to keep entries of both option.hotkeys\n            ...deepMerge(this.nesting.navigationOptions, this.props.navigationOptions),\n        });\n\n        // Set up UI active element related behavior ---------------------------\n        let activeEl;\n        this.uiService = useService(\"ui\");\n        useEffect(\n            () => {\n                Promise.resolve().then(() => {\n                    activeEl = this.uiService.activeElement;\n                });\n            },\n            () => []\n        );\n\n        this.popover = usePopover(DropdownPopover, {\n            animation: false,\n            arrow: this.props.arrow,\n            closeOnClickAway: (target) => {\n                return this.popoverCloseOnClickAway(target, activeEl);\n            },\n            closeOnEscape: false, // Handled via navigation and prevents closing root of nested dropdown\n            env: this.__owl__.childEnv,\n            holdOnHover: this.props.holdOnHover,\n            onClose: () => this.state.close(),\n            onPositioned: (el, { direction }) => this.setTargetDirectionClass(direction),\n            popoverClass: mergeClasses(\n                \"o-dropdown--menu dropdown-menu mx-0\",\n                { \"o-dropdown--menu-submenu\": this.hasParent },\n                this.props.menuClass\n            ),\n            popoverRole: \"menu\",\n            position: this.position,\n            ref: this.menuRef,\n            setActiveElement: false,\n        });\n\n        // As the popover is in another context we need to force\n        // its re-rendering when the dropdown re-renders\n        onRendered(() => (this.popoverRefresher ? this.popoverRefresher.token++ : null));\n\n        onMounted(() => this.onStateChanged(this.state));\n        effect((state) => this.onStateChanged(state), [this.state]);\n\n        useEffect(\n            (target) => this.setTargetElement(target),\n            () => [this.target]\n        );\n\n        onWillUpdateProps(({ disabled }) => {\n            if (disabled) {\n                this.closePopover();\n            }\n        });\n    }\n\n    /** @type {string} */\n    get position() {\n        return this.props.position || (this.hasParent ? \"right-start\" : \"bottom-start\");\n    }\n\n    get hasParent() {\n        return this.nesting.hasParent;\n    }\n\n    /** @type {HTMLElement|null} */\n    get target() {\n        const target = getFirstElementOfNode(this.__owl__.bdom);\n        if (!target) {\n            throw new Error(\n                \"Could not find a valid dropdown toggler, prefer a single html element and put any dynamic content inside of it.\"\n            );\n        }\n        return target;\n    }\n\n    handleClick(event) {\n        if (this.props.disabled) {\n            return;\n        }\n\n        event.stopPropagation();\n        if (this.state.isOpen && !this.hasParent) {\n            this.state.close();\n        } else {\n            this.state.open();\n        }\n    }\n\n    handleMouseEnter() {\n        if (this.props.disabled) {\n            return;\n        }\n\n        if (this.hasParent || this.group.isOpen) {\n            this.target.focus();\n            this.state.open();\n        }\n    }\n\n    onStateChanged(state) {\n        if (state.isOpen) {\n            this.openPopover();\n        } else {\n            this.closePopover();\n        }\n    }\n\n    popoverCloseOnClickAway(target, activeEl) {\n        return this.uiService.getActiveElementOf(target) === activeEl;\n    }\n\n    setTargetElement(target) {\n        if (!target) {\n            return;\n        }\n\n        target.ariaExpanded = false;\n        target.classList.add(\"o-dropdown\");\n\n        if (this.hasParent) {\n            target.classList.add(\"o-dropdown--has-parent\");\n        }\n\n        const tagName = target.tagName.toLowerCase();\n        if (![\"input\", \"textarea\", \"table\", \"thead\", \"tbody\", \"tr\", \"th\", \"td\"].includes(tagName)) {\n            target.classList.add(\"dropdown-toggle\");\n            if (this.hasParent) {\n                target.classList.add(\"o-dropdown-item\", \"o-navigable\", \"dropdown-item\");\n\n                if (!target.classList.contains(\"o-dropdown--no-caret\")) {\n                    target.classList.add(\"o-dropdown-caret\");\n                }\n            }\n        }\n\n        this.defaultDirection = this.position.split(\"-\")[0];\n        this.setTargetDirectionClass(this.defaultDirection);\n\n        if (!this.props.manual) {\n            target.addEventListener(\"click\", this.handleClick.bind(this));\n            target.addEventListener(\"mouseenter\", this.handleMouseEnter.bind(this));\n\n            return () => {\n                target.removeEventListener(\"click\", this.handleClick.bind(this));\n                target.removeEventListener(\"mouseenter\", this.handleMouseEnter.bind(this));\n            };\n        }\n    }\n\n    setTargetDirectionClass(direction) {\n        if (!this.target) {\n            return;\n        }\n        const directionClasses = {\n            bottom: \"dropdown\",\n            top: \"dropup\",\n            left: \"dropstart\",\n            right: \"dropend\",\n        };\n        this.target.classList.remove(...Object.values(directionClasses));\n        this.target.classList.add(directionClasses[direction]);\n    }\n\n    openPopover() {\n        if (this.popover.isOpen || status(this) !== \"mounted\") {\n            return;\n        }\n        if (!this.target || !this.target.isConnected) {\n            this.state.close();\n            return;\n        }\n\n        this.popoverRefresher = reactive({ token: 0 });\n        const props = {\n            beforeOpen: () => this.props.beforeOpen?.(),\n            onOpened: () => this.onOpened(),\n            onClosed: () => this.onClosed(),\n            refresher: this.popoverRefresher,\n            items: this.props.items,\n            slots: this.props.slots,\n        };\n        this.popover.open(this.target, props);\n    }\n\n    closePopover() {\n        this.popover.close();\n        this.navigation.disable();\n    }\n\n    onOpened() {\n        this.navigation.enable();\n        this.props.onOpened?.();\n        this.props.onStateChanged?.(true);\n\n        if (this.target) {\n            this.target.ariaExpanded = true;\n            this.target.classList.add(\"show\");\n        }\n    }\n\n    onClosed() {\n        this.props.onStateChanged?.(false);\n\n        if (this.target) {\n            this.target.ariaExpanded = false;\n            this.target.classList.remove(\"show\");\n            this.setTargetDirectionClass(this.defaultDirection);\n        }\n    }\n}\n", "import { Component, onWillDestroy, useChildSubEnv, xml } from \"@odoo/owl\";\n\nconst GROUPS = new Map();\n\nfunction getGroup(id) {\n    if (!GROUPS.has(id)) {\n        GROUPS.set(id, {\n            group: new Set(),\n            count: 0,\n        });\n    }\n    GROUPS.get(id).count++;\n    return GROUPS.get(id).group;\n}\n\nfunction removeGroup(id) {\n    const groupData = GROUPS.get(id);\n    groupData.count--;\n    if (groupData.count <= 0) {\n        GROUPS.delete(id);\n    }\n}\n\nexport const DROPDOWN_GROUP = Symbol(\"dropdownGroup\");\nexport class DropdownGroup extends Component {\n    static template = xml`<t t-slot=\"default\"/>`;\n    static props = {\n        group: { type: String, optional: true },\n        slots: Object,\n    };\n\n    setup() {\n        if (this.props.group) {\n            const group = getGroup(this.props.group);\n            onWillDestroy(() => removeGroup(this.props.group));\n            useChildSubEnv({ [DROPDOWN_GROUP]: group });\n        } else {\n            useChildSubEnv({ [DROPDOWN_GROUP]: new Set() });\n        }\n    }\n}\n", "import { useEnv, useState } from \"@odoo/owl\";\nimport { DROPDOWN_NESTING } from \"@web/core/dropdown/_behaviours/dropdown_nesting\";\n\n/**\n * @typedef {Object} DropdownState\n * @property {() => void} open\n * @property {() => void} close\n * @property {boolean} isOpen\n */\n\n/**\n * Hook used to interact with the Dropdown state.\n * In order to use it, pass the returned state to the dropdown component, i.e.:\n *  <Dropdown state=\"dropdownState\" ...>...</Dropdown>\n * @param {Object} callbacks\n * @param {Function} callbacks.onOpen\n * @param {Function} callbacks.onClose\n * @returns {DropdownState}\n */\nexport function useDropdownState({ onOpen, onClose } = {}) {\n    const state = useState({\n        isOpen: false,\n        open: () => {\n            state.isOpen = true;\n            onOpen?.();\n        },\n        close: () => {\n            state.isOpen = false;\n            onClose?.();\n        },\n    });\n    return state;\n}\n\n/**\n * Can be used by components to have some control\n * how and when a wrapping dropdown should close.\n */\nexport function useDropdownCloser() {\n    const env = useEnv();\n    const dropdown = env[DROPDOWN_NESTING];\n    return {\n        close: () => dropdown?.close(),\n        closeChildren: () => dropdown?.closeChildren(),\n        closeAll: () => dropdown?.closeAllParents(),\n    };\n}\n", "import { Component } from \"@odoo/owl\";\nimport { useDropdownCloser } from \"@web/core/dropdown/dropdown_hooks\";\n\nconst ClosingMode = {\n    None: \"none\",\n    ClosestParent: \"closest\",\n    AllParents: \"all\",\n};\n\nexport class DropdownItem extends Component {\n    static template = \"web.DropdownItem\";\n    static props = {\n        class: {\n            type: [String, Object],\n            optional: true,\n        },\n        onSelected: {\n            type: Function,\n            optional: true,\n        },\n        closingMode: {\n            type: ClosingMode,\n            optional: true,\n        },\n        attrs: {\n            type: Object,\n            optional: true,\n        },\n        slots: { Object, optional: true },\n    };\n    static defaultProps = {\n        closingMode: ClosingMode.AllParents,\n        attrs: {},\n    };\n\n    setup() {\n        this.dropdownControl = useDropdownCloser();\n    }\n\n    onClick(ev) {\n        if (this.props.attrs && this.props.attrs.href) {\n            ev.preventDefault();\n        }\n        this.props.onSelected?.();\n        switch (this.props.closingMode) {\n            case ClosingMode.ClosestParent:\n                this.dropdownControl.close();\n                break;\n            case ClosingMode.AllParents:\n                this.dropdownControl.closeAll();\n                break;\n        }\n    }\n}\n", "import { Component, useEffect, useRef, useState } from \"@odoo/owl\";\n\nexport class Dropzone extends Component {\n    static props = {\n        extraClass: { type: String, optional: true },\n        onDrop: { type: Function, optional: true },\n        ref: Object,\n        slots: { type: Object, optional: true },\n    };\n    static template = \"web.Dropzone\";\n\n    setup() {\n        super.setup();\n        this.root = useRef(\"root\");\n        this.state = useState({\n            isDraggingInside: false,\n        });\n        useEffect(() => {\n            const { top, left, width, height } = this.props.ref.el.getBoundingClientRect();\n            this.root.el.style = `top:${top}px;left:${left}px;width:${width}px;height:${height}px;`;\n        });\n    }\n}\n", "import { Dropzone } from \"@web/core/dropzone/dropzone\";\nimport { useEffect, useExternalListener } from \"@odoo/owl\";\nimport { useService } from \"@web/core/utils/hooks\";\n\n/**\n * @param {Ref} targetRef - Element on which to place the dropzone.\n * @param {Class} dropzoneComponent - Class used to instantiate the dropzone component.\n * @param {Object} dropzoneComponentProps - Props given to the instantiated dropzone component.\n * @param {function} isDropzoneEnabled - Function that determines whether the dropzone should be enabled.\n */\nexport function useCustomDropzone(targetRef, dropzoneComponent, dropzoneComponentProps, isDropzoneEnabled = () => true) {\n    const overlayService = useService(\"overlay\");\n    const uiService = useService(\"ui\");\n\n    let dragCount = 0;\n    let hasTarget = false;\n    let removeDropzone = false;\n\n    useExternalListener(document, \"dragenter\", onDragEnter, { capture: true });\n    useExternalListener(document, \"dragleave\", onDragLeave, { capture: true });\n    // Prevents the browser to open or download the file when it is dropped\n    // outside of the dropzone.\n    useExternalListener(window, \"dragover\", (ev) => ev.preventDefault());\n    useExternalListener(window, \"drop\", (ev) => {\n        ev.preventDefault();\n        dragCount = 0;\n        updateDropzone();\n    }, { capture: true });\n\n    function updateDropzone() {\n        const hasDropzone = !!removeDropzone;\n        const isTargetInActiveElement = uiService.activeElement.contains(targetRef.el);\n        const shouldDisplayDropzone = dragCount && hasTarget && isTargetInActiveElement && isDropzoneEnabled();\n\n        if (shouldDisplayDropzone && !hasDropzone) {\n            removeDropzone = overlayService.add(dropzoneComponent, {\n                ref: targetRef,\n                ...dropzoneComponentProps\n            });\n        }\n        if (!shouldDisplayDropzone && hasDropzone) {\n            removeDropzone();\n            removeDropzone = false;\n        }\n    }\n\n    function onDragEnter(ev) {\n        if (dragCount || (ev.dataTransfer && ev.dataTransfer.types.includes(\"Files\"))) {\n            dragCount++;\n            updateDropzone();\n        }\n    }\n\n    function onDragLeave() {\n        if (dragCount) {\n            dragCount--;\n            updateDropzone();\n        }\n    }\n\n    useEffect(\n        (el) => {\n            hasTarget = !!el;\n            updateDropzone();\n        },\n        () => [targetRef.el]\n    );\n}\n\n/**\n * @param {Ref} targetRef - Element on which to place the dropzone.\n * @param {function} onDrop - Callback function called when the user drops a file on the dropzone.\n * @param {string} extraClass - Classes that will be added to the standard `Dropzone` component.\n * @param {function} isDropzoneEnabled - Function that determines whether the dropzone should be enabled.\n */\nexport function useDropzone(targetRef, onDrop, extraClass, isDropzoneEnabled = () => true) {\n    const dropzoneComponent = Dropzone;\n    const dropzoneComponentProps = { extraClass, onDrop };\n    useCustomDropzone(targetRef, dropzoneComponent, dropzoneComponentProps, isDropzoneEnabled);\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"@web/core/registry\";\nimport { user } from \"@web/core/user\";\nimport { RainbowMan } from \"./rainbow_man\";\n\nconst effectRegistry = registry.category(\"effects\");\n\n// -----------------------------------------------------------------------------\n// RainbowMan effect\n// -----------------------------------------------------------------------------\n\n/**\n * Handles effect of type \"rainbow_man\". If the effects aren't disabled, returns\n * the RainbowMan component to instantiate and its props. If the effects are\n * disabled, displays the message in a notification.\n *\n * @param {Object} env\n * @param {Object} [params={}]\n * @param {string} [params.message=\"Well Done!\"]\n *    The message in the notice the rainbowman holds or the content of the notification if effects are disabled\n *    Can be a simple a string\n *    Can be a string representation of html (prefer component if you want interactions in the DOM)\n * @param {string} [params.img_url=\"/web/static/img/smile.svg\"]\n *    The url of the image to display inside the rainbow\n * @param {\"slow\"|\"medium\"|\"fast\"|\"no\"} [params.fadeout=\"medium\"]\n *    Delay for rainbowman to disappear\n *    'fast' will make rainbowman dissapear quickly\n *    'medium' and 'slow' will wait little longer before disappearing (can be used when options.message is longer)\n *    'no' will keep rainbowman on screen until user clicks anywhere outside rainbowman\n * @param {typeof import(\"@odoo/owl\").Component} [params.Component]\n *    Custom Component class to instantiate inside the Rainbow Man\n * @param {Object} [params.props]\n *    If params.Component is given, its props can be passed with this argument\n */\nfunction rainbowMan(env, params = {}) {\n    let message = params.message;\n    if (message instanceof Element) {\n        console.log(\n            \"Providing an HTML element to an effect is deprecated. Note that all event handlers will be lost.\"\n        );\n        message = message.outerHTML;\n    } else if (!message) {\n        message = _t(\"Well Done!\");\n    }\n    if (user.showEffect) {\n        /** @type {import(\"./rainbow_man\").RainbowManProps} */\n        const props = {\n            imgUrl: params.img_url || \"/web/static/img/smile.svg\",\n            fadeout: params.fadeout || \"medium\",\n            message,\n            Component: params.Component,\n            props: params.props,\n        };\n        return { Component: RainbowMan, props };\n    }\n    env.services.notification.add(message);\n}\neffectRegistry.add(\"rainbow_man\", rainbowMan);\n\n// -----------------------------------------------------------------------------\n// Effect service\n// -----------------------------------------------------------------------------\n\nexport const effectService = {\n    dependencies: [\"overlay\"],\n    start(env, { overlay }) {\n        /**\n         * @param {Object} [params] various params depending on the type of effect\n         * @param {string} [params.type=\"rainbow_man\"] the effect to display\n         */\n        const add = (params = {}) => {\n            const type = params.type || \"rainbow_man\";\n            const effect = effectRegistry.get(type);\n            const { Component, props } = effect(env, params) || {};\n            if (Component) {\n                const remove = overlay.add(Component, {\n                    ...props,\n                    close: () => remove(),\n                });\n            }\n        };\n\n        return { add };\n    },\n};\n\nregistry.category(\"services\").add(\"effect\", effectService);\n", "import { browser } from \"@web/core/browser/browser\";\n\nimport { Component, useEffect, useExternalListener, useState } from \"@odoo/owl\";\n\n/**\n * @typedef Common\n * @property {string} [fadeout='medium'] Delay for rainbowman to disappear.\n *  - 'fast' will make rainbowman dissapear quickly,\n *  - 'medium' and 'slow' will wait little longer before disappearing\n *      (can be used when props.message is longer),\n *  - 'no' will keep rainbowman on screen until user clicks anywhere outside rainbowman\n * @property {string} [imgUrl] URL of the image to be displayed\n *\n * @typedef Simple\n * @property {string} message Message to be displayed on rainbowman card\n *\n * @typedef Custom\n * @property {typeof import(\"@odoo/owl\").Component} Component\n * @property {any} [props]\n *\n * @typedef {Common & (Simple | Custom)} RainbowManProps\n */\n\n/**\n * The RainbowMan Component is meant to display a 'fun/rewarding' message.  For\n * example, when the user marked a large deal as won, or when he cleared its inbox.\n *\n * This component is mostly a picture and a message with a rainbow animation around.\n * If you want to display a RainbowMan, you probably do not want to do it by\n * importing this file.  The usual way to do that would be to use the effect\n * service.\n */\nexport class RainbowMan extends Component {\n    static template = \"web.RainbowMan\";\n    static rainbowFadeouts = { slow: 4500, medium: 3500, fast: 2000, no: false };\n    static props = {\n        fadeout: String,\n        close: Function,\n        message: String,\n        imgUrl: String,\n        Component: { type: Function, optional: true },\n        props: { type: Object, optional: true },\n    };\n\n    setup() {\n        useExternalListener(document.body, \"click\", this.closeRainbowMan);\n        this.state = useState({ isFading: false });\n        this.delay = RainbowMan.rainbowFadeouts[this.props.fadeout];\n        if (this.delay) {\n            useEffect(\n                () => {\n                    const timeout = browser.setTimeout(() => {\n                        this.state.isFading = true;\n                    }, this.delay);\n                    return () => browser.clearTimeout(timeout);\n                },\n                () => []\n            );\n        }\n    }\n\n    onAnimationEnd(ev) {\n        if (this.delay && ev.animationName === \"reward-fading-reverse\") {\n            ev.stopPropagation();\n            this.closeRainbowMan();\n        }\n    }\n\n    closeRainbowMan() {\n        this.props.close();\n    }\n}\n", "import { markEventHandled } from \"@web/core/utils/misc\";\n\nimport {\n    Component,\n    onMounted,\n    onPatched,\n    onWillDestroy,\n    onWillPatch,\n    onWillStart,\n    onWillUnmount,\n    useEffect,\n    useRef,\n    useState,\n} from \"@odoo/owl\";\n\nimport { loadBundle } from \"@web/core/assets\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\nimport { fuzzyLookup } from \"@web/core/utils/search\";\nimport { useAutofocus, useService } from \"@web/core/utils/hooks\";\nimport { isMobileOS } from \"@web/core/browser/feature_detection\";\n\n/**\n *\n * @param {import(\"@web/core/utils/hooks\").Ref} [ref]\n * @param {Object} props\n * @param {import(\"@web/core/popover/popover_service\").PopoverServiceAddOptions} [options]\n * @param {function} [props.onSelect]\n * @param {function} [props.onClose]\n */\nexport function useEmojiPicker(ref, props, options = {}) {\n    const targets = [];\n    const state = useState({ isOpen: false });\n    const newOptions = {\n        ...options,\n        onClose: () => {\n            state.isOpen = false;\n            options.onClose?.();\n        },\n    };\n    const popover = usePopover(EmojiPicker, { ...newOptions, animation: false });\n    props.storeScroll = {\n        scrollValue: 0,\n        set: (value) => {\n            props.storeScroll.scrollValue = value;\n        },\n        get: () => {\n            return props.storeScroll.scrollValue;\n        },\n    };\n\n    /**\n     * @param {import(\"@web/core/utils/hooks\").Ref} ref\n     */\n    function add(ref, onSelect, { show = false } = {}) {\n        const toggler = () => toggle(ref, onSelect);\n        targets.push([ref, toggler]);\n        if (!ref.el) {\n            return;\n        }\n        ref.el.addEventListener(\"click\", toggler);\n        ref.el.addEventListener(\"mouseenter\", loadEmoji);\n        if (show) {\n            ref.el.click();\n        }\n    }\n\n    function toggle(ref, onSelect = props.onSelect) {\n        if (popover.isOpen) {\n            popover.close();\n        } else {\n            state.isOpen = true;\n            popover.open(ref.el, { ...props, onSelect });\n        }\n    }\n\n    if (ref) {\n        add(ref);\n    }\n    onMounted(() => {\n        for (const [ref, toggle] of targets) {\n            if (!ref.el) {\n                continue;\n            }\n            ref.el.addEventListener(\"click\", toggle);\n            ref.el.addEventListener(\"mouseenter\", loadEmoji);\n        }\n    });\n    onWillPatch(() => {\n        for (const [ref, toggle] of targets) {\n            if (!ref.el) {\n                continue;\n            }\n            ref.el.removeEventListener(\"click\", toggle);\n            ref.el.removeEventListener(\"mouseenter\", loadEmoji);\n        }\n    });\n    onPatched(() => {\n        for (const [ref, toggle] of targets) {\n            if (!ref.el) {\n                continue;\n            }\n            ref.el.addEventListener(\"click\", toggle);\n            ref.el.addEventListener(\"mouseenter\", loadEmoji);\n        }\n    });\n    Object.assign(state, { add });\n    return state;\n}\n\nconst loadingListeners = [];\n\nexport const loader = {\n    loadEmoji: () => loadBundle(\"web.assets_emoji\"),\n    /** @type {{ emojiValueToShortcode: Object<string, string> }} */\n    loaded: undefined,\n    onEmojiLoaded(cb) {\n        loadingListeners.push(cb);\n    },\n};\n\n/**\n * @returns {import(\"@web/core/emoji_picker/emoji_data\")}\n */\nexport async function loadEmoji() {\n    const res = { categories: [], emojis: [] };\n    try {\n        await loader.loadEmoji();\n        const { getCategories, getEmojis } = odoo.loader.modules.get(\n            \"@web/core/emoji_picker/emoji_data\"\n        );\n        res.categories = getCategories();\n        res.emojis = getEmojis();\n        return res;\n    } catch {\n        // Could be intentional (tour ended successfully while emoji still loading)\n        return res;\n    } finally {\n        if (!loader.loaded) {\n            loader.loaded = { emojiValueToShortcode: {} };\n            for (const emoji of res.emojis) {\n                const value = emoji.codepoints;\n                const shortcode = emoji.shortcodes[0];\n                loader.loaded.emojiValueToShortcode[value] = shortcode;\n                for (const listener of loadingListeners) {\n                    listener();\n                }\n                loadingListeners.length = 0;\n            }\n        }\n    }\n}\n\nexport const EMOJI_PICKER_PROPS = [\"close?\", \"onClose?\", \"onSelect\", \"state?\", \"storeScroll?\"];\n\nexport class EmojiPicker extends Component {\n    static props = EMOJI_PICKER_PROPS;\n    static template = \"web.EmojiPicker\";\n\n    categories = null;\n    emojis = null;\n    shouldScrollElem = null;\n    lastSearchTerm;\n    keyboardNavigated = false;\n\n    setup() {\n        this.gridRef = useRef(\"emoji-grid\");\n        this.navbarRef = useRef(\"navbar\");\n        this.ui = useState(useService(\"ui\"));\n        this.isMobileOS = isMobileOS();\n        this.state = useState({\n            activeEmojiIndex: 0,\n            categoryId: null,\n            recent: JSON.parse(browser.localStorage.getItem(\"web.emoji.frequent\") || \"{}\"),\n            searchTerm: \"\",\n        });\n        const onStorage = (ev) => {\n            if (ev.key === \"web.emoji.frequent\") {\n                this.state.recent = ev.newValue ? JSON.parse(ev.newValue) : {};\n            } else if (ev.key === null) {\n                this.state.recent = {};\n            }\n        };\n        browser.addEventListener(\"storage\", onStorage);\n        onWillDestroy(() => {\n            browser.removeEventListener(\"storage\", onStorage);\n        });\n        useAutofocus();\n        onWillStart(async () => {\n            const { categories, emojis } = await loadEmoji();\n            this.categories = categories;\n            this.emojis = emojis;\n            this.emojiByCodepoints = Object.fromEntries(\n                this.emojis.map((emoji) => [emoji.codepoints, emoji])\n            );\n            this.recentCategory = {\n                name: \"Frequently used\",\n                displayName: _t(\"Frequently used\"),\n                title: \"\ud83d\udd53\",\n                sortId: 0,\n            };\n            this.state.categoryId = this.recentEmojis.length\n                ? this.recentCategory.sortId\n                : this.categories[0].sortId;\n        });\n        onMounted(() => {\n            this.navbarResizeObserver = new ResizeObserver(() => this.adaptNavbar());\n            this.navbarResizeObserver.observe(this.navbarRef.el);\n            this.adaptNavbar();\n            if (this.emojis.length === 0) {\n                return;\n            }\n            this.highlightActiveCategory();\n            if (this.props.storeScroll) {\n                this.gridRef.el.scrollTop = this.props.storeScroll.get();\n            }\n        });\n        onPatched(() => {\n            if (this.emojis.length === 0) {\n                return;\n            }\n            if (this.shouldScrollElem) {\n                this.shouldScrollElem = false;\n                const getElement = () =>\n                    this.gridRef.el.querySelector(\n                        `.o-EmojiPicker-category[data-category=\"${this.state.categoryId}\"`\n                    );\n                const elem = getElement();\n                if (elem) {\n                    elem.scrollIntoView();\n                } else {\n                    this.shouldScrollElem = getElement;\n                }\n            }\n        });\n        useEffect(\n            () => this.updateEmojiPickerRepr(),\n            () => [this.state.categoryId, this.state.searchTerm]\n        );\n        useEffect(\n            (el) => {\n                const gridEl = this.gridRef?.el;\n                const activeEl = gridEl?.querySelector(\".o-Emoji.o-active\");\n                if (\n                    gridEl &&\n                    activeEl &&\n                    this.keyboardNavigated &&\n                    !isElementVisible(activeEl, gridEl)\n                ) {\n                    activeEl.scrollIntoView({ block: \"center\", behavior: \"instant\" });\n                    this.keyboardNavigated = false;\n                }\n            },\n            () => [this.state.activeEmojiIndex, this.gridRef?.el]\n        );\n        useEffect(\n            () => {\n                if (this.searchTerm) {\n                    this.gridRef.el.scrollTop = 0;\n                    this.state.categoryId = null;\n                } else {\n                    if (this.lastSearchTerm) {\n                        this.gridRef.el.scrollTop = 0;\n                    }\n                    this.highlightActiveCategory();\n                }\n                this.lastSearchTerm = this.searchTerm;\n            },\n            () => [this.searchTerm]\n        );\n        onWillUnmount(() => {\n            this.navbarResizeObserver.disconnect();\n            if (!this.gridRef.el) {\n                return;\n            }\n            if (this.props.storeScroll) {\n                this.props.storeScroll.set(this.gridRef.el.scrollTop);\n            }\n        });\n    }\n\n    adaptNavbar() {\n        const computedStyle = getComputedStyle(this.navbarRef.el);\n        const availableWidth =\n            this.navbarRef.el.getBoundingClientRect().width -\n            parseInt(computedStyle.paddingLeft) -\n            parseInt(computedStyle.marginLeft) -\n            parseInt(computedStyle.paddingLeft) -\n            parseInt(computedStyle.marginLeft);\n        const itemWidth = this.navbarRef.el.querySelector(\".o-Emoji\").getBoundingClientRect().width;\n        const gapWidth = parseInt(computedStyle.gap);\n        const maxAvailableNavbarItemAmountAtOnce = Math.floor(\n            availableWidth / (itemWidth + gapWidth)\n        );\n        const repr = [];\n        let panel = [];\n        const allCategories = this.getAllCategories();\n        for (const category of allCategories) {\n            if (\n                panel.length === maxAvailableNavbarItemAmountAtOnce - 1 &&\n                category !== allCategories.at(-1)\n            ) {\n                panel.push(\"next\");\n                repr.push(panel);\n                panel = [];\n                panel.push(\"previous\");\n            }\n            panel.push(category.sortId);\n        }\n        if (panel.length > 0) {\n            if (repr.length > 0) {\n                panel.push(\n                    ...[...Array(maxAvailableNavbarItemAmountAtOnce - panel.length)].map(\n                        (_, idx) => \"empty_\" + idx\n                    )\n                );\n            }\n            repr.push(panel);\n        }\n        this.state.emojiNavbarRepr = repr;\n    }\n\n    get currentNavbarPanel() {\n        if (!this.state.emojiNavbarRepr) {\n            return this.getAllCategories().map((c) => c.sortId);\n        }\n        if (this.state.categoryId === null || Number.isNaN(this.state.categoryId)) {\n            return this.state.emojiNavbarRepr[0];\n        }\n        return this.state.emojiNavbarRepr.find((panel) => panel.includes(this.state.categoryId));\n    }\n\n    get searchTerm() {\n        return this.props.state ? this.props.state.searchTerm : this.state.searchTerm;\n    }\n\n    set searchTerm(value) {\n        if (this.props.state) {\n            this.props.state.searchTerm = value;\n        } else {\n            this.state.searchTerm = value;\n        }\n    }\n\n    get itemsNumber() {\n        return this.recentEmojis.length + this.getEmojis().length;\n    }\n\n    get recentEmojis() {\n        const recent = Object.entries(this.state.recent)\n            .sort(([, usage_1], [, usage_2]) => usage_2 - usage_1)\n            .map(([codepoints]) => this.emojiByCodepoints[codepoints]);\n        if (this.searchTerm && recent.length > 0) {\n            return fuzzyLookup(this.searchTerm, recent, (emoji) => [\n                emoji.name,\n                ...emoji.keywords,\n                ...emoji.emoticons,\n                ...emoji.shortcodes,\n            ]);\n        }\n        return recent.slice(0, 42);\n    }\n\n    onClick(ev) {\n        markEventHandled(ev, \"emoji.selectEmoji\");\n    }\n\n    onClickToNextCategories() {\n        const panelIndex = this.state.emojiNavbarRepr.findIndex((p) =>\n            p.includes(this.state.categoryId)\n        );\n        this.selectCategory(this.state.emojiNavbarRepr[panelIndex + 1][1]);\n    }\n\n    onClickToPreviousCategories() {\n        const panelIndex = this.state.emojiNavbarRepr.findIndex((p) =>\n            p.includes(this.state.categoryId)\n        );\n        this.selectCategory(this.state.emojiNavbarRepr[panelIndex - 1].at(-2));\n    }\n\n    /**\n     * Builds the representation of the emoji picker (a 2D matrix of emojis)\n     * from the current DOM state. This is necessary to handle keyboard\n     * navigation of the emoji picker.\n     */\n    updateEmojiPickerRepr() {\n        const emojiEls = Array.from(this.gridRef.el.querySelectorAll(\".o-Emoji\"));\n        const emojiRects = emojiEls.map((el) => el.getBoundingClientRect());\n        this.emojiMatrix = [];\n        for (const [index, pos] of emojiRects.entries()) {\n            const emojiIndex = emojiEls[index].dataset.index;\n            if (this.emojiMatrix.length === 0 || pos.top > emojiRects[index - 1].top) {\n                this.emojiMatrix.push([]);\n            }\n            this.emojiMatrix.at(-1).push(parseInt(emojiIndex));\n        }\n    }\n\n    handleNavigation(key) {\n        const currentIdx = this.state.activeEmojiIndex;\n        let currentRow = -1;\n        let currentCol = -1;\n        const rowIdx = this.emojiMatrix.findIndex((row) => row.includes(currentIdx));\n        if (rowIdx !== -1) {\n            currentRow = rowIdx;\n            currentCol = this.emojiMatrix[currentRow].indexOf(currentIdx);\n        }\n        let newIdx;\n        switch (key) {\n            case \"ArrowDown\": {\n                const rowBelow = this.emojiMatrix[currentRow + 1];\n                const rowBelowBelow = this.emojiMatrix[currentRow + 2];\n                if (rowBelow?.length <= currentCol && rowBelowBelow?.length >= currentCol) {\n                    newIdx = rowBelowBelow?.[currentCol];\n                } else {\n                    newIdx = rowBelow?.[Math.min(currentCol, rowBelow.length - 1)];\n                }\n                break;\n            }\n            case \"ArrowUp\": {\n                const rowAbove = this.emojiMatrix[currentRow - 1];\n                const rowAboveAbove = this.emojiMatrix[currentRow - 2];\n                if (rowAbove?.length <= currentCol && rowAboveAbove?.length >= currentCol) {\n                    newIdx = rowAboveAbove?.[currentCol];\n                } else {\n                    newIdx = rowAbove?.[Math.min(currentCol, rowAbove.length - 1)];\n                }\n                break;\n            }\n            case \"ArrowRight\": {\n                const colRight = currentCol + 1;\n                if (colRight === this.emojiMatrix[currentRow].length) {\n                    const rowBelowRight = this.emojiMatrix[currentRow + 1];\n                    newIdx = rowBelowRight?.[0];\n                } else {\n                    newIdx = this.emojiMatrix[currentRow][colRight];\n                }\n                break;\n            }\n            case \"ArrowLeft\": {\n                const colLeft = currentCol - 1;\n                if (colLeft < 0) {\n                    const rowAboveLeft = this.emojiMatrix[currentRow - 1];\n                    newIdx = rowAboveLeft?.[rowAboveLeft.length - 1] ?? this.state.activeEmojiIndex;\n                } else {\n                    newIdx = this.emojiMatrix[currentRow][colLeft];\n                }\n                break;\n            }\n        }\n        this.state.activeEmojiIndex = newIdx ?? this.state.activeEmojiIndex;\n    }\n\n    onKeydown(ev) {\n        switch (ev.key) {\n            case \"ArrowDown\":\n            case \"ArrowUp\":\n            case \"ArrowRight\":\n            case \"ArrowLeft\":\n                this.handleNavigation(ev.key);\n                this.keyboardNavigated = true;\n                break;\n            case \"Enter\":\n                ev.preventDefault();\n                this.gridRef.el\n                    .querySelector(\n                        `.o-EmojiPicker-content .o-Emoji[data-index=\"${this.state.activeEmojiIndex}\"]`\n                    )\n                    ?.click();\n                break;\n            case \"Escape\":\n                this.props.close?.();\n                this.props.onClose?.();\n                ev.stopPropagation();\n        }\n    }\n\n    getAllCategories() {\n        const res = [...this.categories];\n        if (this.recentEmojis.length > 0) {\n            res.unshift(this.recentCategory);\n        }\n        return res;\n    }\n\n    getEmojis() {\n        let emojisToDisplay = [...this.emojis];\n        const recentEmojis = this.recentEmojis;\n        if (recentEmojis.length > 0 && this.searchTerm) {\n            emojisToDisplay = emojisToDisplay.filter((emoji) => !recentEmojis.includes(emoji));\n        }\n        if (this.searchTerm.length > 1) {\n            return fuzzyLookup(this.searchTerm, emojisToDisplay, (emoji) => [\n                emoji.name,\n                ...emoji.keywords,\n                ...emoji.emoticons,\n                ...emoji.shortcodes,\n            ]);\n        }\n        return emojisToDisplay;\n    }\n\n    getEmojisFromSearch() {\n        return [...this.recentEmojis, ...this.getEmojis()];\n    }\n\n    selectCategory(categoryId) {\n        this.searchTerm = \"\";\n        this.state.categoryId = categoryId;\n        this.shouldScrollElem = true;\n    }\n\n    selectEmoji(ev) {\n        const codepoints = ev.currentTarget.dataset.codepoints;\n        const resetOnSelect = !ev.shiftKey && !this.ui.isSmall;\n        this.props.onSelect(codepoints, resetOnSelect);\n        this.state.recent[codepoints] ??= 0;\n        this.state.recent[codepoints]++;\n        browser.localStorage.setItem(\"web.emoji.frequent\", JSON.stringify(this.state.recent));\n        if (resetOnSelect) {\n            this.gridRef.el.scrollTop = 0;\n            this.props.close?.();\n            this.props.onClose?.();\n        }\n    }\n\n    highlightActiveCategory() {\n        if (!this.gridRef || !this.gridRef.el) {\n            return;\n        }\n        const coords = this.gridRef.el.getBoundingClientRect();\n        const res = document.elementFromPoint(coords.x + 10, coords.y + 10);\n        if (!res) {\n            return;\n        }\n        this.state.categoryId = parseInt(res.dataset.category);\n    }\n}\n\nfunction isElementVisible(el, holder) {\n    const offset = 20;\n    holder = holder || document.body;\n    const { top, bottom, height } = el.getBoundingClientRect();\n    let { top: holderTop, bottom: holderBottom } = holder.getBoundingClientRect();\n    holderTop += offset * 2; // section are position sticky top so emoji can be \"visible\" under section name. Overestimate to assume invisible.\n    holderBottom -= offset;\n    return top - offset <= holderTop ? holderTop - top <= height : bottom - holderBottom <= height;\n}\n", "import { loadBundle } from \"./assets\";\n\nexport async function ensureJQuery() {\n    if (!window.jQuery) {\n        await loadBundle(\"web._assets_jquery\");\n        // allow to instantiate Bootstrap classes via jQuery: e.g. $(...).dropdown\n        const BTS_CLASSES = [\"Carousel\", \"Dropdown\", \"Modal\", \"Popover\", \"Tooltip\", \"Collapse\"];\n        const $ = window.jQuery;\n        for (const CLS of BTS_CLASSES) {\n            const plugin = window[CLS];\n            if (plugin) {\n                const name = plugin.NAME;\n                const JQUERY_NO_CONFLICT = $.fn[name];\n                $.fn[name] = plugin.jQueryInterface;\n                $.fn[name].Constructor = plugin;\n                $.fn[name].noConflict = () => {\n                    $.fn[name] = JQUERY_NO_CONFLICT;\n                    return plugin.jQueryInterface;\n                };\n            }\n        }\n    }\n}\n", "import { browser } from \"../browser/browser\";\nimport { Dialog } from \"../dialog/dialog\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"../registry\";\nimport { Tooltip } from \"@web/core/tooltip/tooltip\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { capitalize } from \"../utils/strings\";\n\nimport { Component, useRef, useState, markup } from \"@odoo/owl\";\n\nconst { DateTime } = luxon;\n\n// This props are added by the error handler\nexport const standardErrorDialogProps = {\n    traceback: { type: [String, { value: null }], optional: true },\n    message: { type: String, optional: true },\n    name: { type: String, optional: true },\n    exceptionName: { type: [String, { value: null }], optional: true },\n    data: { type: [Object, { value: null }], optional: true },\n    subType: { type: [String, { value: null }], optional: true },\n    code: { type: [Number, String, { value: null }], optional: true },\n    type: { type: [String, { value: null }], optional: true },\n    serverHost: { type: [String, { value: null }], optional: true },\n    id: { type: [Number, { value: null }], optional: true },\n    model: { type: [String, { value: null }], optional: true },\n    close: Function, // prop added by the Dialog service\n};\n\nexport const odooExceptionTitleMap = new Map(\n    Object.entries({\n        \"odoo.addons.base.models.ir_mail_server.MailDeliveryException\": _t(\"MailDeliveryException\"),\n        \"odoo.exceptions.AccessDenied\": _t(\"Access Denied\"),\n        \"odoo.exceptions.MissingError\": _t(\"Missing Record\"),\n        \"odoo.addons.web.controllers.action.MissingActionError\": _t(\"Missing Action\"),\n        \"odoo.exceptions.UserError\": _t(\"Invalid Operation\"),\n        \"odoo.exceptions.ValidationError\": _t(\"Validation Error\"),\n        \"odoo.exceptions.AccessError\": _t(\"Access Error\"),\n        \"odoo.exceptions.Warning\": _t(\"Warning\"),\n    })\n);\n\n// -----------------------------------------------------------------------------\n// Generic Error Dialog\n// -----------------------------------------------------------------------------\nexport class ErrorDialog extends Component {\n    static template = \"web.ErrorDialog\";\n    static components = { Dialog };\n    static title = _t(\"Odoo Error\");\n    static showTracebackButtonText = _t(\"See technical details\");\n    static hideTracebackButtonText = _t(\"Hide technical details\");\n    static props = { ...standardErrorDialogProps };\n\n    setup() {\n        this.state = useState({\n            showTraceback: false,\n        });\n        this.copyButtonRef = useRef(\"copyButton\");\n        this.popover = usePopover(Tooltip);\n        this.contextDetails = \"Occured \";\n        if (this.props.serverHost) {\n            this.contextDetails += `on ${this.props.serverHost} `;\n        }\n        if (this.props.model && this.props.id) {\n            this.contextDetails += `on model ${this.props.model} and id ${this.props.id} `;\n        }\n        this.contextDetails += `on ${DateTime.now()\n            .setZone(\"UTC\")\n            .toFormat(\"yyyy-MM-dd HH:mm:ss\")} GMT`;\n    }\n\n    showTooltip() {\n        this.popover.open(this.copyButtonRef.el, { tooltip: _t(\"Copied\") });\n        browser.setTimeout(this.popover.close, 800);\n    }\n\n    onClickClipboard() {\n        browser.navigator.clipboard.writeText(\n            `${this.props.name}\\n\\n${this.props.message}\\n\\n${this.contextDetails}\\n\\n${this.props.traceback}`\n        );\n        this.showTooltip();\n    }\n}\n\n// -----------------------------------------------------------------------------\n// Client Error Dialog\n// -----------------------------------------------------------------------------\nexport class ClientErrorDialog extends ErrorDialog {}\nClientErrorDialog.title = _t(\"Odoo Client Error\");\n\n// -----------------------------------------------------------------------------\n// Network Error Dialog\n// -----------------------------------------------------------------------------\nexport class NetworkErrorDialog extends ErrorDialog {}\nNetworkErrorDialog.title = _t(\"Odoo Network Error\");\n\n// -----------------------------------------------------------------------------\n// RPC Error Dialog\n// -----------------------------------------------------------------------------\nexport class RPCErrorDialog extends ErrorDialog {\n    setup() {\n        super.setup();\n        this.inferTitle();\n        this.traceback = this.props.traceback;\n        if (this.props.data && this.props.data.debug) {\n            this.traceback = `${this.props.data.debug}\\nThe above server error caused the following client error:\\n${this.traceback}`;\n        }\n    }\n    inferTitle() {\n        // If the server provides an exception name that we have in a registry.\n        if (this.props.exceptionName && odooExceptionTitleMap.has(this.props.exceptionName)) {\n            this.title = odooExceptionTitleMap.get(this.props.exceptionName).toString();\n            return;\n        }\n        // Fall back to a name based on the error type.\n        if (!this.props.type) {\n            return;\n        }\n        switch (this.props.type) {\n            case \"server\":\n                this.title = _t(\"Odoo Server Error\");\n                break;\n            case \"script\":\n                this.title = _t(\"Odoo Client Error\");\n                break;\n            case \"network\":\n                this.title = _t(\"Odoo Network Error\");\n                break;\n        }\n    }\n\n    onClickClipboard() {\n        browser.navigator.clipboard.writeText(\n            `${this.props.name}\\n\\n${this.props.message}\\n\\n${this.contextDetails}\\n\\n${this.traceback}`\n        );\n        this.showTooltip();\n    }\n}\n\n// -----------------------------------------------------------------------------\n// Warning Dialog\n// -----------------------------------------------------------------------------\nexport class WarningDialog extends Component {\n    static template = \"web.WarningDialog\";\n    static components = { Dialog };\n    static props = {\n        ...standardErrorDialogProps,\n        title: { type: String, optional: true },\n    };\n\n    setup() {\n        this.title = this.inferTitle();\n        const { data, message } = this.props;\n        if (data && data.arguments && data.arguments.length > 0) {\n            this.message = data.arguments[0];\n        } else {\n            this.message = message;\n        }\n    }\n    inferTitle() {\n        if (this.props.exceptionName && odooExceptionTitleMap.has(this.props.exceptionName)) {\n            return odooExceptionTitleMap.get(this.props.exceptionName).toString();\n        }\n        return this.props.title || _t(\"Odoo Warning\");\n    }\n}\n\n// -----------------------------------------------------------------------------\n// Redirect Warning Dialog\n// -----------------------------------------------------------------------------\nexport class RedirectWarningDialog extends Component {\n    static template = \"web.RedirectWarningDialog\";\n    static components = { Dialog };\n    static props = { ...standardErrorDialogProps };\n\n    setup() {\n        this.actionService = useService(\"action\");\n        const { data, subType } = this.props;\n        const [message, actionId, buttonText, additionalContext] = data.arguments;\n        this.title = capitalize(subType) || _t(\"Odoo Warning\");\n        this.message = message;\n        this.actionId = actionId;\n        this.buttonText = buttonText;\n        this.additionalContext = additionalContext;\n    }\n    async onClick() {\n        const options = {};\n        if (this.additionalContext) {\n            options.additionalContext = this.additionalContext;\n        }\n        if (this.actionId.help) {\n            this.actionId.help = markup(this.actionId.help);\n        }\n        await this.actionService.doAction(this.actionId, options);\n        this.props.close();\n    }\n}\n\n// -----------------------------------------------------------------------------\n// Error 504 Dialog\n// -----------------------------------------------------------------------------\nexport class Error504Dialog extends Component {\n    static template = \"web.Error504Dialog\";\n    static components = { Dialog };\n    static props = { ...standardErrorDialogProps };\n}\n\n// -----------------------------------------------------------------------------\n// Expired Session Error Dialog\n// -----------------------------------------------------------------------------\nexport class SessionExpiredDialog extends Component {\n    static template = \"web.SessionExpiredDialog\";\n    static components = { Dialog };\n    static props = { ...standardErrorDialogProps };\n\n    onClick() {\n        browser.location.reload();\n    }\n}\n\nregistry\n    .category(\"error_dialogs\")\n    .add(\"odoo.exceptions.AccessDenied\", WarningDialog)\n    .add(\"odoo.exceptions.AccessError\", WarningDialog)\n    .add(\"odoo.exceptions.MissingError\", WarningDialog)\n    .add(\"odoo.addons.web.controllers.action.MissingActionError\", WarningDialog)\n    .add(\"odoo.exceptions.UserError\", WarningDialog)\n    .add(\"odoo.exceptions.ValidationError\", WarningDialog)\n    .add(\"odoo.exceptions.RedirectWarning\", RedirectWarningDialog)\n    .add(\"odoo.http.SessionExpiredException\", SessionExpiredDialog)\n    .add(\"werkzeug.exceptions.Forbidden\", SessionExpiredDialog)\n    .add(\"504\", Error504Dialog);\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { browser } from \"../browser/browser\";\nimport { ConnectionLostError, RPCError, rpc } from \"../network/rpc\";\nimport { registry } from \"../registry\";\nimport {\n    ClientErrorDialog,\n    ErrorDialog,\n    NetworkErrorDialog,\n    RPCErrorDialog,\n} from \"./error_dialogs\";\nimport { UncaughtClientError, ThirdPartyScriptError, UncaughtPromiseError } from \"./error_service\";\n\n/**\n * @typedef {import(\"../../env\").OdooEnv} OdooEnv\n * @typedef {import(\"./error_service\").UncaughtError} UncaughError\n */\n\nconst errorHandlerRegistry = registry.category(\"error_handlers\");\nconst errorDialogRegistry = registry.category(\"error_dialogs\");\nconst errorNotificationRegistry = registry.category(\"error_notifications\");\n\n// -----------------------------------------------------------------------------\n// RPC errors\n// -----------------------------------------------------------------------------\n\n/**\n * @param {OdooEnv} env\n * @param {UncaughError} error\n * @param {Error} originalError\n * @returns {boolean}\n */\nexport function rpcErrorHandler(env, error, originalError) {\n    if (!(error instanceof UncaughtPromiseError)) {\n        return false;\n    }\n    if (originalError instanceof RPCError) {\n        // When an error comes from the server, it can have an exeption name.\n        // (or any string truly). It is used as key in the error dialog from\n        // server registry to know which dialog component to use.\n        // It's how a backend dev can easily map its error to another component.\n        // Note that for a client side exception, we don't use this registry\n        // as we can directly assign a value to `component`.\n        // error is here a RPCError\n        error.unhandledRejectionEvent.preventDefault();\n        const exceptionName = originalError.exceptionName;\n        let ErrorComponent = originalError.Component;\n        if (!ErrorComponent && exceptionName) {\n            if (errorNotificationRegistry.contains(exceptionName)) {\n                const notif = errorNotificationRegistry.get(exceptionName);\n                env.services.notification.add(notif.message || originalError.data.message, notif);\n                return true;\n            }\n            if (errorDialogRegistry.contains(exceptionName)) {\n                ErrorComponent = errorDialogRegistry.get(exceptionName);\n            }\n        }\n        if (!ErrorComponent && originalError.data.context) {\n            const exceptionClass = originalError.data.context.exception_class;\n            if (errorDialogRegistry.contains(exceptionClass)) {\n                ErrorComponent = errorDialogRegistry.get(exceptionClass);\n            }\n        }\n\n        env.services.dialog.add(ErrorComponent || RPCErrorDialog, {\n            traceback: error.traceback,\n            message: originalError.message,\n            name: originalError.name,\n            exceptionName: originalError.exceptionName,\n            data: originalError.data,\n            subType: originalError.subType,\n            code: originalError.code,\n            type: originalError.type,\n            serverHost: error.event?.target?.location.host,\n            id: originalError.id,\n            model: originalError.model,\n        });\n        return true;\n    }\n}\n\nerrorHandlerRegistry.add(\"rpcErrorHandler\", rpcErrorHandler, { sequence: 97 });\n\n// -----------------------------------------------------------------------------\n// Lost connection errors\n// -----------------------------------------------------------------------------\n\nlet connectionLostNotifRemove = null;\n/**\n * @param {OdooEnv} env\n * @param {UncaughError} error\n * @param {Error} originalError\n * @returns {boolean}\n */\nexport function lostConnectionHandler(env, error, originalError) {\n    if (!(error instanceof UncaughtPromiseError)) {\n        return false;\n    }\n    if (originalError instanceof ConnectionLostError) {\n        if (connectionLostNotifRemove) {\n            // notification already displayed (can occur if there were several\n            // concurrent rpcs when the connection was lost)\n            return true;\n        }\n        connectionLostNotifRemove = env.services.notification.add(\n            _t(\"Connection lost. Trying to reconnect...\"),\n            { sticky: true }\n        );\n        let delay = 2000;\n        browser.setTimeout(function checkConnection() {\n            rpc(\"/web/webclient/version_info\", {})\n                .then(function () {\n                    if (connectionLostNotifRemove) {\n                        connectionLostNotifRemove();\n                        connectionLostNotifRemove = null;\n                    }\n                    env.services.notification.add(_t(\"Connection restored. You are back online.\"), {\n                        type: \"info\",\n                    });\n                })\n                .catch(() => {\n                    // exponential backoff, with some jitter\n                    delay = delay * 1.5 + 500 * Math.random();\n                    browser.setTimeout(checkConnection, delay);\n                });\n        }, delay);\n        return true;\n    }\n}\nerrorHandlerRegistry.add(\"lostConnectionHandler\", lostConnectionHandler, { sequence: 98 });\n\n// -----------------------------------------------------------------------------\n// Default handler\n// -----------------------------------------------------------------------------\n\nconst defaultDialogs = new Map([\n    [UncaughtClientError, ClientErrorDialog],\n    [UncaughtPromiseError, ClientErrorDialog],\n    [ThirdPartyScriptError, NetworkErrorDialog],\n]);\n\n/**\n * Handles the errors based on the very general error categories emitted by the\n * error service. Notice how we do not look at the original error at all.\n *\n * @param {OdooEnv} env\n * @param {UncaughError} error\n * @returns {boolean}\n */\nexport function defaultHandler(env, error) {\n    const DialogComponent = defaultDialogs.get(error.constructor) || ErrorDialog;\n    env.services.dialog.add(DialogComponent, {\n        traceback: error.traceback,\n        message: error.message,\n        name: error.name,\n        serverHost: error.event?.target?.location.host,\n    });\n    return true;\n}\nerrorHandlerRegistry.add(\"defaultHandler\", defaultHandler, { sequence: 100 });\n", "import { browser } from \"../browser/browser\";\nimport { registry } from \"../registry\";\nimport { completeUncaughtError, getErrorTechnicalName } from \"./error_utils\";\nimport { isBrowserFirefox, isBrowserChrome } from \"@web/core/browser/feature_detection\";\n\n/**\n * Uncaught Errors have 4 properties:\n * - name: technical name of the error (UncaughtError, ...)\n * - message: short user visible description of the issue (\"Uncaught Cors Error\")\n * - traceback: long description, possibly technical of the issue (such as a traceback)\n * - originalError: the error that was actually being caught. Note that it is not\n *      necessarily an error (for ex, if some code does throw \"boom\")\n */\nexport class UncaughtError extends Error {\n    constructor(message) {\n        super(message);\n        this.name = getErrorTechnicalName(this);\n        this.traceback = null;\n    }\n}\n\nexport class UncaughtClientError extends UncaughtError {\n    constructor(message = \"Uncaught Javascript Error\") {\n        super(message);\n    }\n}\n\nexport class UncaughtPromiseError extends UncaughtError {\n    constructor(message = \"Uncaught Promise\") {\n        super(message);\n        this.unhandledRejectionEvent = null;\n    }\n}\n\nexport class ThirdPartyScriptError extends UncaughtError {\n    constructor(message = \"Third-Party Script Error\") {\n        super(message);\n    }\n}\n\nexport const errorService = {\n    start(env) {\n        function handleError(uncaughtError, retry = true) {\n            function shouldLogError() {\n                // Only log errors that are relevant business-wise, following the heuristics:\n                // Error.event and Error.traceback have been assigned\n                // in one of the two error event listeners below.\n                // If preventDefault was already executed on the event, don't log it.\n                return (\n                    uncaughtError.event &&\n                    !uncaughtError.event.defaultPrevented &&\n                    uncaughtError.traceback\n                );\n            }\n            let originalError = uncaughtError;\n            while (originalError instanceof Error && \"cause\" in originalError) {\n                originalError = originalError.cause;\n            }\n            for (const [name, handler] of registry.category(\"error_handlers\").getEntries()) {\n                try {\n                    if (handler(env, uncaughtError, originalError)) {\n                        break;\n                    }\n                } catch (e) {\n                    if (shouldLogError()) {\n                        uncaughtError.event.preventDefault();\n                        console.error(\n                            `@web/core/error_service: handler \"${name}\" failed with \"${\n                                e.cause || e\n                            }\" while trying to handle:\\n` + uncaughtError.traceback\n                        );\n                    }\n                    return;\n                }\n            }\n            if (shouldLogError()) {\n                // Log the full traceback instead of letting the browser log the incomplete one\n                uncaughtError.event.preventDefault();\n                console.error(uncaughtError.traceback);\n            }\n        }\n\n        browser.addEventListener(\"error\", async (ev) => {\n            const { colno, error, filename, lineno, message } = ev;\n            const errorsToIgnore = [\n                // Ignore some unnecessary \"ResizeObserver loop limit exceeded\" error in Firefox.\n                \"ResizeObserver loop completed with undelivered notifications.\",\n                // ignore Chrome video internal error: https://crbug.com/809574\n                \"ResizeObserver loop limit exceeded\",\n            ];\n            if (!error && errorsToIgnore.includes(message)) {\n                return;\n            }\n            const isRedactedError = !filename && !lineno && !colno;\n            const isThirdPartyScriptError =\n                isRedactedError ||\n                // Firefox doesn't hide details of errors occuring in third-party scripts, check origin explicitly\n                (isBrowserFirefox() && new URL(filename).origin !== window.location.origin);\n            // Don't display error dialogs for third party script errors unless we are in debug mode\n            if (isThirdPartyScriptError && !odoo.debug) {\n                return;\n            }\n            let uncaughtError;\n            if (isRedactedError) {\n                uncaughtError = new ThirdPartyScriptError();\n                uncaughtError.traceback =\n                    `An error whose details cannot be accessed by the Odoo framework has occurred.\\n` +\n                    `The error probably originates from a JavaScript file served from a different origin.\\n` +\n                    `The full error is available in the browser console.`;\n            } else {\n                uncaughtError = new UncaughtClientError();\n                uncaughtError.event = ev;\n                if (error instanceof Error) {\n                    error.errorEvent = ev;\n                    const annotated = env.debug && env.debug.includes(\"assets\");\n                    await completeUncaughtError(uncaughtError, error, annotated);\n                }\n            }\n            uncaughtError.cause = error;\n            handleError(uncaughtError);\n        });\n\n        browser.addEventListener(\"unhandledrejection\", async (ev) => {\n            const error = ev.reason;\n            let traceback;\n            if (isBrowserChrome() && ev instanceof CustomEvent && error === undefined) {\n                // This fix is ad-hoc to a bug in the Honey Paypal extension\n                // They throw a CustomEvent instead of the specified PromiseRejectionEvent\n                // https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event\n                // Moreover Chrome doesn't seem to sandbox enough the extension, as it seems irrelevant\n                // to have extension's errors in the main business page.\n                // We want to ignore those errors as they are not produced by us, and are parasiting\n                // the navigation. We do this according to the heuristic expressed in the if.\n                if (!odoo.debug) {\n                    return;\n                }\n                traceback =\n                    `Uncaught unknown Error\\n` +\n                    `An unknown error occured. This may be due to a Chrome extension meddling with Odoo.\\n` +\n                    `(Opening your browser console might give you a hint on the error.)`;\n            }\n            const uncaughtError = new UncaughtPromiseError();\n            uncaughtError.unhandledRejectionEvent = ev;\n            uncaughtError.event = ev;\n            uncaughtError.traceback = traceback;\n            if (error instanceof Error) {\n                error.errorEvent = ev;\n                const annotated = env.debug && env.debug.includes(\"assets\");\n                await completeUncaughtError(uncaughtError, error, annotated);\n            }\n            uncaughtError.cause = error;\n            handleError(uncaughtError);\n        });\n    },\n};\n\nregistry.category(\"services\").add(\"error\", errorService, { sequence: 1 });\n", "import { loadJS } from \"../assets\"; // use the real, non patched (in tests), loadJS\n\n/** @typedef {import(\"./error_service\").UncaughtError} UncaughtError */\n\n/**\n * @param {UncaughtError} uncaughtError\n * @param {Error} originalError\n * @returns {string}\n */\nfunction combineErrorNames(uncaughtError, originalError) {\n    const originalErrorName = getErrorTechnicalName(originalError);\n    const uncaughtErrorName = getErrorTechnicalName(uncaughtError);\n    if (originalErrorName === Error.name) {\n        return uncaughtErrorName;\n    } else {\n        return `${uncaughtErrorName} > ${originalErrorName}`;\n    }\n}\n\n/**\n * Returns the full traceback for an error chain based on error causes\n *\n * @param {Error} error\n * @returns {string}\n */\nexport function fullTraceback(error) {\n    let traceback = formatTraceback(error);\n    let current = error.cause;\n    while (current) {\n        traceback += `\\n\\nCaused by: ${\n            current instanceof Error ? formatTraceback(current) : current\n        }`;\n        current = current.cause;\n    }\n    return traceback;\n}\n\n/**\n * Returns the full annotated traceback for an error chain based on error causes\n *\n * @param {Error} error\n * @returns {Promise<string>}\n */\nexport async function fullAnnotatedTraceback(error) {\n    if (error.annotatedTraceback) {\n        return error.annotatedTraceback;\n    }\n    // If we don't call preventDefault  synchronously while handling the error\n    // event, the error will be logged in the console with an unannotated\n    // traceback. This is a problem because annotating a traceback cannot be\n    // done synchronously. To work around this issue, we always call\n    // preventDefault, which means it is never logged but we rethrow the error\n    // after annotating its traceback, which will cause the error to be handled\n    // again after the traceback has been annotated, and this function will be\n    // called again and return synchronously (see above)\n    if (error.errorEvent) {\n        error.errorEvent.preventDefault();\n    }\n    let traceback;\n    try {\n        traceback = await annotateTraceback(error);\n        let current = error.cause;\n        while (current) {\n            traceback += `\\n\\nCaused by: ${\n                current instanceof Error ? await annotateTraceback(current) : current\n            }`;\n            current = current.cause;\n        }\n    } catch (e) {\n        console.warn(\"Failed to annotate traceback for error:\", error, \"failure reason:\", e);\n        traceback = fullTraceback(error);\n    }\n    error.annotatedTraceback = traceback;\n    if (error.errorEvent) {\n        throw error;\n    }\n    return traceback;\n}\n\n/**\n * @param {UncaughtError} uncaughtError\n * @param {Error} originalError\n * @param {boolean} annotated\n * @returns {Promise<void>}\n */\nexport async function completeUncaughtError(uncaughtError, originalError, annotated = false) {\n    uncaughtError.name = combineErrorNames(uncaughtError, originalError);\n    if (annotated) {\n        uncaughtError.traceback = await fullAnnotatedTraceback(originalError);\n    } else {\n        uncaughtError.traceback = fullTraceback(originalError);\n    }\n    if (originalError.message) {\n        uncaughtError.message = `${uncaughtError.message} > ${originalError.message}`;\n    }\n    uncaughtError.cause = originalError;\n}\n\n/**\n * @param {Error} error\n * @returns {string}\n */\nexport function getErrorTechnicalName(error) {\n    return error.name !== Error.name ? error.name : error.constructor.name;\n}\n\n/**\n * Format the traceback of an error. Basically, we just add the error message\n * in the traceback if necessary (Chrome already does it by default, but not\n * other browser.)\n *\n * @param {Error} error\n * @returns {string}\n */\nexport function formatTraceback(error) {\n    let traceback = error.stack;\n    const errorName = getErrorTechnicalName(error);\n    // ensure the proper error name and error message are present in the traceback, no matter the error.stack brower's formatting.\n    // Stack example:\n    // Error: Mock: Can't write value\n    //     _onOpenFormView@http://localhost:8069/web/content/425-baf33f1/web.assets.js:1064:30\n    //     ...\n    const descriptionLine = `${errorName}: ${error.message}`;\n    if (error.stack.split(\"\\n\")[0].trim() !== descriptionLine) {\n        // avoid having the description line twice if already present\n        traceback = `${descriptionLine}\\n${error.stack}`.replace(/\\n/g, \"\\n    \");\n    }\n    return traceback;\n}\n\n/**\n * Returns an annotated traceback from an error. This is asynchronous because\n * it needs to fetch the sourcemaps for each script involved in the error,\n * then compute the correct file/line numbers and add the information to the\n * correct line.\n *\n * @param {Error} error\n * @returns {Promise<string>}\n */\nexport async function annotateTraceback(error) {\n    const traceback = formatTraceback(error);\n    try {\n        await loadJS(\"/web/static/lib/stacktracejs/stacktrace.js\");\n    } catch {\n        return traceback;\n    }\n    // In Firefox, the error stack generated by anonymous code (example: invalid\n    // code in a template) is not compatible with the stacktrace lib. This code\n    // corrects the stack to make it compatible with the lib stacktrace.\n    if (error.stack) {\n        const regex = / line (\\d*) > (Function):(\\d*)/gm;\n        const subst = `:$1`;\n        error.stack = error.stack.replace(regex, subst);\n    }\n    // eslint-disable-next-line no-undef\n    let frames;\n    try {\n        frames = await StackTrace.fromError(error);\n    } catch (e) {\n        // This can crash if the originalError has no stack/stacktrace property\n        console.warn(\"The following error could not be annotated:\", error, e);\n        return traceback;\n    }\n    const lines = traceback.split(\"\\n\");\n    if (lines[lines.length - 1].trim() === \"\") {\n        // firefox traceback have an empty line at the end\n        lines.splice(-1);\n    }\n\n    let lineIndex = 0;\n    let frameIndex = 0;\n    while (frameIndex < frames.length) {\n        const line = lines[lineIndex];\n        // skip lines that have no location information as they don't correspond to a frame\n        if (!line.match(/:\\d+:\\d+\\)?$/)) {\n            lineIndex++;\n            continue;\n        }\n        const frame = frames[frameIndex];\n        const info = ` (${frame.fileName}:${frame.lineNumber})`;\n        lines[lineIndex] = line + info;\n        lineIndex++;\n        frameIndex++;\n    }\n    return lines.join(\"\\n\");\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\nimport { _t, translationIsReady } from \"@web/core/l10n/translation\";\nimport { getOrigin } from \"@web/core/utils/urls\";\n\nconst scssErrorNotificationService = {\n    dependencies: [\"notification\"],\n    start(env, { notification }) {\n        const origin = getOrigin();\n        const assets = [...document.styleSheets].filter((sheet) => {\n            return (\n                sheet.href?.includes(\"/web\") &&\n                sheet.href?.includes(\"/assets/\") &&\n                // CORS security rules don't allow reading content in JS\n                new URL(sheet.href, browser.location.origin).origin === origin\n            );\n        });\n        translationIsReady.then(() => {\n            for (const asset of assets) {\n                let cssRules;\n                try {\n                    // The filter above isn't enough to protect against CORS errors when reading\n                    // the cssRules property. Indeed, it seems that if the protocol is http, reading\n                    // that property can also trigger a CORS error, even if the origin is the same.\n                    // Anyway, we never want this line to crash, so we protect it.\n                    // See opw 3746910.\n                    cssRules = asset.cssRules;\n                } catch {\n                    continue;\n                }\n                const lastRule = cssRules?.[cssRules?.length - 1];\n                if (lastRule?.selectorText === \"css_error_message\") {\n                    const message = _t(\n                        \"The style compilation failed. This is an administrator or developer error that must be fixed for the entire database before continuing working. See browser console or server logs for details.\"\n                    );\n                    notification.add(message, {\n                        title: _t(\"Style error\"),\n                        sticky: true,\n                        type: \"danger\",\n                    });\n                    console.log(\n                        lastRule.style.content\n                            .replaceAll(\"\\\\a\", \"\\n\")\n                            .replaceAll(\"\\\\*\", \"*\")\n                            .replaceAll(`\\\\\"`, `\"`)\n                    );\n                }\n            }\n        });\n    },\n};\nregistry.category(\"services\").add(\"scss_error_display\", scssErrorNotificationService);\n", "import { Component, onWillStart, onWillUpdateProps } from \"@odoo/owl\";\nimport { getExpressionDisplayedOperators } from \"@web/core/expression_editor/expression_editor_operator_editor\";\nimport {\n    condition,\n    expressionFromTree,\n    treeFromExpression,\n} from \"@web/core/tree_editor/condition_tree\";\nimport { TreeEditor } from \"@web/core/tree_editor/tree_editor\";\nimport { getOperatorEditorInfo } from \"@web/core/tree_editor/tree_editor_operator_editor\";\nimport { getDefaultValue } from \"@web/core/tree_editor/tree_editor_value_editors\";\nimport { getDefaultPath } from \"@web/core/tree_editor/utils\";\nimport { ModelFieldSelector } from \"@web/core/model_field_selector/model_field_selector\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport class ExpressionEditor extends Component {\n    static template = \"web.ExpressionEditor\";\n    static components = { TreeEditor };\n    static props = {\n        resModel: String,\n        fields: Object,\n        expression: String,\n        update: Function,\n    };\n\n    setup() {\n        onWillStart(() => this.onPropsUpdated(this.props));\n        onWillUpdateProps((nextProps) => this.onPropsUpdated(nextProps));\n    }\n\n    async onPropsUpdated(props) {\n        this.filteredFields = Object.fromEntries(\n            Object.entries(props.fields).filter(([_, fieldDef]) => fieldDef.type !== \"properties\")\n        );\n        try {\n            this.tree = treeFromExpression(props.expression, {\n                getFieldDef: (name) => this.getFieldDef(name, props),\n                distributeNot: !this.isDebugMode,\n            });\n        } catch {\n            this.tree = null;\n        }\n    }\n\n    getFieldDef(name, props = this.props) {\n        if (typeof name === \"string\") {\n            return props.fields[name] || null;\n        }\n        return null;\n    }\n\n    getDefaultCondition() {\n        const defaultPath = getDefaultPath(this.filteredFields);\n        const fieldDef = this.filteredFields[defaultPath];\n        const operator = getExpressionDisplayedOperators(fieldDef)[0];\n        const value = getDefaultValue(fieldDef, operator);\n        return condition(fieldDef.name, operator, value);\n    }\n\n    getDefaultOperator(fieldDef) {\n        return getExpressionDisplayedOperators(fieldDef)[0];\n    }\n\n    getOperatorEditorInfo(fieldDef) {\n        const operators = getExpressionDisplayedOperators(fieldDef);\n        return getOperatorEditorInfo(operators, fieldDef);\n    }\n\n    getPathEditorInfo(resModel, defaultCondition) {\n        if (resModel !== this.props.resModel) {\n            throw new Error(\n                `Expression editor doesn't support tree as value so resModel has to be props.resModel`\n            );\n        }\n        return {\n            component: ModelFieldSelector,\n            extractProps: ({ value, update }) => ({\n                path: value,\n                update,\n                resModel: this.props.resModel,\n                readonly: false,\n                filter: (fieldDef) => fieldDef.name in this.filteredFields,\n                showDebugInput: false,\n                followRelations: false,\n                isDebugMode: this.isDebugMode,\n            }),\n            isSupported: (value) => [0, 1].includes(value) || value in this.filteredFields,\n            // by construction, all values received by the path editor are O/1 or a field (name) in this.props.fields.\n            // (see _leafFromAST in condition_tree.js)\n            stringify: (value) => this.props.fields[value].string,\n            defaultValue: () => defaultCondition.path,\n            message: _t(\"Field properties not supported\"),\n        };\n    }\n\n    get isDebugMode() {\n        return !!this.env.debug;\n    }\n\n    onExpressionChange(expression) {\n        this.props.update(expression);\n    }\n\n    resetExpression() {\n        this.props.update(\"True\");\n    }\n\n    update(tree) {\n        const expression = expressionFromTree(tree, {\n            getFieldDef: (name) => this.getFieldDef(name),\n        });\n        this.props.update(expression);\n    }\n}\n", "import { getDomainDisplayedOperators } from \"@web/core/domain_selector/domain_selector_operator_editor\";\n\nconst EXPRESSION_VALID_OPERATORS = [\n    \"<\",\n    \"<=\",\n    \">\",\n    \">=\",\n    \"between\",\n    \"within\",\n    \"in\",\n    \"not in\",\n    \"=\",\n    \"!=\",\n    \"set\",\n    \"not_set\",\n    \"starts_with\",\n    \"ends_with\",\n    \"is\",\n    \"is_not\",\n];\n\nexport function getExpressionDisplayedOperators(fieldDef) {\n    const operators = getDomainDisplayedOperators(fieldDef);\n    return operators.filter((operator) => EXPRESSION_VALID_OPERATORS.includes(operator));\n}\n", "import { Component, useRef, useState } from \"@odoo/owl\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { ExpressionEditor } from \"@web/core/expression_editor/expression_editor\";\nimport { evaluateExpr } from \"@web/core/py_js/py\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { user } from \"@web/core/user\";\n\nexport class ExpressionEditorDialog extends Component {\n    static components = { Dialog, ExpressionEditor };\n    static template = \"web.ExpressionEditorDialog\";\n    static props = {\n        close: Function,\n        resModel: String,\n        fields: Object,\n        expression: String,\n        onConfirm: Function,\n    };\n\n    setup() {\n        this.notification = useService(\"notification\");\n        this.state = useState({\n            expression: this.props.expression,\n        });\n        this.confirmButtonRef = useRef(\"confirm\");\n    }\n\n    get expressionEditorProps() {\n        return {\n            resModel: this.props.resModel,\n            fields: this.props.fields,\n            expression: this.state.expression,\n            update: (expression) => {\n                this.state.expression = expression;\n            },\n        };\n    }\n\n    makeDefaultRecord() {\n        const record = {};\n        for (const [name, { type }] of Object.entries(this.props.fields)) {\n            switch (type) {\n                case \"integer\":\n                case \"float\":\n                case \"monetary\":\n                    record[name] = name === \"id\" ? false : 0;\n                    break;\n                case \"one2many\":\n                case \"many2many\":\n                    record[name] = [];\n                    break;\n                default:\n                    record[name] = false;\n            }\n        }\n        return record;\n    }\n\n    async onConfirm() {\n        this.confirmButtonRef.el.disabled = true;\n        const record = this.makeDefaultRecord();\n        const evalContext = { ...user.context, ...record };\n        try {\n            evaluateExpr(this.state.expression, evalContext);\n        } catch {\n            if (this.confirmButtonRef.el) {\n                this.confirmButtonRef.el.disabled = false;\n            }\n            this.notification.add(_t(\"Expression is invalid. Please correct it\"), {\n                type: \"danger\",\n            });\n            return;\n        }\n        this.props.onConfirm(this.state.expression);\n        this.props.close();\n    }\n\n    onDiscard() {\n        this.props.close();\n    }\n}\n", "import { Cache } from \"@web/core/utils/cache\";\nimport { Domain } from \"@web/core/domain\";\nimport { registry } from \"@web/core/registry\";\n\n/**\n * @typedef {Object} LoadFieldsOptions\n * @property {string[]|false} [fieldNames]\n * @property {string[]} [attributes]\n */\n\nexport const fieldService = {\n    dependencies: [\"orm\"],\n    async: [\"loadFields\", \"loadPath\", \"loadPropertyDefinitions\"],\n    start(env, { orm }) {\n        const cache = new Cache(\n            (resModel, options) => {\n                return orm\n                    .call(resModel, \"fields_get\", [options.fieldNames, options.attributes])\n                    .catch((error) => {\n                        cache.clear(resModel, options);\n                        return Promise.reject(error);\n                    });\n            },\n            (resModel, options) =>\n                JSON.stringify([resModel, options.fieldNames, options.attributes])\n        );\n\n        env.bus.addEventListener(\"CLEAR-CACHES\", () => cache.invalidate());\n\n        /**\n         * @param {string} resModel\n         * @param {LoadFieldsOptions} [options]\n         * @returns {Promise<object>}\n         */\n        async function loadFields(resModel, options = {}) {\n            if (typeof resModel !== \"string\" || !resModel) {\n                throw new Error(`Invalid model name: ${resModel}`);\n            }\n            return cache.read(resModel, options);\n        }\n\n        /**\n         * @param {Object} fieldDefs\n         * @param {string} name\n         * @param {import(\"@web/core/domain\").DomainListRepr} [domain=[]]\n         * @returns {Promise<Object>}\n         */\n        async function _loadPropertyDefinitions(fieldDefs, name, domain = []) {\n            const {\n                definition_record: definitionRecord,\n                definition_record_field: definitionRecordField,\n            } = fieldDefs[name];\n            const definitionRecordModel = fieldDefs[definitionRecord].relation;\n\n            domain = Domain.and([[[definitionRecordField, \"!=\", false]], domain]).toList();\n\n            const result = await orm.webSearchRead(definitionRecordModel, domain, {\n                specification: {\n                    display_name: {},\n                    [definitionRecordField]: {},\n                },\n            });\n\n            const definitions = {};\n            for (const record of result.records) {\n                for (const definition of record[definitionRecordField]) {\n                    definitions[definition.name] = {\n                        is_property: true,\n                        // for now, all properties are searchable but their definitions don't contain that info\n                        searchable: true,\n                        // differentiate definitions with same name but on different parent\n                        record_id: record.id,\n                        record_name: record.display_name,\n                        ...definition,\n                    };\n                }\n            }\n            return definitions;\n        }\n\n        /**\n         * @param {string} resModel\n         * @param {string} fieldName\n         * @param {import(\"@web/core/domain\").DomainListRepr} [domain]\n         * @returns {Promise<object[]>}\n         */\n        async function loadPropertyDefinitions(resModel, fieldName, domain) {\n            const fieldDefs = await loadFields(resModel);\n            return _loadPropertyDefinitions(fieldDefs, fieldName, domain);\n        }\n\n        /**\n         * @param {string|null} resModel valid model name or null (case virtual)\n         * @param {Object|null} fieldDefs\n         * @param {string[]} names\n         */\n        async function _loadPath(resModel, fieldDefs, names) {\n            if (!fieldDefs) {\n                return { isInvalid: \"path\", names, modelsInfo: [] };\n            }\n\n            const [name, ...remainingNames] = names;\n            const modelsInfo = [{ resModel, fieldDefs }];\n            if (resModel === \"*\" && remainingNames.length) {\n                return { isInvalid: \"path\", names, modelsInfo };\n            }\n\n            const fieldDef = fieldDefs[name];\n            if ((name !== \"*\" && !fieldDef) || (name === \"*\" && remainingNames.length)) {\n                return { isInvalid: \"path\", names, modelsInfo };\n            }\n\n            if (!remainingNames.length) {\n                return { names, modelsInfo };\n            }\n\n            let subResult;\n            if (fieldDef.relation) {\n                subResult = await _loadPath(\n                    fieldDef.relation,\n                    await loadFields(fieldDef.relation),\n                    remainingNames\n                );\n            } else if (fieldDef.type === \"properties\") {\n                subResult = await _loadPath(\n                    \"*\",\n                    await _loadPropertyDefinitions(fieldDefs, name),\n                    remainingNames\n                );\n            }\n\n            if (subResult) {\n                const result = {\n                    names,\n                    modelsInfo: [...modelsInfo, ...subResult.modelsInfo],\n                };\n                if (subResult.isInvalid) {\n                    result.isInvalid = \"path\";\n                }\n                return result;\n            }\n\n            return { isInvalid: \"path\", names, modelsInfo };\n        }\n\n        /**\n         * Note: the symbol * can be used at the end of path (e.g path=\"*\" or path=\"user_id.*\").\n         * It says to load the fields of the appropriate model.\n         * @param {string} resModel\n         * @param {string} path\n         * @returns {Promise<Object>}\n         */\n        async function loadPath(resModel, path = \"*\") {\n            const fieldDefs = await loadFields(resModel);\n            if (typeof path !== \"string\" || !path) {\n                throw new Error(`Invalid path: ${path}`);\n            }\n            return _loadPath(resModel, fieldDefs, path.split(\".\"));\n        }\n\n        return { loadFields, loadPath, loadPropertyDefinitions };\n    },\n};\n\nregistry.category(\"services\").add(\"field\", fieldService);\n", "import { Component, onMounted, useRef, useState } from \"@odoo/owl\";\nimport { useFileUploader } from \"@web/core/utils/files\";\n\n/**\n * Custom file input\n *\n * Component representing a customized input of type file. It takes a sub-template\n * in its default t-slot and uses it as the trigger to open the file upload\n * prompt.\n * @extends Component\n *\n * Props:\n * @param {string} [props.acceptedFileExtensions='*'] Comma-separated\n *      list of authorized file extensions (default to all).\n * @param {string} [props.route='/web/binary/upload'] Route called when\n *      a file is uploaded in the input.\n * @param {string} [props.resId]\n * @param {string} [props.resModel]\n * @param {string} [props.multiUpload=false] Whether the input should allow\n *      to upload multiple files at once.\n */\nexport class FileInput extends Component {\n    static template = \"web.FileInput\";\n    static defaultProps = {\n        acceptedFileExtensions: \"*\",\n        hidden: false,\n        multiUpload: false,\n        onUpload: () => {},\n        route: \"/web/binary/upload_attachment\",\n        beforeOpen: async () => true,\n    };\n    static props = {\n        acceptedFileExtensions: { type: String, optional: true },\n        autoOpen: { type: Boolean, optional: true },\n        hidden: { type: Boolean, optional: true },\n        multiUpload: { type: Boolean, optional: true },\n        onUpload: { type: Function, optional: true },\n        beforeOpen: { type: Function, optional: true },\n        resId: { type: Number, optional: true },\n        resModel: { type: String, optional: true },\n        route: { type: String, optional: true },\n        \"*\": true,\n    };\n\n    setup() {\n        this.uploadFiles = useFileUploader();\n        this.fileInputRef = useRef(\"file-input\");\n        this.state = useState({\n            // Disables upload button if currently uploading.\n            isDisable: false,\n        });\n\n        onMounted(() => {\n            if (this.props.autoOpen) {\n                this.onTriggerClicked();\n            }\n        });\n    }\n\n    get httpParams() {\n        const { resId, resModel } = this.props;\n        const params = {\n            csrf_token: odoo.csrf_token,\n            ufile: [...this.fileInputRef.el.files],\n        };\n        if (resModel) {\n            params.model = resModel;\n        }\n        if (resId !== undefined) {\n            params.id = resId;\n        }\n        return params;\n    }\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * Upload an attachment to the given route with the given parameters:\n     * - ufile: list of files contained in the file input\n     * - csrf_token: CSRF token provided by the odoo global object\n     * - resModel: a specific model which will be given when creating the attachment\n     * - resId: the id of the resModel target instance\n     */\n    async onFileInputChange() {\n        this.state.isDisable = true;\n        const parsedFileData = await this.uploadFiles(this.props.route, this.httpParams);\n        if (parsedFileData) {\n            // When calling onUpload, also pass the files to allow to get data like their names\n            this.props.onUpload(\n                parsedFileData,\n                this.fileInputRef.el ? this.fileInputRef.el.files : []\n            );\n            // Because the input would not trigger this method if the same file name is uploaded,\n            // we must clear the value after handling the upload\n            this.fileInputRef.el.value = null;\n        }\n        this.state.isDisable = false;\n    }\n\n    /**\n     * Redirect clicks from the trigger element to the input.\n     */\n    async onTriggerClicked() {\n        if (await this.props.beforeOpen()) {\n            this.fileInputRef.el.click();\n        }\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { useService } from \"../utils/hooks\";\nimport { ConfirmationDialog } from \"../confirmation_dialog/confirmation_dialog\";\n\nimport { Component } from \"@odoo/owl\";\n\nexport class FileUploadProgressBar extends Component {\n    static template = \"web.FileUploadProgressBar\";\n    static props = {\n        fileUpload: { type: Object },\n    };\n\n    setup() {\n        this.dialogService = useService(\"dialog\");\n    }\n\n    onCancel() {\n        if (!this.props.fileUpload.xhr) {\n            return;\n        }\n        this.dialogService.add(ConfirmationDialog, {\n            body: _t(\"Do you really want to cancel the upload of %s?\", this.props.fileUpload.title),\n            confirm: () => {\n                this.props.fileUpload.xhr.abort();\n            },\n        });\n    }\n}\n", "import { Component } from \"@odoo/owl\";\n\nexport class FileUploadProgressContainer extends Component {\n    static template = \"web.FileUploadProgressContainer\";\n    static props = {\n        Component: { optional: false },\n        shouldDisplay: { type: Function, optional: true },\n        fileUploads: { type: Object },\n    };\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { FileUploadProgressBar } from \"./file_upload_progress_bar\";\n\nimport { Component } from \"@odoo/owl\";\n\nexport class FileUploadProgressRecord extends Component {\n    static template = \"\";\n    static components = {\n        FileUploadProgressBar,\n    };\n    static props = {\n        fileUpload: Object,\n        selector: { type: String, optional: true },\n    };\n    getProgressTexts() {\n        const fileUpload = this.props.fileUpload;\n        const percent = Math.round(fileUpload.progress * 100);\n        if (percent === 100) {\n            return {\n                left: _t(\"Processing...\"),\n                right: \"\",\n            };\n        } else {\n            const mbLoaded = Math.round(fileUpload.loaded / 1000000);\n            const mbTotal = Math.round(fileUpload.total / 1000000);\n            return {\n                left: _t(\"Uploading... (%s%)\", percent),\n                right: _t(\"(%(mbLoaded)s/%(mbTotal)sMB)\", { mbLoaded, mbTotal }),\n            };\n        }\n    }\n}\n\nexport class FileUploadProgressKanbanRecord extends FileUploadProgressRecord {\n    static template = \"web.FileUploadProgressKanbanRecord\";\n}\n\nexport class FileUploadProgressDataRow extends FileUploadProgressRecord {\n    static template = \"web.FileUploadProgressDataRow\";\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"../registry\";\n\nimport { EventBus, reactive } from \"@odoo/owl\";\n\nexport const fileUploadService = {\n    dependencies: [\"notification\"],\n    /**\n     * Overridden during tests to return a mocked XHR.\n     *\n     * @private\n     * @returns {XMLHttpRequest}\n     */\n    createXhr() {\n        return new window.XMLHttpRequest();\n    },\n\n    start(env, { notificationService }) {\n        const uploads = reactive({});\n        let nextId = 1;\n        const bus = new EventBus();\n\n        /**\n         * @param {string}                          route\n         * @param {FileList|Array<File>}            files\n         * @param {Object}                          [params]\n         * @param {function(formData): void}        [params.buildFormData]\n         * @param {Boolean}                         [params.displayErrorNotification]\n         * @returns {reactive}                      upload\n         * @returns {XMLHttpRequest}                upload.xhr\n         * @returns {FormData}                      upload.data\n         * @returns {Number}                        upload.progress\n         * @returns {Number}                        upload.loaded\n         * @returns {Number}                        upload.total\n         * @returns {String}                        upload.title\n         * @returns {String||undefined}             upload.type\n         */\n        const upload = async (route, files, params = {}) => {\n            const xhr = this.createXhr();\n            xhr.open(\"POST\", route);\n            const formData = new FormData();\n            formData.append(\"csrf_token\", odoo.csrf_token);\n            for (const file of files) {\n                formData.append(\"ufile\", file);\n            }\n            if (params.buildFormData) {\n                params.buildFormData(formData);\n            }\n            const upload = reactive({\n                id: nextId++,\n                xhr,\n                data: formData,\n                progress: 0,\n                loaded: 0,\n                total: 0,\n                state: \"pending\",\n                title: files.length === 1 ? files[0].name : _t(\"%s Files\", files.length),\n                type: files.length === 1 ? files[0].type : undefined,\n            });\n            uploads[upload.id] = upload;\n            // Progress listener\n            xhr.upload.addEventListener(\"progress\", async (ev) => {\n                upload.progress = ev.loaded / ev.total;\n                upload.loaded = ev.loaded;\n                upload.total = ev.total;\n                upload.state = \"loading\";\n            });\n            // Load listener\n            xhr.addEventListener(\"load\", () => {\n                delete uploads[upload.id];\n                upload.state = \"loaded\";\n                bus.trigger(\"FILE_UPLOAD_LOADED\", { upload });\n            });\n            // Error listener\n            xhr.addEventListener(\"error\", async () => {\n                delete uploads[upload.id];\n                upload.state = \"error\";\n                // Disable this option if you need more explicit error handling.\n                if (\n                    params.displayErrorNotification !== undefined &&\n                    params.displayErrorNotification\n                ) {\n                    notificationService.add(_t(\"An error occured while uploading.\"), {\n                        title: _t(\"Error\"),\n                        sticky: true,\n                    });\n                }\n                bus.trigger(\"FILE_UPLOAD_ERROR\", { upload });\n            });\n            // Abort listener, considered as error\n            xhr.addEventListener(\"abort\", async () => {\n                delete uploads[upload.id];\n                upload.state = \"abort\";\n                bus.trigger(\"FILE_UPLOAD_ERROR\", { upload });\n            });\n            xhr.send(formData);\n            bus.trigger(\"FILE_UPLOAD_ADDED\", { upload });\n            return upload;\n        };\n\n        return { bus, upload, uploads };\n    },\n};\n\nregistry.category(\"services\").add(\"file_upload\", fileUploadService);\n", "import { url } from \"@web/core/utils/urls\";\n\nexport const FileModelMixin = (T) =>\n    class extends T {\n        access_token;\n        checksum;\n        extension;\n        filename;\n        id;\n        mimetype;\n        name;\n        /** @type {\"binary\"|\"url\"} */\n        type;\n        /** @type {string} */\n        tmpUrl;\n        /**\n         * This URL should not be used as the URL to serve the file. `urlRoute` should be used\n         * instead. The server will properly redirect to the correct URL when necessary.\n         *\n         * @type {string}\n         */\n        url;\n        /** @type {boolean} */\n        uploading;\n\n        get defaultSource() {\n            const route = url(this.urlRoute, this.urlQueryParams);\n            const encodedRoute = encodeURIComponent(route);\n            if (this.isPdf) {\n                return `/web/static/lib/pdfjs/web/viewer.html?file=${encodedRoute}#pagemode=none`;\n            }\n            if (this.isUrlYoutube) {\n                const urlArr = this.url.split(\"/\");\n                let token = urlArr[urlArr.length - 1];\n                if (token.includes(\"watch\")) {\n                    token = token.split(\"v=\")[1];\n                    const amp = token.indexOf(\"&\");\n                    if (amp !== -1) {\n                        token = token.substring(0, amp);\n                    }\n                }\n                return `https://www.youtube.com/embed/${token}`;\n            }\n            return route;\n        }\n\n        get displayName() {\n            return this.name || this.filename;\n        }\n\n        get downloadUrl() {\n            return url(this.urlRoute, { ...this.urlQueryParams, download: true });\n        }\n\n        get isImage() {\n            const imageMimetypes = [\n                \"image/bmp\",\n                \"image/gif\",\n                \"image/jpeg\",\n                \"image/png\",\n                \"image/svg+xml\",\n                \"image/tiff\",\n                \"image/x-icon\",\n                \"image/webp\",\n            ];\n            return imageMimetypes.includes(this.mimetype);\n        }\n\n        get isPdf() {\n            return this.mimetype && this.mimetype.startsWith(\"application/pdf\");\n        }\n\n        get isText() {\n            const textMimeType = [\n                \"application/javascript\",\n                \"application/json\",\n                \"text/css\",\n                \"text/html\",\n                \"text/plain\",\n            ];\n            return textMimeType.includes(this.mimetype);\n        }\n\n        get isUrl() {\n            return this.type === \"url\" && this.url;\n        }\n\n        get isUrlYoutube() {\n            return !!this.url && this.url.includes(\"youtu\");\n        }\n\n        get isVideo() {\n            const videoMimeTypes = [\"audio/mpeg\", \"video/x-matroska\", \"video/mp4\", \"video/webm\"];\n            return videoMimeTypes.includes(this.mimetype);\n        }\n\n        get isViewable() {\n            return (\n                (this.isText || this.isImage || this.isVideo || this.isPdf || this.isUrlYoutube) &&\n                !this.uploading\n            );\n        }\n\n        /**\n         * @returns {Object}\n         */\n        get urlQueryParams() {\n            if (this.uploading && this.tmpUrl) {\n                return {};\n            }\n            const params = {\n                access_token: this.access_token,\n                filename: this.name,\n                unique: this.checksum,\n            };\n            for (const prop in params) {\n                if (!params[prop]) {\n                    delete params[prop];\n                }\n            }\n            return params;\n        }\n\n        /**\n         * @returns {string}\n         */\n        get urlRoute() {\n            if (this.uploading && this.tmpUrl) {\n                return this.tmpUrl;\n            }\n            return this.isImage ? `/web/image/${this.id}` : `/web/content/${this.id}`;\n        }\n    };\n\nexport class FileModel extends FileModelMixin(Object) {}\n", "import { Component, useRef, useState } from \"@odoo/owl\";\nimport { useAutofocus, useService } from \"@web/core/utils/hooks\";\n\n/**\n * @typedef {Object} File\n * @property {string} displayName\n * @property {string} downloadUrl\n * @property {boolean} [isImage]\n * @property {boolean} [isPdf]\n * @property {boolean} [isVideo]\n * @property {boolean} [isText]\n * @property {string} [defaultSource]\n * @property {boolean} [isUrlYoutube]\n * @property {string} [mimetype]\n * @property {boolean} [isViewable]\n * @typedef {Object} Props\n * @property {Array<File>} files\n * @property {number} startIndex\n * @property {function} close\n * @property {boolean} [modal]\n * @extends {Component<Props, Env>}\n */\nexport class FileViewer extends Component {\n    static template = \"web.FileViewer\";\n    static components = {};\n    static props = [\"files\", \"startIndex\", \"close?\", \"modal?\"];\n    static defaultProps = {\n        modal: true,\n    };\n\n    setup() {\n        useAutofocus();\n        this.imageRef = useRef(\"image\");\n        this.zoomerRef = useRef(\"zoomer\");\n\n        this.isDragging = false;\n        this.dragStartX = 0;\n        this.dragStartY = 0;\n\n        this.scrollZoomStep = 0.1;\n        this.zoomStep = 0.5;\n        this.minScale = 0.5;\n        this.translate = {\n            dx: 0,\n            dy: 0,\n            x: 0,\n            y: 0,\n        };\n\n        this.state = useState({\n            index: this.props.startIndex,\n            file: this.props.files[this.props.startIndex],\n            imageLoaded: false,\n            scale: 1,\n            angle: 0,\n        });\n        this.ui = useState(useService(\"ui\"));\n    }\n\n    onImageLoaded() {\n        this.state.imageLoaded = true;\n    }\n\n    close() {\n        this.props.close && this.props.close();\n    }\n\n    next() {\n        const last = this.props.files.length - 1;\n        this.activateFile(this.state.index === last ? 0 : this.state.index + 1);\n    }\n\n    previous() {\n        const last = this.props.files.length - 1;\n        this.activateFile(this.state.index === 0 ? last : this.state.index - 1);\n    }\n\n    activateFile(index) {\n        this.state.index = index;\n        this.state.file = this.props.files[index];\n    }\n\n    onKeydown(ev) {\n        switch (ev.key) {\n            case \"ArrowRight\":\n                this.next();\n                break;\n            case \"ArrowLeft\":\n                this.previous();\n                break;\n            case \"Escape\":\n                this.close();\n                break;\n            case \"q\":\n                this.close();\n                break;\n        }\n        if (this.state.file.isImage) {\n            switch (ev.key) {\n                case \"r\":\n                    this.rotate();\n                    break;\n                case \"+\":\n                    this.zoomIn();\n                    break;\n                case \"-\":\n                    this.zoomOut();\n                    break;\n                case \"0\":\n                    this.resetZoom();\n                    break;\n            }\n        }\n    }\n\n    /**\n     * @param {Event} ev\n     */\n    onWheelImage(ev) {\n        if (ev.deltaY > 0) {\n            this.zoomOut({ scroll: true });\n        } else {\n            this.zoomIn({ scroll: true });\n        }\n    }\n\n    /**\n     * @param {DragEvent} ev\n     */\n    onMousedownImage(ev) {\n        if (this.isDragging) {\n            return;\n        }\n        if (ev.button !== 0) {\n            return;\n        }\n        this.isDragging = true;\n        this.dragStartX = ev.clientX;\n        this.dragStartY = ev.clientY;\n    }\n\n    onMouseupImage() {\n        if (!this.isDragging) {\n            return;\n        }\n        this.isDragging = false;\n        this.translate.x += this.translate.dx;\n        this.translate.y += this.translate.dy;\n        this.translate.dx = 0;\n        this.translate.dy = 0;\n        this.updateZoomerStyle();\n    }\n\n    /**\n     * @param {DragEvent}\n     */\n    onMousemoveView(ev) {\n        if (!this.isDragging) {\n            return;\n        }\n        this.translate.dx = ev.clientX - this.dragStartX;\n        this.translate.dy = ev.clientY - this.dragStartY;\n        this.updateZoomerStyle();\n    }\n\n    resetZoom() {\n        this.state.scale = 1;\n        this.updateZoomerStyle();\n    }\n\n    rotate() {\n        this.state.angle += 90;\n    }\n\n    /**\n     * @param {{ scroll?: boolean }}\n     */\n    zoomIn({ scroll = false } = {}) {\n        this.state.scale = this.state.scale + (scroll ? this.scrollZoomStep : this.zoomStep);\n        this.updateZoomerStyle();\n    }\n\n    /**\n     * @param {{ scroll?: boolean }}\n     */\n    zoomOut({ scroll = false } = {}) {\n        if (this.state.scale === this.minScale) {\n            return;\n        }\n        const unflooredAdaptedScale =\n            this.state.scale - (scroll ? this.scrollZoomStep : this.zoomStep);\n        this.state.scale = Math.max(this.minScale, unflooredAdaptedScale);\n        this.updateZoomerStyle();\n    }\n\n    updateZoomerStyle() {\n        const tx =\n            this.imageRef.el.offsetWidth * this.state.scale > this.zoomerRef.el.offsetWidth\n                ? this.translate.x + this.translate.dx\n                : 0;\n        const ty =\n            this.imageRef.el.offsetHeight * this.state.scale > this.zoomerRef.el.offsetHeight\n                ? this.translate.y + this.translate.dy\n                : 0;\n        if (tx === 0) {\n            this.translate.x = 0;\n        }\n        if (ty === 0) {\n            this.translate.y = 0;\n        }\n        this.zoomerRef.el.style = \"transform: \" + `translate(${tx}px, ${ty}px)`;\n    }\n\n    get imageStyle() {\n        let style =\n            \"transform: \" +\n            `scale3d(${this.state.scale}, ${this.state.scale}, 1) ` +\n            `rotate(${this.state.angle}deg);`;\n\n        if (this.state.angle % 180 !== 0) {\n            style += `max-height: ${window.innerWidth}px; max-width: ${window.innerHeight}px;`;\n        } else {\n            style += \"max-height: 100%; max-width: 100%;\";\n        }\n        return style;\n    }\n\n    onClickPrint() {\n        const printWindow = window.open(\"about:blank\", \"_new\");\n        printWindow.document.open();\n        printWindow.document.write(`\n                <html>\n                    <head>\n                        <script>\n                            function onloadImage() {\n                                setTimeout('printImage()', 10);\n                            }\n                            function printImage() {\n                                window.print();\n                                window.close();\n                            }\n                        </script>\n                    </head>\n                    <body onload='onloadImage()'>\n                        <img src=\"${this.state.file.defaultSource}\" alt=\"\"/>\n                    </body>\n                </html>`);\n        printWindow.document.close();\n    }\n}\n", "import { onWillDestroy } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\nimport { FileViewer } from \"./file_viewer\";\n\nlet id = 1;\n\nexport function createFileViewer() {\n    const fileViewerId = `web.file_viewer${id++}`;\n    /**\n     * @param {import(\"@web/core/file_viewer/file_viewer\").FileViewer.props.files[]} file\n     * @param {import(\"@web/core/file_viewer/file_viewer\").FileViewer.props.files} files\n     */\n    function open(file, files = [file]) {\n        if (!file.isViewable) {\n            return;\n        }\n        if (files.length > 0) {\n            const viewableFiles = files.filter((file) => file.isViewable);\n            const index = viewableFiles.indexOf(file);\n            registry.category(\"main_components\").add(fileViewerId, {\n                Component: FileViewer,\n                props: { files: viewableFiles, startIndex: index, close },\n            });\n        }\n    }\n\n    function close() {\n        registry.category(\"main_components\").remove(fileViewerId);\n    }\n    return { open, close };\n}\n\nexport function useFileViewer() {\n    const { open, close } = createFileViewer();\n    onWillDestroy(close);\n    return { open, close };\n}\n", "import { useService } from \"@web/core/utils/hooks\";\n\nimport { useEffect } from \"@odoo/owl\";\n\n/**\n * This hook will register/unregister the given registration\n * when the caller component will mount/unmount.\n *\n * @param {string} hotkey\n * @param {import(\"./hotkey_service\").HotkeyCallback} callback\n * @param {import(\"./hotkey_service\").HotkeyOptions} [options] additional options\n */\nexport function useHotkey(hotkey, callback, options = {}) {\n    const hotkeyService = useService(\"hotkey\");\n    useEffect(\n        () => hotkeyService.add(hotkey, callback, options),\n        () => []\n    );\n}\n", "import { isMacOS } from \"../browser/feature_detection\";\nimport { registry } from \"../registry\";\nimport { browser } from \"../browser/browser\";\nimport { getVisibleElements } from \"../utils/ui\";\n\n/**\n * @typedef {(context: { area: HTMLElement, target: EventTarget }) => void} HotkeyCallback\n *\n * @typedef {Object} HotkeyOptions\n * @property {boolean} [allowRepeat]\n *  allow registration to perform multiple times when hotkey is held down\n * @property {boolean} [bypassEditableProtection]\n *  if true the hotkey service will call this registration\n *  even if an editable element is focused\n * @property {boolean} [global]\n *  allow registration to perform no matter the UI active element\n * @property {() => HTMLElement} [area]\n *  adds a restricted operating area for this hotkey\n * @property {() => boolean} [isAvailable]\n *  adds a validation before calling the hotkey registration's callback\n * @property {() => HTMLElement} [withOverlay]\n *  provides the element on which the overlay should be displayed\n *  Please note that if provided the hotkey will only work with\n *  the overlay access key, similarly to all [data-hotkey] DOM attributes.\n *\n * @typedef {HotkeyOptions & {\n *  hotkey: string,\n *  callback: HotkeyCallback,\n *  activeElement: HTMLElement,\n * }} HotkeyRegistration\n */\n\nconst ALPHANUM_KEYS = \"abcdefghijklmnopqrstuvwxyz0123456789\".split(\"\");\nconst NAV_KEYS = [\n    \"arrowleft\",\n    \"arrowright\",\n    \"arrowup\",\n    \"arrowdown\",\n    \"pageup\",\n    \"pagedown\",\n    \"home\",\n    \"end\",\n    \"backspace\",\n    \"enter\",\n    \"tab\",\n    \"delete\",\n    \"space\",\n];\nconst MODIFIERS = [\"alt\", \"control\", \"shift\"];\nconst AUTHORIZED_KEYS = [...ALPHANUM_KEYS, ...NAV_KEYS, \"escape\"];\n\n/**\n * Get the actual hotkey being pressed.\n *\n * @param {KeyboardEvent} ev\n * @returns {string} the active hotkey, in lowercase\n */\nexport function getActiveHotkey(ev) {\n    if (!ev.key) {\n        // Chrome may trigger incomplete keydown events under certain circumstances.\n        // E.g. when using browser built-in autocomplete on an input.\n        // See https://stackoverflow.com/questions/59534586/google-chrome-fires-keydown-event-when-form-autocomplete\n        return \"\";\n    }\n    if (ev.isComposing) {\n        // This case happens with an IME for example: we let it handle all key events.\n        return \"\";\n    }\n    const hotkey = [];\n\n    // ------- Modifiers -------\n    // Modifiers are pushed in ascending order to the hotkey.\n    if (isMacOS() ? ev.ctrlKey : ev.altKey) {\n        hotkey.push(\"alt\");\n    }\n    if (isMacOS() ? ev.metaKey : ev.ctrlKey) {\n        hotkey.push(\"control\");\n    }\n    if (ev.shiftKey) {\n        hotkey.push(\"shift\");\n    }\n\n    // ------- Key -------\n    let key = ev.key.toLowerCase();\n\n    // The browser space is natively \" \", we want \"space\" for esthetic reasons\n    if (key === \" \") {\n        key = \"space\";\n    }\n\n    // Identify if the user has tapped on the number keys above the text keys.\n    if (ev.code && ev.code.indexOf(\"Digit\") === 0) {\n        key = ev.code.slice(-1);\n    }\n    // Prefer physical keys for non-latin keyboard layout.\n    if (!AUTHORIZED_KEYS.includes(key) && ev.code && ev.code.indexOf(\"Key\") === 0) {\n        key = ev.code.slice(-1).toLowerCase();\n    }\n    // Make sure we do not duplicate a modifier key\n    if (!MODIFIERS.includes(key)) {\n        hotkey.push(key);\n    }\n\n    return hotkey.join(\"+\");\n}\n\nexport const hotkeyService = {\n    dependencies: [\"ui\"],\n    // Be aware that all odoo hotkeys are designed with this modifier in mind,\n    // so changing the overlay modifier may conflict with some shortcuts.\n    overlayModifier: \"alt\",\n    start(env, { ui }) {\n        /** @type {Map<number, HotkeyRegistration>} */\n        const registrations = new Map();\n        let nextToken = 0;\n        let overlaysVisible = false;\n\n        addListeners(browser);\n\n        function addListeners(target) {\n            target.addEventListener(\"keydown\", onKeydown);\n            target.addEventListener(\"keyup\", removeHotkeyOverlays);\n            target.addEventListener(\"blur\", removeHotkeyOverlays);\n            target.addEventListener(\"click\", removeHotkeyOverlays);\n        }\n\n        /**\n         * Handler for keydown events.\n         * Verifies if the keyboard event can be dispatched or not.\n         * Rules sequence to forbid dispatching :\n         * - UI is blocked\n         * - the pressed key is not whitelisted\n         *\n         * @param {KeyboardEvent} event\n         */\n        function onKeydown(event) {\n            if (event.code && event.code.indexOf(\"Numpad\") === 0 && /^\\d$/.test(event.key)) {\n                // Ignore all number keys from the Keypad because of a certain input method\n                // of (advance-)ASCII characters on Windows OS: ALT+[numerical code from keypad]\n                // See https://support.microsoft.com/en-us/office/insert-ascii-or-unicode-latin-based-symbols-and-characters-d13f58d3-7bcb-44a7-a4d5-972ee12e50e0#bm1\n                return;\n            }\n\n            const hotkey = getActiveHotkey(event);\n            if (!hotkey) {\n                return;\n            }\n            const { activeElement, isBlocked } = ui;\n\n            // Do not dispatch if UI is blocked\n            if (isBlocked) {\n                return;\n            }\n\n            // Replace all [accesskey] attrs by [data-hotkey] on all elements.\n            // This is needed to take over on the default accesskey behavior\n            // and also to avoid any conflict with it.\n            const elementsWithAccessKey = document.querySelectorAll(\"[accesskey]\");\n            for (const el of elementsWithAccessKey) {\n                if (el instanceof HTMLElement) {\n                    el.dataset.hotkey = el.accessKey;\n                    el.removeAttribute(\"accesskey\");\n                }\n            }\n\n            // Special case: open hotkey overlays\n            if (!overlaysVisible && hotkey === hotkeyService.overlayModifier) {\n                addHotkeyOverlays(activeElement);\n                event.preventDefault();\n                return;\n            }\n\n            // Is the pressed key NOT whitelisted ?\n            const singleKey = hotkey.split(\"+\").pop();\n            if (!AUTHORIZED_KEYS.includes(singleKey)) {\n                return;\n            }\n\n            // Protect any editable target that does not explicitly accept hotkeys\n            // NB: except for ESC, which is always allowed as hotkey in editables.\n            const targetIsEditable =\n                event.target instanceof HTMLElement &&\n                (/input|textarea/i.test(event.target.tagName) || event.target.isContentEditable) &&\n                !event.target.matches(\"input[type=checkbox], input[type=radio]\");\n            const shouldProtectEditable =\n                targetIsEditable && !event.target.dataset.allowHotkeys && singleKey !== \"escape\";\n\n            // Finally, prepare and dispatch.\n            const infos = {\n                activeElement,\n                hotkey,\n                isRepeated: event.repeat,\n                target: event.target,\n                shouldProtectEditable,\n            };\n            const dispatched = dispatch(infos);\n            if (dispatched) {\n                // Only if event has been handled.\n                // Purpose: prevent browser defaults\n                event.preventDefault();\n                // Purpose: stop other window keydown listeners (e.g. home menu)\n                event.stopImmediatePropagation();\n            }\n\n            // Finally, always remove overlays at that point\n            if (overlaysVisible) {\n                removeHotkeyOverlays();\n                event.preventDefault();\n            }\n        }\n\n        /**\n         * Dispatches an hotkey to first matching registration.\n         * Registrations are iterated in following order:\n         * - priority to all registrations done through the hotkeyService.add()\n         *   method (NB: in descending order of insertion = newer first)\n         * - then all registrations done through the DOM [data-hotkey] attribute\n         *\n         * @param {{\n         *  activeElement: HTMLElement,\n         *  hotkey: string,\n         *  isRepeated: boolean,\n         *  target: EventTarget,\n         *  shouldProtectEditable: boolean,\n         * }} infos\n         * @returns {boolean} true if has been dispatched\n         */\n        function dispatch(infos) {\n            const { activeElement, hotkey, isRepeated, target, shouldProtectEditable } = infos;\n\n            // Prepare registrations and the common filter\n            const reversedRegistrations = Array.from(registrations.values()).reverse();\n            const domRegistrations = getDomRegistrations(hotkey, activeElement);\n            const allRegistrations = reversedRegistrations.concat(domRegistrations);\n\n            // Find all candidates\n            const candidates = allRegistrations.filter(\n                (reg) =>\n                    reg.hotkey === hotkey &&\n                    (reg.allowRepeat || !isRepeated) &&\n                    (reg.bypassEditableProtection || !shouldProtectEditable) &&\n                    (reg.global || reg.activeElement === activeElement) &&\n                    (!reg.isAvailable || reg.isAvailable()) &&\n                    (!reg.area || (target && reg.area() && reg.area().contains(target)))\n            );\n\n            // First candidate\n            let winner = candidates.shift();\n            if (winner && winner.area) {\n                // If there is an area, find the closest one\n                for (const candidate of candidates.filter((c) => Boolean(c.area))) {\n                    if (candidate.area() && winner.area().contains(candidate.area())) {\n                        winner = candidate;\n                    }\n                }\n            }\n\n            // Dispatch actual hotkey to the matching registration\n            if (winner) {\n                winner.callback({\n                    area: winner.area && winner.area(),\n                    target,\n                });\n                return true;\n            }\n            return false;\n        }\n\n        /**\n         * Get a list of registrations from the [data-hotkey] defined in the DOM\n         *\n         * @param {string} hotkey\n         * @param {HTMLElement} activeElement\n         * @returns {HotkeyRegistration[]}\n         */\n        function getDomRegistrations(hotkey, activeElement) {\n            const overlayModParts = hotkeyService.overlayModifier.split(\"+\");\n            if (!overlayModParts.every((el) => hotkey.includes(el))) {\n                return [];\n            }\n\n            // Get all elements having a data-hotkey attribute  and matching\n            // the actual hotkey without the overlayModifier.\n            const cleanHotkey = hotkey\n                .split(\"+\")\n                .filter((key) => !overlayModParts.includes(key))\n                .join(\"+\");\n            const elems = getVisibleElements(activeElement, `[data-hotkey='${cleanHotkey}' i]`);\n            return elems.map((el) => ({\n                hotkey,\n                activeElement,\n                bypassEditableProtection: true,\n                callback: () => {\n                    if (document.activeElement) {\n                        document.activeElement.blur();\n                    }\n                    el.focus();\n                    setTimeout(() => el.click());\n                },\n            }));\n        }\n\n        /**\n         * Add the hotkey overlays respecting the ui active element.\n         * @param {HTMLElement} activeElement\n         */\n        function addHotkeyOverlays(activeElement) {\n            // Gather the hotkeys to overlay registered through the useHotkey hook.\n            const hotkeysFromHookToHighlight = [];\n            for (const [, registration] of registrations) {\n                const overlayElement = registration.withOverlay?.();\n                if (overlayElement) {\n                    hotkeysFromHookToHighlight.push({\n                        hotkey: registration.hotkey.replace(\n                            `${hotkeyService.overlayModifier}+`,\n                            \"\"\n                        ),\n                        el: overlayElement,\n                    });\n                }\n            }\n\n            // Gather the hotkeys to overlay registered through the DOM datasets.\n            const hotkeysFromDomToHighlight = getVisibleElements(\n                activeElement,\n                \"[data-hotkey]:not(:disabled)\"\n            ).map((el) => ({ hotkey: el.dataset.hotkey, el }));\n\n            const items = [...hotkeysFromDomToHighlight, ...hotkeysFromHookToHighlight];\n            for (const item of items) {\n                const hotkey = item.hotkey;\n                const overlay = document.createElement(\"div\");\n                overlay.classList.add(\n                    \"o_web_hotkey_overlay\",\n                    \"position-absolute\",\n                    \"top-0\",\n                    \"bottom-0\",\n                    \"start-0\",\n                    \"end-0\",\n                    \"d-flex\",\n                    \"justify-content-center\",\n                    \"align-items-center\",\n                    \"m-0\",\n                    \"bg-black-50\",\n                    \"h6\"\n                );\n                overlay.style.zIndex = 1;\n                const overlayKbd = document.createElement(\"kbd\");\n                overlayKbd.className = \"small\";\n                overlayKbd.appendChild(document.createTextNode(hotkey.toUpperCase()));\n                overlay.appendChild(overlayKbd);\n\n                let overlayParent;\n                if (item.el.tagName.toUpperCase() === \"INPUT\") {\n                    // special case for the search input that has an access key\n                    // defined. We cannot set the overlay on the input itself,\n                    // only on its parent.\n                    overlayParent = item.el.parentElement;\n                } else {\n                    overlayParent = item.el;\n                }\n\n                if (overlayParent.style.position !== \"absolute\") {\n                    overlayParent.style.position = \"relative\";\n                }\n                overlayParent.appendChild(overlay);\n            }\n            overlaysVisible = true;\n        }\n\n        /**\n         * Remove all the hotkey overlays.\n         */\n        function removeHotkeyOverlays() {\n            for (const overlay of document.querySelectorAll(\".o_web_hotkey_overlay\")) {\n                overlay.remove();\n            }\n            overlaysVisible = false;\n        }\n\n        /**\n         * Registers a new hotkey.\n         *\n         * @param {string} hotkey\n         * @param {HotkeyCallback} callback\n         * @param {HotkeyOptions} [options]\n         * @returns {number} registration token\n         */\n        function registerHotkey(hotkey, callback, options = {}) {\n            // Validate some informations\n            if (!hotkey || hotkey.length === 0) {\n                throw new Error(\"You must specify an hotkey when registering a registration.\");\n            }\n\n            if (!callback || typeof callback !== \"function\") {\n                throw new Error(\n                    \"You must specify a callback function when registering a registration.\"\n                );\n            }\n\n            /**\n             * An hotkey must comply to these rules:\n             *  - all parts are whitelisted\n             *  - single key part comes last\n             *  - each part is separated by the dash character: \"+\"\n             */\n            const keys = hotkey\n                .toLowerCase()\n                .split(\"+\")\n                .filter((k) => !MODIFIERS.includes(k));\n            if (keys.some((k) => !AUTHORIZED_KEYS.includes(k))) {\n                throw new Error(\n                    `You are trying to subscribe for an hotkey ('${hotkey}')\n            that contains parts not whitelisted: ${keys.join(\", \")}`\n                );\n            } else if (keys.length > 1) {\n                throw new Error(\n                    `You are trying to subscribe for an hotkey ('${hotkey}')\n            that contains more than one single key part: ${keys.join(\"+\")}`\n                );\n            }\n\n            // Add registration\n            const token = nextToken++;\n            /** @type {HotkeyRegistration} */\n            const registration = {\n                hotkey: hotkey.toLowerCase(),\n                callback,\n                activeElement: null,\n                allowRepeat: options && options.allowRepeat,\n                bypassEditableProtection: options && options.bypassEditableProtection,\n                global: options && options.global,\n                area: options && options.area,\n                isAvailable: options && options.isAvailable,\n                withOverlay: options && options.withOverlay,\n            };\n\n            // Due to the way elements are mounted in the DOM by Owl (bottom-to-top),\n            // we need to wait the next micro task tick to set the context owner of the registration.\n            Promise.resolve().then(() => {\n                registration.activeElement = ui.activeElement;\n            });\n\n            registrations.set(token, registration);\n            return token;\n        }\n\n        /**\n         * Unsubscribes the token corresponding registration.\n         *\n         * @param {number} token\n         */\n        function unregisterHotkey(token) {\n            registrations.delete(token);\n        }\n\n        return {\n            /**\n             * @param {string} hotkey\n             * @param {HotkeyCallback} callback\n             * @param {HotkeyOptions} [options]\n             * @returns {() => void}\n             */\n            add(hotkey, callback, options = {}) {\n                const token = registerHotkey(hotkey, callback, options);\n                return () => {\n                    unregisterHotkey(token);\n                };\n            },\n            /**\n             * @param {HTMLIFrameElement} iframe\n             */\n            registerIframe(iframe) {\n                addListeners(iframe.contentWindow);\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"hotkey\", hotkeyService);\n/** @typedef {ReturnType<hotkeyService[\"start\"]>} HotkeyService */\n", "import { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\nimport { Component, onMounted, useState } from \"@odoo/owl\";\nimport { isDisplayStandalone } from \"@web/core/browser/feature_detection\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\n\nexport class InstallScopedApp extends Component {\n    static props = {};\n    static template = \"web.InstallScopedApp\";\n    static components = { Dropdown };\n    setup() {\n        this.pwa = useState(useService(\"pwa\"));\n        this.state = useState({ manifest: {}, showInstallUI: false });\n        this.isDisplayStandalone = isDisplayStandalone();\n        // BeforeInstallPrompt event can take while before the browser triggers it. Some will display\n        // immediately, others will wait that the user has interacted for some time with the website.\n        this.isInstallationPossible = browser.BeforeInstallPromptEvent !== undefined;\n        onMounted(async () => {\n            this.state.manifest = await this.pwa.getManifest();\n            this.state.showInstallUI = true;\n        });\n    }\n    onChangeName(ev) {\n        const value = ev.target.value;\n        if (value !== this.state.manifest.name) {\n            const url = new URL(document.location.href);\n            url.searchParams.set(\"app_name\", encodeURIComponent(value));\n            browser.location.replace(url);\n        }\n    }\n    onInstall() {\n        this.state.showInstallUI = false;\n        this.pwa.show({\n            onDone: (res) => {\n                if (res.outcome === \"accepted\") {\n                    browser.location.replace(this.state.manifest.start_url);\n                } else {\n                    this.state.showInstallUI = true;\n                }\n            },\n        });\n    }\n}\n\nregistry.category(\"public_components\").add(\"web.install_scoped_app\", InstallScopedApp);\n", "import { localization } from \"@web/core/l10n/localization\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { memoize } from \"@web/core/utils/functions\";\nimport { ensureArray } from \"../utils/arrays\";\n\nconst { DateTime, Settings } = luxon;\n\n/**\n * @typedef ConversionOptions\n *  This is a list of the available options to either:\n *  - convert a DateTime to a string (format)\n *  - convert a string to a DateTime (parse)\n *  All of these are optional and the default values are issued by the Localization service.\n *\n * @property {string} [format]\n *  Format used to format a DateTime or to parse a formatted string.\n *  > Default: the session localization format.\n * @property {boolean} [condensed] if true, months, days and hours will be formatted without\n *  leading 0.\n *\n * @typedef {luxon.DateTime} DateTime\n *\n * @typedef {[NullableDateTime, NullableDateTime]} NullableDateRange\n *\n * @typedef {DateTime | false | null | undefined} NullableDateTime\n */\n\n/**\n * Limits defining a valid date.\n * This is needed because the server only understands 4-digit years.\n * Note: both of these are in the local timezone\n */\nexport const MIN_VALID_DATE = DateTime.fromObject({ year: 1000 });\nexport const MAX_VALID_DATE = DateTime.fromObject({ year: 9999 }).endOf(\"year\");\n\nconst SERVER_DATE_FORMAT = \"yyyy-MM-dd\";\nconst SERVER_TIME_FORMAT = \"HH:mm:ss\";\nconst SERVER_DATETIME_FORMAT = `${SERVER_DATE_FORMAT} ${SERVER_TIME_FORMAT}`;\n\nconst nonAlphaRegex = /[^a-z]/gi;\nconst nonDigitRegex = /[^\\d]/g;\n\nconst normalizeFormatTable = {\n    // Python strftime to luxon.js conversion table\n    // See odoo/addons/base/views/res_lang_views.xml\n    // for details about supported directives\n    a: \"ccc\",\n    A: \"cccc\",\n    b: \"MMM\",\n    B: \"MMMM\",\n    d: \"dd\",\n    H: \"HH\",\n    I: \"hh\",\n    j: \"o\",\n    m: \"MM\",\n    M: \"mm\",\n    p: \"a\",\n    S: \"ss\",\n    W: \"WW\",\n    w: \"c\",\n    y: \"yy\",\n    Y: \"yyyy\",\n    c: \"ccc MMM d HH:mm:ss yyyy\",\n    x: \"MM/dd/yy\",\n    X: \"HH:mm:ss\",\n};\n\nconst smartDateUnits = {\n    d: \"days\",\n    m: \"months\",\n    w: \"weeks\",\n    y: \"years\",\n};\nconst smartDateRegex = new RegExp(\n    [\"^\", \"([+-])\", \"(\\\\d+)\", `([${Object.keys(smartDateUnits).join(\"\")}]?)`, \"$\"].join(\"\\\\s*\"),\n    \"i\"\n);\n\n/** @type {WeakMap<DateTime, string>} */\nconst dateCache = new WeakMap();\n/** @type {WeakMap<DateTime, string>} */\nconst dateTimeCache = new WeakMap();\n\nexport class ConversionError extends Error {\n    name = \"ConversionError\";\n}\n\n//-----------------------------------------------------------------------------\n// Helpers\n//-----------------------------------------------------------------------------\n\n/**\n * Checks whether 2 given dates or date ranges are equal. Both values are allowed\n * to be falsy or to not be of the same type (which will return false).\n *\n * @param {NullableDateTime | NullableDateRange} d1\n * @param {NullableDateTime | NullableDateRange} d2\n * @returns {boolean}\n */\nexport function areDatesEqual(d1, d2) {\n    if (Array.isArray(d1) || Array.isArray(d2)) {\n        // One of the values is a date range -> checks deep equality between the ranges\n        d1 = ensureArray(d1);\n        d2 = ensureArray(d2);\n        return d1.length === d2.length && d1.every((d1Val, i) => areDatesEqual(d1Val, d2[i]));\n    }\n    if (d1 instanceof DateTime && d2 instanceof DateTime && d1 !== d2) {\n        // Both values are DateTime objects -> use Luxon's comparison\n        return d1.equals(d2);\n    } else {\n        // One of the values is not a DateTime object -> fallback to strict equal\n        return d1 === d2;\n    }\n}\n\n/**\n * @param {DateTime} desired\n * @param {DateTime} minDate\n * @param {DateTime} maxDate\n */\nexport function clampDate(desired, minDate, maxDate) {\n    if (maxDate < desired) {\n        return maxDate;\n    }\n    if (minDate > desired) {\n        return minDate;\n    }\n    return desired;\n}\n\n/**\n * Get the week number of a given date, in the user's locale settings.\n *\n * @param {Date | luxon.DateTime} date\n * @returns {number}\n *  the ISO week number (1-53) of the Monday nearest to the locale's first day of the week\n */\nexport function getLocalWeekNumber(date) {\n    return getLocalYearAndWeek(date).week;\n}\n\n/**\n * Get the week year and week number of a given date, in the user's locale settings.\n *\n * @param {Date | luxon.DateTime} date\n * @returns {{ year: number, week: number }}\n *  the year the week is part of, and\n *  the ISO week number (1-53) of the Monday nearest to the locale's first day of the week\n */\nexport function getLocalYearAndWeek(date) {\n    if (!date.isLuxonDateTime) {\n        date = DateTime.fromJSDate(date);\n    }\n    const { weekStart } = localization;\n    // go to start of week\n    date = date.minus({ days: (date.weekday + 7 - weekStart) % 7 });\n    // go to nearest Monday, up to 3 days back- or forwards\n    date =\n        weekStart > 1 && weekStart < 5 // if firstDay after Mon & before Fri\n            ? date.minus({ days: (date.weekday + 6) % 7 }) // then go back 1-3 days\n            : date.plus({ days: (8 - date.weekday) % 7 }); // else go forwards 0-3 days\n    date = date.plus({ days: 6 }); // go to last weekday of ISO week\n    const jan4 = DateTime.local(date.year, 1, 4);\n    // count from previous year if week falls before Jan 4\n    const diffDays =\n        date < jan4 ? date.diff(jan4.minus({ years: 1 }), \"day\").days : date.diff(jan4, \"day\").days;\n    return { year: date.year, week: Math.trunc(diffDays / 7) + 1 };\n}\n\n/**\n * Get the start of the week for the given date, in the user's locale settings.\n * The start of the week is determined by the `weekStart` setting.\n *\n * Luxon's `.startOf(\"week\")` method uses the ISO week definition, which starts on Monday.\n * Luxon has a `.startOf(\"week\", { useLocaleWeeks: true })` method, but it relies on the\n * Intl API and the `getWeekInfo` method, which is not supported in all browsers.\n * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo#browser_compatibility\n *\n * @param {luxon.DateTime} date\n * @returns {luxon.DateTime}\n */\nexport function getStartOfLocalWeek(date) {\n    const { weekStart } = localization;\n    const weekday = date.weekday < weekStart ? weekStart - 7 : weekStart;\n    return date.set({ weekday }).startOf(\"day\");\n}\n\n/**\n * Get the end of the week for the given date, in the user's locale settings.\n * The end of the week is determined by the `weekStart` setting.\n *\n * Luxon's `.endOf(\"week\")` method uses the ISO week definition, which starts on Monday.\n * Luxon has a `.endOf(\"week\", { useLocaleWeeks: true })` method, but it relies on the\n * Intl API and the `getWeekInfo` method, which is not supported in all browsers.\n * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo#browser_compatibility\n *\n * @param {luxon.DateTime} date\n * @returns {luxon.DateTime}\n */\nexport function getEndOfLocalWeek(date) {\n    return getStartOfLocalWeek(date).plus({ days: 6 }).endOf(\"day\");\n}\n\n/**\n * Returns whether the given format is a 24-hour format.\n * Falls back to localization time format if none is given.\n *\n * @param {string} format\n */\nexport function is24HourFormat(format) {\n    return /H/.test(format || localization.timeFormat);\n}\n\n/**\n * @param {NullableDateTime | NullableDateRange} value\n * @param {NullableDateRange} range\n * @returns {boolean}\n */\nexport function isInRange(value, range) {\n    if (!value || !range) {\n        return false;\n    }\n    if (Array.isArray(value)) {\n        const actualValues = value.filter(Boolean);\n        if (actualValues.length < 2) {\n            return isInRange(actualValues[0], range);\n        }\n        return (\n            (value[0] <= range[0] && range[0] <= value[1]) ||\n            (range[0] <= value[0] && value[0] <= range[1])\n        );\n    } else {\n        return range[0] <= value && value <= range[1];\n    }\n}\n\n/**\n * Returns whether the given format uses a meridiem suffix (AM/PM).\n * Falls back to localization time format if none is given.\n *\n * @param {string} format\n */\nexport function isMeridiemFormat(format) {\n    return /a/.test(format || localization.timeFormat);\n}\n\n/**\n * Returns whether the given DateTime is valid.\n * The date is considered valid if it:\n * - is a DateTime object\n * - has the \"isValid\" flag set to true\n * - is between 1000-01-01 and 9999-12-31 (both included)\n * @see MIN_VALID_DATE\n * @see MAX_VALID_DATE\n *\n * @param {NullableDateTime} date\n */\nfunction isValidDate(date) {\n    return date && date.isValid && isInRange(date, [MIN_VALID_DATE, MAX_VALID_DATE]);\n}\n\n/**\n * Smart date inputs are shortcuts to write dates quicker.\n * These shortcuts should respect the format ^[+-]\\d+[dmwy]?$\n *\n * e.g.\n *   \"+1d\" or \"+1\" will return now + 1 day\n *   \"-2w\" will return now - 2 weeks\n *   \"+3m\" will return now + 3 months\n *   \"-4y\" will return now + 4 years\n *\n * @param {string} value\n * @returns {NullableDateTime} Luxon datetime object (in the user's local timezone)\n */\nfunction parseSmartDateInput(value) {\n    const match = value.match(smartDateRegex);\n    if (match) {\n        let date = DateTime.local();\n        const offset = parseInt(match[2], 10);\n        const unit = smartDateUnits[(match[3] || \"d\").toLowerCase()];\n        if (match[1] === \"+\") {\n            date = date.plus({ [unit]: offset });\n        } else {\n            date = date.minus({ [unit]: offset });\n        }\n        return date;\n    }\n    return false;\n}\n\n/**\n * Removes any duplicate *subsequent* alphabetic characters in a given string.\n * Example: \"aa-bb-CCcc-ddD-c xxxx-Yy-ZZ\" -> \"a-b-Cc-dD-c x-Yy-Z\"\n *\n * @type {(str: string) => string}\n */\nconst stripAlphaDupes = memoize(function stripAlphaDupes(str) {\n    return str.replace(/[a-z]/gi, (letter, index, str) =>\n        letter === str[index - 1] ? \"\" : letter\n    );\n});\n\n/**\n * Convert Python strftime to escaped luxon.js format.\n *\n * @type {(format: string) => string}\n */\nexport const strftimeToLuxonFormat = memoize(function strftimeToLuxonFormat(format) {\n    const output = [];\n    let inToken = false;\n    for (let index = 0; index < format.length; ++index) {\n        let character = format[index];\n        if (character === \"%\" && !inToken) {\n            inToken = true;\n            continue;\n        }\n        if (/[a-z]/gi.test(character)) {\n            if (inToken && normalizeFormatTable[character] !== undefined) {\n                character = normalizeFormatTable[character];\n            } else {\n                character = `'${character}'`; // luxon escape\n            }\n        }\n        output.push(character);\n        inToken = false;\n    }\n    return output.join(\"\");\n});\n\n/**\n * Lazy getter returning the start of the current day.\n */\nexport function today() {\n    return DateTime.local().startOf(\"day\");\n}\n\n//-----------------------------------------------------------------------------\n// Formatting\n//-----------------------------------------------------------------------------\n\nconst condensedFormats = {};\n/**\n * Given a date(time) format, returns a format where months, days and hours are\n * displayed without the leading 0 (e.g. 03/05/2024 08:00:00 => 3/5/2024 8:00:00).\n *\n * @param {string} format\n * @returns string\n */\nfunction getCondensedFormat(format) {\n    const originalFormat = format;\n    if (!condensedFormats[originalFormat]) {\n        format = format.replace(/(^|[^M])M{2}([^M]|$)/, \"$1M$2\");\n        format = format.replace(/(^|[^d])d{2}([^d]|$)/, \"$1d$2\");\n        format = format.replace(/(^|[^H])H{2}([^H]|$)/, \"$1H$2\");\n        condensedFormats[originalFormat] = format;\n    }\n    return condensedFormats[originalFormat];\n}\n\n/**\n * Formats a DateTime object to a date string\n *\n * @param {NullableDateTime} value\n * @param {ConversionOptions} [options={}]\n */\nexport function formatDate(value, options = {}) {\n    if (!value) {\n        return \"\";\n    }\n    let format = options.format;\n    if (!format) {\n        format = localization.dateFormat;\n        if (options.condensed) {\n            format = getCondensedFormat(format);\n        }\n    }\n    return value.toFormat(format);\n}\n\n/**\n * Formats a DateTime object to a datetime string\n *\n * @param {NullableDateTime} value\n * @param {ConversionOptions} [options={}]\n */\nexport function formatDateTime(value, options = {}) {\n    if (!value) {\n        return \"\";\n    }\n    let format = options.format;\n    if (!format) {\n        if (options.showSeconds === false) {\n            format = `${localization.dateFormat} ${localization.shortTimeFormat}`;\n        } else {\n            format = localization.dateTimeFormat;\n        }\n        if (options.condensed) {\n            format = getCondensedFormat(format);\n        }\n    }\n    return value.setZone(options.tz || \"default\").toFormat(format);\n}\n\n/**\n * Converts a given duration in seconds into a human-readable format.\n *\n * The function takes a duration in seconds and converts it into a human-readable form,\n * such as \"1h\" or \"1 hour, 30 minutes\", depending on the value of the `showFullDuration` parameter.\n * If the `showFullDuration` is set to true, the function will display up to two non-zero duration\n * components in long form (e.g: hours, minutes).\n * Otherwise, it will show just the largest non-zero duration component in narrow form (e.g: y or h).\n * Luxon takes care of translations given the current locale.\n *\n * @param {number} seconds - The duration in seconds to be converted.\n * @param {boolean} showFullDuration - If true, the output will have two components in long form.\n * Otherwise, just one component will be displayed in narrow form.\n *\n * @returns {string} A human-readable string representation of the duration.\n *\n * @example\n * // Sample usage\n * const durationInSeconds = 7320; // 2 hours and 2 minutes (2 * 3600 + 2 * 60)\n * const fullDuration = humanizeDuration(durationInSeconds, true);\n * console.log(fullDuration); // Output: \"2 hours, 2 minutes\"\n *\n * const shortDuration = humanizeDuration(durationInSeconds, false);\n * console.log(shortDuration); // Output: \"2h\"\n */\nexport function formatDuration(seconds, showFullDuration) {\n    const displayStyle = showFullDuration ? \"long\" : \"narrow\";\n    const numberOfValuesToDisplay = showFullDuration ? 2 : 1;\n    const durationKeys = [\"years\", \"months\", \"days\", \"hours\", \"minutes\"];\n\n    if (seconds < 60) {\n        seconds = 60;\n    }\n    seconds -= seconds % 60;\n\n    let duration = luxon.Duration.fromObject({ seconds: seconds }).shiftTo(...durationKeys);\n    duration = duration.shiftTo(...durationKeys.filter((key) => duration.get(key)));\n    const durationSplit = duration.toHuman({ unitDisplay: displayStyle }).split(\",\");\n\n    if (!showFullDuration && duration.loc.locale.includes(\"en\") && duration.months > 0) {\n        durationSplit[0] = durationSplit[0].replace(\"m\", \"M\");\n    }\n    return durationSplit.slice(0, numberOfValuesToDisplay).join(\",\");\n}\n\n/**\n * Formats the given DateTime to the server date format.\n * @param {DateTime} value\n * @returns {string}\n */\nexport function serializeDate(value) {\n    if (!dateCache.has(value)) {\n        dateCache.set(value, value.toFormat(SERVER_DATE_FORMAT, { numberingSystem: \"latn\" }));\n    }\n    return dateCache.get(value);\n}\n\n/**\n * Formats the given DateTime to the server datetime format.\n * @param {DateTime} value\n * @returns {string}\n */\nexport function serializeDateTime(value) {\n    if (!dateTimeCache.has(value)) {\n        dateTimeCache.set(\n            value,\n            value.setZone(\"utc\").toFormat(SERVER_DATETIME_FORMAT, { numberingSystem: \"latn\" })\n        );\n    }\n    return dateTimeCache.get(value);\n}\n\n//-----------------------------------------------------------------------------\n// Parsing\n//-----------------------------------------------------------------------------\n\n/**\n * Parses a string value to a Luxon DateTime object.\n *\n * @param {string} value\n * @param {ConversionOptions} [options={}]\n *\n * @see parseDateTime (Note: since we're only interested by the date itself, the\n *  returned value will always be set at the start of the day)\n */\nexport function parseDate(value, options = {}) {\n    const parsed = parseDateTime(value, {\n        ...options,\n        format: options.format || localization.dateFormat,\n    });\n    return parsed && parsed.startOf(\"day\");\n}\n\n/**\n * Parses a string value to a Luxon DateTime object.\n *\n * @param {string} value value to parse.\n *  - Value can take the form of a smart date:\n *    e.g. \"+3w\" for three weeks from now.\n *    (`options.format` is ignored in this case)\n *\n *  - If value cannot be parsed within the provided format,\n *    ISO8601 and SQL formats are then tried. If these formats\n *    include a timezone information, the returned value will\n *    still be set to the user's timezone.\n *    e.g. \"2020-01-01T12:00:00+06:00\" with the user's timezone being UTC+1,\n *         the returned value will express the same timestamp but in UTC+1 (here time will be 7:00).\n *\n * @param {ConversionOptions} options\n *\n * @returns {NullableDateTime} Luxon DateTime object in user's timezone\n */\nexport function parseDateTime(value, options = {}) {\n    if (!value) {\n        return false;\n    }\n\n    const fmt = options.format || localization.dateTimeFormat;\n    const parseOpts = {\n        setZone: true,\n        zone: options.tz || \"default\",\n    };\n    const switchToLatin = Settings.defaultNumberingSystem !== \"latn\" && /[0-9]/.test(value);\n\n    // Force numbering system to latin if actual numbers are found in the value\n    if (switchToLatin) {\n        parseOpts.numberingSystem = \"latn\";\n    }\n\n    // Base case: try parsing with the given format and options\n    let result = DateTime.fromFormat(value, fmt, parseOpts);\n\n    // Try parsing as a smart date\n    if (!isValidDate(result)) {\n        result = parseSmartDateInput(value);\n    }\n\n    // Try parsing with partial date parts\n    if (!isValidDate(result)) {\n        const fmtWoZero = stripAlphaDupes(fmt);\n        result = DateTime.fromFormat(value, fmtWoZero, parseOpts);\n    }\n\n    // Try parsing with custom shorthand date parts\n    if (!isValidDate(result)) {\n        // Luxon is not permissive regarding delimiting characters in the format.\n        // So if the value to parse has less characters than the format, we would\n        // try to parse without the delimiting characters.\n        const digitList = value.split(nonDigitRegex).filter(Boolean);\n        const fmtList = fmt.split(nonAlphaRegex).filter(Boolean);\n        const valWoSeps = digitList.join(\"\");\n\n        // This is the weird part: we try to adapt the given format to comply with\n        // the amount of digits in the given value. To do this we split the format\n        // and the value on non-letter and non-digit characters respectively. This\n        // should create the same amount of grouping parameters, and the format\n        // groups are trimmed according to the length of their corresponding\n        // digit group. The 'carry' variable allows for the length of a digit\n        // group to overflow to the next format group. This is typically the case\n        // when the given value doesn't have non-digit separators and generates\n        // one big digit group instead.\n        let carry = 0;\n        const fmtWoSeps = fmtList\n            .map((part, i) => {\n                const digitLength = (digitList[i] || \"\").length;\n                const actualPart = part.slice(0, digitLength + carry);\n                carry += digitLength - actualPart.length;\n                return actualPart;\n            })\n            .join(\"\");\n\n        result = DateTime.fromFormat(valWoSeps, fmtWoSeps, parseOpts);\n    }\n\n    // Try with defaul ISO or SQL formats\n    if (!isValidDate(result)) {\n        // Also try some fallback formats, but only if value counts more than\n        // four digit characters as this could get misinterpreted as the time of\n        // the actual date.\n        const valueDigits = value.replace(nonDigitRegex, \"\");\n        if (valueDigits.length > 4) {\n            result = DateTime.fromISO(value, parseOpts); // ISO8601\n            if (!isValidDate(result)) {\n                result = DateTime.fromSQL(value, parseOpts); // last try: SQL\n            }\n        }\n    }\n\n    // No working parsing methods: throw an error\n    if (!isValidDate(result)) {\n        throw new ConversionError(_t(\"'%s' is not a correct date or datetime\", value));\n    }\n\n    // Revert to original numbering system\n    if (switchToLatin) {\n        result = result.reconfigure({\n            numberingSystem: Settings.defaultNumberingSystem,\n        });\n    }\n\n    return result.setZone(options.tz || \"default\");\n}\n\n/**\n * Returns a date object parsed from the given serialized string.\n * @param {string} value serialized date string, e.g. \"2018-01-01\"\n */\nexport function deserializeDate(value, options = {}) {\n    options = { numberingSystem: \"latn\", zone: \"default\", ...options };\n    return DateTime.fromSQL(value, options).reconfigure({\n        numberingSystem: Settings.defaultNumberingSystem,\n    });\n}\n\n/**\n * Returns a datetime object parsed from the given serialized string.\n * @param {string} value serialized datetime string, e.g. \"2018-01-01 00:00:00\", expressed in UTC\n */\nexport function deserializeDateTime(value, options = {}) {\n    return DateTime.fromSQL(value, { numberingSystem: \"latn\", zone: \"utc\" })\n        .setZone(options?.tz || \"default\")\n        .reconfigure({\n            numberingSystem: Settings.defaultNumberingSystem,\n        });\n}\n", "/**\n * @typedef Localization\n * @property {string} dateFormat\n * @property {string} dateTimeFormat\n * @property {string} timeFormat\n * @property {string} decimalPoint\n * @property {\"ltr\" | \"rtl\"} direction\n * @property {[number, number]} grouping\n * @property {boolean} multiLang\n * @property {string} thousandsSep\n * @property {number} weekStart\n * @property {string} code\n */\n\n/**\n * This is the main object holding user specific data about the localization. Its basically\n * the JS counterpart of the \"res.lang\" model.\n * It is useful to directly access those data anywhere, even outside Components.\n *\n * Important Note: its data are actually loaded by the localization_service,\n * so a code like the following would not work:\n *   import { localization } from \"@web/core/l10n/localization\";\n *   const dateFormat = localization.dateFormat; // dateFormat isn't set yet\n * @type {Localization}\n */\nexport const localization = new Proxy(\n    {},\n    {\n        get: (target, p) => {\n            // \"then\" can be called implicitly if the object is returned in an\n            // `async` function, so we need to allow it.\n            if (p in target || p === \"then\") {\n                return Reflect.get(target, p);\n            }\n            throw new Error(\n                `could not access localization parameter \"${p}\": parameters are not ready yet. Maybe add 'localization' to your dependencies?`\n            );\n        },\n    }\n);\n", "import { session } from \"@web/session\";\nimport { jsToPyLocale } from \"@web/core/l10n/utils\";\nimport { user } from \"@web/core/user\";\nimport { browser } from \"../browser/browser\";\nimport { registry } from \"../registry\";\nimport { strftimeToLuxonFormat } from \"./dates\";\nimport { localization } from \"./localization\";\nimport { translatedTerms, translationLoaded, translationIsReady } from \"./translation\";\n\nconst { Settings } = luxon;\n\n/** @type {[RegExp, string][]} */\nconst NUMBERING_SYSTEMS = [\n    [/^ar-(sa|sy|001)$/i, \"arab\"],\n    [/^bn/i, \"beng\"],\n    [/^bo/i, \"tibt\"],\n    // [/^fa/i, \"Farsi (Persian)\"], // No numberingSystem found in Intl\n    // [/^(hi|mr|ne)/i, \"Hindi\"], // No numberingSystem found in Intl\n    // [/^my/i, \"Burmese\"], // No numberingSystem found in Intl\n    [/^pa-in/i, \"guru\"],\n    [/^ta/i, \"tamldec\"],\n    [/.*/i, \"latn\"],\n];\n\nexport const localizationService = {\n    start: async () => {\n        const cacheHashes = session.cache_hashes || {};\n        const translationsHash = cacheHashes.translations || new Date().getTime().toString();\n        const lang = jsToPyLocale(user.lang || document.documentElement.getAttribute(\"lang\"));\n        const translationURL = session.translationURL || \"/web/webclient/translations\";\n        let url = `${translationURL}/${translationsHash}`;\n        if (lang) {\n            url += `?lang=${lang}`;\n        }\n\n        const response = await browser.fetch(url);\n        if (!response.ok) {\n            throw new Error(\"Error while fetching translations\");\n        }\n\n        const {\n            lang_parameters: userLocalization,\n            modules: modules,\n            multi_lang: multiLang,\n        } = await response.json();\n\n        // FIXME We flatten the result of the python route.\n        // Eventually, we want a new python route to return directly the good result.\n        const terms = {};\n        for (const addon of Object.keys(modules)) {\n            for (const message of modules[addon].messages) {\n                terms[message.id] = message.string;\n            }\n        }\n\n        Object.assign(translatedTerms, terms);\n        translatedTerms[translationLoaded] = true;\n        translationIsReady.resolve(true);\n\n        const locale = user.lang || browser.navigator.language;\n        Settings.defaultLocale = locale;\n        for (const [re, numberingSystem] of NUMBERING_SYSTEMS) {\n            if (re.test(locale)) {\n                Settings.defaultNumberingSystem = numberingSystem;\n                break;\n            }\n        }\n\n        const dateFormat = strftimeToLuxonFormat(userLocalization.date_format);\n        const timeFormat = strftimeToLuxonFormat(userLocalization.time_format);\n        const shortTimeFormat = strftimeToLuxonFormat(userLocalization.short_time_format);\n        const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n        const grouping = JSON.parse(userLocalization.grouping);\n\n        Object.assign(localization, {\n            dateFormat,\n            timeFormat,\n            shortTimeFormat,\n            dateTimeFormat,\n            decimalPoint: userLocalization.decimal_point,\n            direction: userLocalization.direction,\n            grouping,\n            multiLang,\n            thousandsSep: userLocalization.thousands_sep,\n            weekStart: userLocalization.week_start,\n            code: jsToPyLocale(locale),\n        });\n        return localization;\n    },\n};\n\nregistry.category(\"services\").add(\"localization\", localizationService);\n", "import { Deferred } from \"@web/core/utils/concurrency\";\nimport { sprintf } from \"@web/core/utils/strings\";\n\nexport const translationLoaded = Symbol(\"translationLoaded\");\nexport const translatedTerms = {\n    [translationLoaded]: false,\n};\nexport const translationIsReady = new Deferred();\n/**\n * Translate a term, or return the term if no translation can be found.\n *\n * @param {string} term\n * @returns {string|LazyTranslatedString}\n */\nexport function _t(term, ...values) {\n    if (translatedTerms[translationLoaded]) {\n        const translation = translatedTerms[term] ?? term;\n        if (values.length === 0) {\n            return translation;\n        }\n        return sprintf(translation, ...values);\n    } else {\n        return new LazyTranslatedString(term, ...values);\n    }\n}\n\nclass LazyTranslatedString extends String {\n    constructor(term, ...values) {\n        super(term);\n        this.values = values;\n    }\n    valueOf() {\n        const term = super.valueOf();\n        if (translatedTerms[translationLoaded]) {\n            const translation = translatedTerms[term] ?? term;\n            if (this.values.length === 0) {\n                return translation;\n            }\n            return sprintf(translation, ...this.values);\n        } else {\n            throw new Error(`translation error`);\n        }\n    }\n    toString() {\n        return this.valueOf();\n    }\n}\n\n/*\n * Setup jQuery timeago:\n * Strings in timeago are \"composed\" with prefixes, words and suffixes. This\n * makes their detection by our translating system impossible. Use all literal\n * strings we're using with a translation mark here so the extractor can do its\n * job.\n */\n_t(\"less than a minute ago\");\n_t(\"about a minute ago\");\n_t(\"%d minutes ago\");\n_t(\"about an hour ago\");\n_t(\"%d hours ago\");\n_t(\"a day ago\");\n_t(\"%d days ago\");\n_t(\"about a month ago\");\n_t(\"%d months ago\");\n_t(\"about a year ago\");\n_t(\"%d years ago\");\n\n/**\n * Load the installed languages long names and code\n *\n * The result of the call is put in cache.\n * If any new language is installed, a full page refresh will happen,\n * so there is no need invalidate it.\n */\nexport async function loadLanguages(orm) {\n    if (!loadLanguages.installedLanguages) {\n        loadLanguages.installedLanguages = await orm.call(\"res.lang\", \"get_installed\");\n    }\n    return loadLanguages.installedLanguages;\n}\n", "export * from \"@web/core/l10n/utils/format_list\";\nexport * from \"@web/core/l10n/utils/locales\";\n", "import { user } from \"@web/core/user\";\n\n/**\n * Convert Unicode TR35-49 list pattern types to ES Intl.ListFormat options\n */\nconst LIST_STYLES = {\n    standard: {\n        type: \"conjunction\",\n        style: \"long\",\n    },\n    \"standard-short\": {\n        type: \"conjunction\",\n        style: \"short\",\n    },\n    or: {\n        type: \"disjunction\",\n        style: \"long\",\n    },\n    \"or-short\": {\n        type: \"disjunction\",\n        style: \"short\",\n    },\n    unit: {\n        type: \"unit\",\n        style: \"long\",\n    },\n    \"unit-short\": {\n        type: \"unit\",\n        style: \"short\",\n    },\n    \"unit-narrow\": {\n        type: \"unit\",\n        style: \"narrow\",\n    },\n};\n\n/**\n * Format the items in `list` as a list in a locale-dependent manner with the chosen style.\n *\n * The available styles are defined in the Unicode TR35-49 spec:\n * * standard:\n *   A typical \"and\" list for arbitrary placeholders.\n *   e.g. \"January, February, and March\"\n * * standard-short:\n *   A short version of an \"and\" list, suitable for use with short or abbreviated placeholder values.\n *   e.g. \"Jan., Feb., and Mar.\"\n * * or:\n *   A typical \"or\" list for arbitrary placeholders.\n *   e.g. \"January, February, or March\"\n * * or-short:\n *   A short version of an \"or\" list.\n *   e.g. \"Jan., Feb., or Mar.\"\n * * unit:\n *   A list suitable for wide units.\n *   e.g. \"3 feet, 7 inches\"\n * * unit-short:\n *   A list suitable for short units\n *   e.g. \"3 ft, 7 in\"\n * * unit-narrow:\n *   A list suitable for narrow units, where space on the screen is very limited.\n *   e.g. \"3\u2032 7\u2033\"\n *\n * See https://www.unicode.org/reports/tr35/tr35-49/tr35-general.html#ListPatterns for more details.\n *\n * @param {string[]} list The array of values to format into a list.\n * @param {Object} [param0]\n * @param {string} [param0.localeCode] The locale to use (e.g. en-US).\n * @param {\"standard\"|\"standard-short\"|\"or\"|\"or-short\"|\"unit\"|\"unit-short\"|\"unit-narrow\"} [param0.style=\"standard\"] The style to format the list with.\n * @returns {string} The formatted list.\n */\nexport function formatList(list, { localeCode = \"\", style = \"standard\" } = {}) {\n    const locale = localeCode || user.lang || \"en-US\";\n    const formatter = new Intl.ListFormat(locale, LIST_STYLES[style]);\n    return formatter.format(list);\n}\n", "/**\n * Converts a locale from JavaScript to Python format.\n *\n * Most of the time the conversion is simply to replace - with _.\n * Example: fr-BE \u2192 fr_BE\n *\n * Exceptions:\n *  - Serbian can be written in both Latin and Cyrillic scripts interchangeably,\n *  therefore its locale includes a special modifier to indicate which script to\n *  use. Example: sr-Latn \u2192 sr@latin\n *  - Tagalog/Filipino: The \"fil\" locale is replaced by \"tl\" for compatibility\n *  with the Python side (where the \"fil\" locale doesn't exist).\n *\n * BCP 47 (JS):\n *  language[-extlang][-script][-region][-variant][-extension][-privateuse]\n *  https://www.ietf.org/rfc/rfc5646.txt\n * XPG syntax (Python):\n *  language[_territory][.codeset][@modifier]\n *  https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html\n *\n * @param {string} locale The locale formatted for use on the JavaScript-side.\n * @returns {string} The locale formatted for use on the Python-side.\n */\nexport function jsToPyLocale(locale) {\n    if (!locale) {\n        return \"\";\n    }\n    try {\n        var { language, script, region } = new Intl.Locale(locale);\n        // new Intl.Locale(\"tl-PH\") produces fil-PH, which one might not expect\n        if (language === \"fil\") {\n            language = \"tl\";\n        }\n    } catch {\n        return locale;\n    }\n    let xpgLocale = language;\n    if (region) {\n        xpgLocale += `_${region}`;\n    }\n    switch (script) {\n        case \"Cyrl\":\n            xpgLocale += \"@Cyrl\";\n            break;\n        case \"Latn\":\n            xpgLocale += \"@latin\";\n            break;\n    }\n    return xpgLocale;\n}\n\n/**\n * Converts a locale from Python to JavaScript format.\n *\n * Most of the time the conversion is simply to replace _ with -.\n * Example: fr_BE \u2192 fr-BE\n *\n * Exception: Serbian can be written in both Latin and Cyrillic scripts\n * interchangeably, therefore its locale includes a special modifier\n * to indicate which script to use.\n * Example: sr@latin \u2192 sr-Latn\n *\n * BCP 47 (JS):\n *  language[-extlang][-script][-region][-variant][-extension][-privateuse]\n *  https://www.ietf.org/rfc/rfc5646.txt\n * XPG syntax (Python):\n *  language[_territory][.codeset][@modifier]\n *  https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html\n *\n * @param {string} locale The locale formatted for use on the Python-side.\n * @returns {string} The locale formatted for use on the JavaScript-side.\n */\nexport function pyToJsLocale(locale) {\n    if (!locale) {\n        return \"\";\n    }\n    const regex = /^([a-z]+)(_[A-Z\\d]+)?(@.+)?$/;\n    const match = locale.match(regex);\n    if (!match) {\n        return locale;\n    }\n    const [, language, territory, modifier] = match;\n    const subtags = [language];\n    switch (modifier) {\n        case \"@Cyrl\":\n            subtags.push(\"Cyrl\");\n            break;\n        case \"@latin\":\n            subtags.push(\"Latn\");\n            break;\n    }\n    if (territory) {\n        subtags.push(territory.slice(1));\n    }\n    return subtags.join(\"-\");\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { isVisible } from \"@web/core/utils/ui\";\nimport { delay, Mutex } from \"@web/core/utils/concurrency\";\nimport { validate } from \"@odoo/owl\";\n\nconst macroSchema = {\n    name: { type: String, optional: true },\n    checkDelay: { type: Number, optional: true }, //Delay before checking if element is in DOM.\n    stepDelay: { type: Number, optional: true }, //Wait this delay between steps\n    timeout: { type: Number, optional: true },\n    steps: {\n        type: Array,\n        element: {\n            initialDelay: { type: Function, optional: true },\n            action: { type: Function },\n            trigger: { type: [Function, String], optional: true },\n            timeout: { type: Number, optional: true },\n        },\n    },\n    onComplete: { type: Function, optional: true },\n    onStep: { type: Function, optional: true },\n    onError: { type: Function, optional: true },\n    onTimeout: { type: Function, optional: true },\n};\n\n/**\n * @typedef MacroStep\n * @property {string} [trigger]\n * - An action returning a \"truthy\" value means that the step isn't successful.\n * - Current step index won't be incremented.\n * @property {string | (el: Element, step: MacroStep) => undefined | string} [action]\n * @property {*} [*] - any payload to the step.\n *\n * @typedef MacroDescriptor\n * @property {() => Element | undefined} trigger\n * @property {() => {}} action\n */\n\nexport const ACTION_HELPERS = {\n    click(el, _step) {\n        el.dispatchEvent(new MouseEvent(\"mouseover\"));\n        el.dispatchEvent(new MouseEvent(\"mouseenter\"));\n        el.dispatchEvent(new MouseEvent(\"mousedown\"));\n        el.dispatchEvent(new MouseEvent(\"mouseup\"));\n        el.click();\n        el.dispatchEvent(new MouseEvent(\"mouseout\"));\n        el.dispatchEvent(new MouseEvent(\"mouseleave\"));\n    },\n    text(el, step) {\n        // simulate an input (probably need to add keydown/keyup events)\n        this.click(el, step);\n        el.value = step.value;\n        el.dispatchEvent(new InputEvent(\"input\", { bubbles: true }));\n        el.dispatchEvent(new InputEvent(\"change\", { bubbles: true }));\n    },\n};\n\nconst mutex = new Mutex();\n\nclass TimeoutError extends Error {}\n\nexport class Macro {\n    currentIndex = 0;\n    isComplete = false;\n    calledBack = false;\n    constructor(descr) {\n        try {\n            validate(descr, macroSchema);\n        } catch (error) {\n            throw new Error(\n                `Error in schema for Macro ${JSON.stringify(descr, null, 4)}\\n${error.message}`\n            );\n        }\n        Object.assign(this, descr);\n        this.name = this.name || \"anonymous\";\n        this.onComplete = this.onComplete || (() => {});\n        this.onStep = this.onStep || (() => {});\n        this.stepElFound = new Array(this.steps.length).fill(false);\n        this.stepHasStarted = new Array(this.steps.length).fill(false);\n        this.observer = new MacroMutationObserver(() => this.debounceAdvance(\"mutation\"));\n    }\n\n    async start(target = document) {\n        this.observer.observe(target);\n        this.debounceAdvance(\"next\");\n    }\n\n    getDebounceDelay() {\n        let delay = Math.max(this.checkDelay ?? 750, 50);\n        // Called only once per step.\n        if (!this.stepHasStarted[this.currentIndex]) {\n            delay = this.currentIndex === 0 ? 0 : 50;\n            this.stepHasStarted[this.currentIndex] = true;\n            if (this.currentStep?.initialDelay) {\n                const initialDelay = parseFloat(this.currentStep.initialDelay());\n                delay = initialDelay >= 0 ? initialDelay : delay;\n            }\n        }\n        return delay;\n    }\n\n    async advance() {\n        if (this.isComplete) {\n            return;\n        }\n        if (this.currentStep.trigger) {\n            this.setTimer();\n        }\n        let proceedToAction = true;\n        if (this.currentStep.trigger) {\n            proceedToAction = this.findTrigger();\n        }\n        if (proceedToAction) {\n            this.safeCall(this.onStep, this.currentElement, this.currentStep);\n            this.clearTimer();\n            const actionResult = await this.performAction();\n            if (!actionResult) {\n                // If falsy action result, it means the action worked properly.\n                // So we can proceed to the next step.\n                this.increment();\n                this.debounceAdvance(\"next\");\n            }\n        }\n    }\n\n    /**\n     * Find the trigger and assess whether it can continue on performing the actions.\n     * @returns {boolean}\n     */\n    findTrigger() {\n        if (this.isComplete) {\n            return;\n        }\n        const trigger = this.currentStep.trigger;\n        try {\n            if (typeof trigger === \"function\") {\n                this.currentElement = this.safeCall(trigger);\n            } else if (typeof trigger === \"string\") {\n                const triggerEl = document.querySelector(trigger);\n                this.currentElement = isVisible(triggerEl) && triggerEl;\n            } else {\n                throw new Error(`Trigger can only be string or function.`);\n            }\n        } catch (error) {\n            this.stop(`Error when trying to find trigger: ${error.message}`);\n        }\n        return !!this.currentElement;\n    }\n\n    /**\n     * Calls the `step.action` expecting no return to be successful.\n     */\n    async performAction() {\n        let actionResult;\n        try {\n            const action = this.currentStep.action;\n            if (action in ACTION_HELPERS) {\n                actionResult = ACTION_HELPERS[action](this.currentElement, this.currentStep);\n            } else if (typeof action === \"function\") {\n                actionResult = await this.safeCall(action, this.currentElement);\n            }\n        } catch (error) {\n            this.stop(`ERROR IN ACTION: ${error.message}`);\n        }\n        return actionResult;\n    }\n\n    get currentStep() {\n        return this.steps[this.currentIndex];\n    }\n\n    get currentElement() {\n        return this.stepElFound[this.currentIndex];\n    }\n\n    set currentElement(value) {\n        this.stepElFound[this.currentIndex] = value;\n    }\n\n    increment() {\n        this.currentIndex++;\n        if (this.currentIndex >= this.steps.length) {\n            this.stop();\n        }\n    }\n\n    safeCall(fn, ...args) {\n        if (this.isComplete) {\n            return;\n        }\n        try {\n            return fn(...args);\n        } catch (e) {\n            this.stop(e);\n        }\n    }\n\n    /**\n     * Timer for findTrigger only (not for doing action)\n     */\n    setTimer() {\n        this.clearTimer();\n        const timeout = this.currentStep.timeout || this.timeout;\n        if (timeout > 0) {\n            this.timer = browser.setTimeout(() => {\n                this.stop(new TimeoutError(timeout));\n            }, timeout);\n        }\n    }\n\n    clearTimer() {\n        this.resetDebounce();\n        if (this.timer) {\n            browser.clearTimeout(this.timer);\n        }\n    }\n\n    resetDebounce() {\n        if (this.debouncedAdvance) {\n            browser.clearTimeout(this.debouncedAdvance);\n        }\n    }\n\n    /**\n     * @param {\"next\"|\"mutation\"} from\n     */\n    async debounceAdvance(from) {\n        this.resetDebounce();\n        // Make sure to take the only possible path.\n        // A step always starts with \"next\".\n        // A step can only be continued with \"mutation\".\n        // We abort when the macro is finished or if a mutex occurs afterwards.\n        if (\n            this.isComplete ||\n            (from === \"next\" && this.stepHasStarted[this.currentIndex]) ||\n            (from === \"mutation\" && !this.stepHasStarted[this.currentIndex]) ||\n            (from === \"mutation\" && this.currentElement)\n        ) {\n            return;\n        }\n        // When browser refresh just after the last step.\n        if (!this.currentStep && this.currentIndex === 0) {\n            await delay(300);\n            this.stop();\n        } else if (from === \"next\" && !this.currentStep.trigger) {\n            this.advance();\n        } else {\n            this.debouncedAdvance = browser.setTimeout(\n                () => mutex.exec(() => this.advance()),\n                this.getDebounceDelay()\n            );\n        }\n    }\n\n    stop(error) {\n        this.clearTimer();\n        this.isComplete = true;\n        this.observer.disconnect();\n        if (!this.calledBack) {\n            this.calledBack = true;\n            if (error) {\n                if (error instanceof TimeoutError) {\n                    if (typeof this.onTimeout === \"function\") {\n                        this.onTimeout(error.message, this.currentStep, this.currentIndex);\n                    } else {\n                        console.error(\"Step timeout\");\n                    }\n                } else {\n                    if (typeof this.onError === \"function\") {\n                        this.onError(error, this.currentStep, this.currentIndex);\n                    } else {\n                        console.error(error);\n                    }\n                }\n            } else if (this.currentIndex === this.steps.length) {\n                mutex.getUnlockedDef().then(() => {\n                    this.onComplete();\n                });\n            }\n        }\n        return;\n    }\n}\n\nexport class MacroMutationObserver {\n    observerOptions = {\n        attributes: true,\n        childList: true,\n        subtree: true,\n        characterData: true,\n    };\n    constructor(callback) {\n        this.callback = callback;\n        this.observer = new MutationObserver((mutationList, observer) => {\n            callback();\n            mutationList.forEach((mutationRecord) =>\n                Array.from(mutationRecord.addedNodes).forEach((node) => {\n                    let iframes = [];\n                    if (String(node.tagName).toLowerCase() === \"iframe\") {\n                        iframes = [node];\n                    } else if (node instanceof HTMLElement) {\n                        iframes = Array.from(node.querySelectorAll(\"iframe\"));\n                    }\n                    iframes.forEach((iframeEl) =>\n                        this.observeIframe(iframeEl, observer, () => callback())\n                    );\n                    this.findAllShadowRoots(node).forEach((shadowRoot) =>\n                        observer.observe(shadowRoot, this.observerOptions)\n                    );\n                })\n            );\n        });\n    }\n    disconnect() {\n        this.observer.disconnect();\n    }\n    findAllShadowRoots(node, shadowRoots = []) {\n        if (node.shadowRoot) {\n            shadowRoots.push(node.shadowRoot);\n            this.findAllShadowRoots(node.shadowRoot, shadowRoots);\n        }\n        node.childNodes.forEach((child) => {\n            this.findAllShadowRoots(child, shadowRoots);\n        });\n        return shadowRoots;\n    }\n    observe(target) {\n        this.observer.observe(target, this.observerOptions);\n        //When iframes already exist at \"this.target\" initialization\n        target\n            .querySelectorAll(\"iframe\")\n            .forEach((el) => this.observeIframe(el, this.observer, () => this.callback()));\n        //When shadowDom already exist at \"this.target\" initialization\n        this.findAllShadowRoots(target).forEach((shadowRoot) => {\n            this.observer.observe(shadowRoot, this.observerOptions);\n        });\n    }\n    observeIframe(iframeEl, observer, callback) {\n        const observerOptions = {\n            attributes: true,\n            childList: true,\n            subtree: true,\n            characterData: true,\n        };\n        const observeIframeContent = () => {\n            if (iframeEl.contentDocument) {\n                iframeEl.contentDocument.addEventListener(\"load\", (event) => {\n                    callback();\n                    observer.observe(event.target, observerOptions);\n                });\n                if (!iframeEl.src || iframeEl.contentDocument.readyState === \"complete\") {\n                    callback();\n                    observer.observe(iframeEl.contentDocument, observerOptions);\n                }\n            }\n        };\n        observeIframeContent();\n        iframeEl.addEventListener(\"load\", observeIframeContent);\n    }\n}\n", "import { Component, xml } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\nimport { useRegistry } from \"@web/core/registry_hook\";\nimport { ErrorHandler } from \"@web/core/utils/components\";\n\nconst mainComponents = registry.category(\"main_components\");\n\nmainComponents.addValidation({\n    Component: { validate: (c) => c.prototype instanceof Component },\n    props: { type: Object, optional: true }\n});\n\nexport class MainComponentsContainer extends Component {\n    static components = { ErrorHandler };\n    static props = {};\n    static template = xml`\n    <div class=\"o-main-components-container\">\n        <t t-foreach=\"Components.entries\" t-as=\"C\" t-key=\"C[0]\">\n            <ErrorHandler onError=\"error => this.handleComponentError(error, C)\">\n                <t t-component=\"C[1].Component\" t-props=\"C[1].props\"/>\n            </ErrorHandler>\n        </t>\n    </div>\n    `;\n\n    setup() {\n        this.Components = useRegistry(mainComponents);\n    }\n\n    handleComponentError(error, C) {\n        // remove the faulty component and rerender without it\n        this.Components.entries.splice(this.Components.entries.indexOf(C), 1);\n        this.render();\n        /**\n         * we rethrow the error to notify the user something bad happened.\n         * We do it after a tick to make sure owl can properly finish its\n         * rendering\n         */\n        Promise.resolve().then(() => {\n            throw error;\n        });\n    }\n}\n", "import { Component, onWillStart, onWillUpdateProps, useState } from \"@odoo/owl\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { ModelFieldSelectorPopover } from \"./model_field_selector_popover\";\nimport { useLoadFieldInfo, useLoadPathDescription } from \"./utils\";\nimport { usePopover } from \"@web/core/popover/popover_hook\";\n\nexport class ModelFieldSelector extends Component {\n    static template = \"web._ModelFieldSelector\";\n    static components = {\n        Popover: ModelFieldSelectorPopover,\n    };\n    static props = {\n        resModel: String,\n        path: { optional: true },\n        allowEmpty: { type: Boolean, optional: true },\n        readonly: { type: Boolean, optional: true },\n        showSearchInput: { type: Boolean, optional: true },\n        isDebugMode: { type: Boolean, optional: true },\n        update: { type: Function, optional: true },\n        filter: { type: Function, optional: true },\n        followRelations: { type: Boolean, optional: true },\n        showDebugInput: { type: Boolean, optional: true },\n    };\n    static defaultProps = {\n        readonly: true,\n        allowEmpty: false,\n        isDebugMode: false,\n        showSearchInput: true,\n        update: () => {},\n        followRelations: true,\n    };\n\n    setup() {\n        this.loadPathDescription = useLoadPathDescription();\n        const loadFieldInfo = useLoadFieldInfo();\n        this.popover = usePopover(this.constructor.components.Popover, {\n            popoverClass: \"o_popover_field_selector\",\n            onClose: async () => {\n                if (this.newPath !== null) {\n                    const fieldInfo = await loadFieldInfo(this.props.resModel, this.newPath);\n                    this.props.update(this.newPath, fieldInfo);\n                }\n            },\n        });\n        this.keepLast = new KeepLast();\n        this.state = useState({ isInvalid: false, displayNames: [] });\n        onWillStart(() => this.updateState(this.props));\n        onWillUpdateProps((nextProps) => this.updateState(nextProps));\n    }\n\n    openPopover(currentTarget) {\n        if (this.props.readonly) {\n            return;\n        }\n        this.newPath = null;\n        this.popover.open(currentTarget, {\n            resModel: this.props.resModel,\n            path: this.props.path,\n            update: (path, _fieldInfo, debug = false) => {\n                this.newPath = path;\n                if (!debug) {\n                    this.updateState({ ...this.props, path }, true);\n                }\n            },\n            showSearchInput: this.props.showSearchInput,\n            isDebugMode: this.props.isDebugMode,\n            filter: this.props.filter,\n            followRelations: this.props.followRelations,\n            showDebugInput: this.props.showDebugInput,\n        });\n    }\n\n    async updateState(params, isConcurrent) {\n        const { resModel, path, allowEmpty } = params;\n        let prom = this.loadPathDescription(resModel, path, allowEmpty);\n        if (isConcurrent) {\n            prom = this.keepLast.add(prom);\n        }\n        const state = await prom;\n        Object.assign(this.state, state);\n    }\n\n    clear() {\n        if (this.popover.isOpen) {\n            this.newPath = \"\";\n            this.popover.close();\n            return;\n        }\n        this.props.update(\"\", { resModel: this.props.resModel, fieldDef: null });\n    }\n}\n", "import { Component, onWillStart, useEffect, useRef, useState } from \"@odoo/owl\";\nimport { debounce } from \"@web/core/utils/timing\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { fuzzyLookup } from \"@web/core/utils/search\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { sortBy } from \"@web/core/utils/arrays\";\nimport { useService } from \"@web/core/utils/hooks\";\n\nclass Page {\n    constructor(resModel, fieldDefs, options = {}) {\n        this.resModel = resModel;\n        this.fieldDefs = fieldDefs;\n        const { previousPage = null, selectedName = null, isDebugMode } = options;\n        this.previousPage = previousPage;\n        this.selectedName = selectedName;\n        this.isDebugMode = isDebugMode;\n        this.sortedFieldNames = sortBy(Object.keys(fieldDefs), (key) => fieldDefs[key].string);\n        this.fieldNames = this.sortedFieldNames;\n        this.query = \"\";\n        this.focusedFieldName = null;\n        this.resetFocusedFieldName();\n    }\n\n    get path() {\n        const previousPath = this.previousPage?.path || \"\";\n        if (this.selectedName) {\n            if (previousPath) {\n                return `${previousPath}.${this.selectedName}`;\n            } else {\n                return this.selectedName;\n            }\n        }\n        return previousPath;\n    }\n\n    get selectedField() {\n        return this.fieldDefs[this.selectedName];\n    }\n\n    get title() {\n        const prefix = this.previousPage?.previousPage ? \"... > \" : \"\";\n        const title = this.previousPage?.selectedField.string || \"\";\n        if (prefix.length || title.length) {\n            return `${prefix}${title}`;\n        }\n        return _t(\"Select a field\");\n    }\n\n    focus(direction) {\n        if (!this.fieldNames.length) {\n            return;\n        }\n        const index = this.fieldNames.indexOf(this.focusedFieldName);\n        if (direction === \"previous\") {\n            if (index === 0) {\n                this.focusedFieldName = this.fieldNames[this.fieldNames.length - 1];\n            } else {\n                this.focusedFieldName = this.fieldNames[index - 1];\n            }\n        } else {\n            if (index === this.fieldNames.length - 1) {\n                this.focusedFieldName = this.fieldNames[0];\n            } else {\n                this.focusedFieldName = this.fieldNames[index + 1];\n            }\n        }\n    }\n\n    resetFocusedFieldName() {\n        if (this.selectedName && this.fieldNames.includes(this.selectedName)) {\n            this.focusedFieldName = this.selectedName;\n        } else {\n            this.focusedFieldName = this.fieldNames.length ? this.fieldNames[0] : null;\n        }\n    }\n\n    searchFields(query = \"\") {\n        this.query = query;\n        this.fieldNames = this.sortedFieldNames;\n        if (query) {\n            this.fieldNames = fuzzyLookup(query, this.fieldNames, (key) => {\n                const vals = [this.fieldDefs[key].string];\n                if (this.isDebugMode) {\n                    vals.push(key);\n                }\n                return vals;\n            });\n        }\n        this.resetFocusedFieldName();\n    }\n}\n\nexport class ModelFieldSelectorPopover extends Component {\n    static template = \"web.ModelFieldSelectorPopover\";\n    static props = {\n        close: Function,\n        filter: { type: Function, optional: true },\n        followRelations: { type: Boolean, optional: true },\n        showDebugInput: { type: Boolean, optional: true },\n        isDebugMode: { type: Boolean, optional: true },\n        path: { optional: true },\n        resModel: String,\n        showSearchInput: { type: Boolean, optional: true },\n        update: Function,\n    };\n    static defaultProps = {\n        filter: (fieldDef) => fieldDef.searchable,\n        isDebugMode: false,\n        followRelations: true,\n    };\n\n    setup() {\n        this.fieldService = useService(\"field\");\n        this.state = useState({ page: null });\n        this.keepLast = new KeepLast();\n        this.debouncedSearchFields = debounce(this.searchFields.bind(this), 250);\n\n        onWillStart(async () => {\n            this.state.page = await this.loadPages(this.props.resModel, this.props.path);\n        });\n\n        const rootRef = useRef(\"root\");\n        useEffect(() => {\n            const focusedElement = rootRef.el.querySelector(\n                \".o_model_field_selector_popover_item.active\"\n            );\n            if (focusedElement) {\n                // current page can be empty (e.g. after a search)\n                focusedElement.scrollIntoView({ block: \"center\" });\n            }\n        });\n        useEffect(\n            () => {\n                if (this.props.showSearchInput) {\n                    const searchInput = rootRef.el.querySelector(\n                        \".o_model_field_selector_popover_search .o_input\"\n                    );\n                    searchInput.focus();\n                }\n            },\n            () => [this.state.page]\n        );\n    }\n\n    get showDebugInput() {\n        return this.props.showDebugInput ?? this.props.isDebugMode;\n    }\n\n    filter(fieldDefs, path) {\n        const filteredKeys = Object.keys(fieldDefs).filter((k) =>\n            this.props.filter(fieldDefs[k], path)\n        );\n        return Object.fromEntries(filteredKeys.map((k) => [k, fieldDefs[k]]));\n    }\n\n    async followRelation(fieldDef) {\n        const { modelsInfo } = await this.keepLast.add(\n            this.fieldService.loadPath(this.state.page.resModel, `${fieldDef.name}.*`)\n        );\n        this.state.page.selectedName = fieldDef.name;\n        const { resModel, fieldDefs } = modelsInfo.at(-1);\n        this.openPage(\n            new Page(resModel, this.filter(fieldDefs, this.state.page.path), {\n                previousPage: this.state.page,\n                isDebugMode: this.props.isDebugMode,\n            })\n        );\n    }\n\n    goToPreviousPage() {\n        this.keepLast.add(Promise.resolve());\n        this.openPage(this.state.page.previousPage);\n    }\n\n    async loadNewPath(path) {\n        const newPage = await this.keepLast.add(this.loadPages(this.props.resModel, path));\n        this.openPage(newPage);\n    }\n\n    async loadPages(resModel, path) {\n        if (typeof path !== \"string\" || !path.length) {\n            const fieldDefs = await this.fieldService.loadFields(resModel);\n            return new Page(resModel, this.filter(fieldDefs, path), {\n                isDebugMode: this.props.isDebugMode,\n            });\n        }\n        const { isInvalid, modelsInfo, names } = await this.fieldService.loadPath(resModel, path);\n        switch (isInvalid) {\n            case \"model\":\n                throw new Error(`Invalid model name: ${resModel}`);\n            case \"path\": {\n                const { resModel, fieldDefs } = modelsInfo[0];\n                return new Page(resModel, this.filter(fieldDefs, path), {\n                    selectedName: path,\n                    isDebugMode: this.props.isDebugMode,\n                });\n            }\n            default: {\n                let page = null;\n                for (let index = 0; index < names.length; index++) {\n                    const name = names[index];\n                    const { resModel, fieldDefs } = modelsInfo[index];\n                    page = new Page(resModel, this.filter(fieldDefs, path), {\n                        previousPage: page,\n                        selectedName: name,\n                        isDebugMode: this.props.isDebugMode,\n                    });\n                }\n                return page;\n            }\n        }\n    }\n\n    openPage(page) {\n        this.state.page = page;\n        this.state.page.searchFields();\n        this.props.update(page.path);\n    }\n\n    searchFields(query) {\n        this.state.page.searchFields(query);\n    }\n\n    selectField(field) {\n        if (field.type === \"properties\") {\n            return this.followRelation(field);\n        }\n        this.keepLast.add(Promise.resolve());\n        this.state.page.selectedName = field.name;\n        this.props.update(this.state.page.path, field);\n        this.props.close(true);\n    }\n\n    onDebugInputKeydown(ev) {\n        switch (ev.key) {\n            case \"Enter\": {\n                ev.preventDefault();\n                ev.stopPropagation();\n                this.loadNewPath(ev.currentTarget.value);\n                break;\n            }\n        }\n    }\n\n    // @TODO should rework/improve this and maybe use hotkeys\n    async onInputKeydown(ev) {\n        const { page } = this.state;\n        switch (ev.key) {\n            case \"ArrowUp\": {\n                if (ev.target.selectionStart === 0) {\n                    page.focus(\"previous\");\n                }\n                break;\n            }\n            case \"ArrowDown\": {\n                if (ev.target.selectionStart === page.query.length) {\n                    page.focus(\"next\");\n                }\n                break;\n            }\n            case \"ArrowLeft\": {\n                if (ev.target.selectionStart === 0 && page.previousPage) {\n                    this.goToPreviousPage();\n                }\n                break;\n            }\n            case \"ArrowRight\": {\n                if (ev.target.selectionStart === page.query.length) {\n                    const focusedFieldName = this.state.page.focusedFieldName;\n                    if (focusedFieldName) {\n                        const fieldDef = this.state.page.fieldDefs[focusedFieldName];\n                        if (fieldDef.relation || fieldDef.type === \"properties\") {\n                            this.followRelation(fieldDef);\n                        }\n                    }\n                }\n                break;\n            }\n            case \"Enter\": {\n                const focusedFieldName = this.state.page.focusedFieldName;\n                if (focusedFieldName) {\n                    const fieldDef = this.state.page.fieldDefs[focusedFieldName];\n                    this.selectField(fieldDef);\n                } else {\n                    ev.preventDefault();\n                    ev.stopPropagation();\n                }\n                break;\n            }\n            case \"Escape\": {\n                ev.preventDefault();\n                ev.stopPropagation();\n                this.props.close();\n                break;\n            }\n        }\n    }\n}\n", "import { useService } from \"@web/core/utils/hooks\";\n\nfunction makeString(value) {\n    return String(value ?? \"-\");\n}\n\nexport function useLoadFieldInfo(fieldService) {\n    fieldService ||= useService(\"field\");\n    return async (resModel, path) => {\n        if (typeof path !== \"string\" || !path) {\n            return { resModel, fieldDef: null };\n        }\n        const { isInvalid, names, modelsInfo } = await fieldService.loadPath(resModel, path);\n        if (isInvalid) {\n            return { resModel, fieldDef: null };\n        }\n        const name = names.at(-1);\n        const modelInfo = modelsInfo.at(-1);\n        return { resModel: modelInfo.resModel, fieldDef: modelInfo.fieldDefs[name] };\n    };\n}\n\nexport function useLoadPathDescription(fieldService) {\n    fieldService ||= useService(\"field\");\n    return async (resModel, path, allowEmpty) => {\n        if ([0, 1].includes(path)) {\n            return { isInvalid: false, displayNames: [makeString(path)] };\n        }\n        if (allowEmpty && !path) {\n            return { isInvalid: false, displayNames: [] };\n        }\n        if (typeof path !== \"string\" || !path) {\n            return { isInvalid: true, displayNames: [makeString()] };\n        }\n        const { isInvalid, modelsInfo, names } = await fieldService.loadPath(resModel, path);\n        const result = { isInvalid: !!isInvalid, displayNames: [] };\n        if (!isInvalid) {\n            const lastName = names.at(-1);\n            const lastFieldDef = modelsInfo.at(-1).fieldDefs[lastName];\n            if ([\"properties\", \"properties_definition\"].includes(lastFieldDef.type)) {\n                // there is no known case where we want to select a 'properties' field directly\n                result.isInvalid = true;\n            }\n        }\n        for (let index = 0; index < names.length; index++) {\n            const name = names[index];\n            const fieldDef = modelsInfo[index]?.fieldDefs[name];\n            result.displayNames.push(fieldDef?.string || makeString(name));\n        }\n        return result;\n    };\n}\n", "import { AutoComplete } from \"@web/core/autocomplete/autocomplete\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { fuzzyLookup } from \"@web/core/utils/search\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nimport { Component, onWillStart } from \"@odoo/owl\";\n\nexport class ModelSelector extends Component {\n    static template = \"web.ModelSelector\";\n    static components = { AutoComplete };\n    static props = {\n        onModelSelected: Function,\n        id: { type: String, optional: true },\n        value: { type: String, optional: true },\n        // list of models technical name, if not set\n        // we will fetch all models we have access to\n        models: { type: Array, optional: true },\n    };\n\n    setup() {\n        this.orm = useService(\"orm\");\n\n        onWillStart(async () => {\n            if (!this.props.models) {\n                this.models = await this._fetchAvailableModels();\n            } else {\n                this.models = await this.orm.call(\"ir.model\", \"display_name_for\", [\n                    this.props.models,\n                ]);\n            }\n\n            this.models = this.models.map((record) => ({\n                label: record.display_name,\n                technical: record.model,\n                classList: {\n                    [`o_model_selector_${record.model}`]: 1,\n                },\n            }));\n        });\n    }\n\n    get sources() {\n        return [this.optionsSource];\n    }\n    get optionsSource() {\n        return {\n            placeholder: _t(\"Loading...\"),\n            options: this.loadOptionsSource.bind(this),\n        };\n    }\n\n    onSelect(option) {\n        this.props.onModelSelected({\n            label: option.label,\n            technical: option.technical,\n        });\n    }\n\n    filterModels(name) {\n        if (!name) {\n            const visibleModels = this.models.slice(0, 8);\n            if (this.models.length - visibleModels.length > 0) {\n                visibleModels.push({\n                    label: _t(\"Start typing...\"),\n                    unselectable: true,\n                    classList: \"o_m2o_start_typing\",\n                });\n            }\n            return visibleModels;\n        }\n        return fuzzyLookup(name, this.models, (model) => model.technical + model.label);\n    }\n\n    loadOptionsSource(request) {\n        const options = this.filterModels(request);\n\n        if (!options.length) {\n            options.push({\n                label: _t(\"No records\"),\n                classList: \"o_m2o_no_result\",\n                unselectable: true,\n            });\n        }\n        return options;\n    }\n\n    /**\n     * Fetch the list of the models that can be\n     * selected for the relational properties.\n     */\n    async _fetchAvailableModels() {\n        const result = await this.orm.call(\"ir.model\", \"get_available_models\");\n        return result || [];\n    }\n}\n", "import { registry } from \"@web/core/registry\";\nimport { unique, zip } from \"@web/core/utils/arrays\";\nimport { Deferred } from \"@web/core/utils/concurrency\";\n\nexport const ERROR_INACCESSIBLE_OR_MISSING = Symbol(\"INACCESSIBLE OR MISSING RECORD ID\");\n\nfunction isId(val) {\n    return Number.isInteger(val) && val >= 1;\n}\n\n/**\n * @typedef {Record<string, (string|ERROR_INACCESSIBLE_OR_MISSING)>} DisplayNames\n */\n\nexport const nameService = {\n    dependencies: [\"orm\"],\n    async: [\"loadDisplayNames\"],\n    start(env, { orm }) {\n        let cache = {};\n        const batches = {};\n\n        function clearCache() {\n            cache = {};\n        }\n\n        env.bus.addEventListener(\"ACTION_MANAGER:UPDATE\", clearCache);\n\n        function getMapping(resModel) {\n            if (!cache[resModel]) {\n                cache[resModel] = {};\n            }\n            return cache[resModel];\n        }\n\n        /**\n         * @param {string} resModel valid resModel name\n         * @param {DisplayNames} displayNames\n         */\n        function addDisplayNames(resModel, displayNames) {\n            const mapping = getMapping(resModel);\n            for (const resId in displayNames) {\n                mapping[resId] = new Deferred();\n                mapping[resId].resolve(displayNames[resId]);\n            }\n        }\n\n        /**\n         * @param {string} resModel valid resModel name\n         * @param {number[]} resIds valid ids\n         * @returns {Promise<DisplayNames>}\n         */\n        async function loadDisplayNames(resModel, resIds) {\n            const mapping = getMapping(resModel);\n            const proms = [];\n            const resIdsToFetch = [];\n            for (const resId of unique(resIds)) {\n                if (!isId(resId)) {\n                    throw new Error(`Invalid ID: ${resId}`);\n                }\n                if (!(resId in mapping)) {\n                    mapping[resId] = new Deferred();\n                    resIdsToFetch.push(resId);\n                }\n                proms.push(mapping[resId]);\n            }\n            if (resIdsToFetch.length) {\n                if (batches[resModel]) {\n                    batches[resModel].push(...resIdsToFetch);\n                } else {\n                    batches[resModel] = resIdsToFetch;\n                    await Promise.resolve();\n                    const idsInBatch = unique(batches[resModel]);\n                    delete batches[resModel];\n\n                    const specification = { display_name: {} };\n                    orm.silent\n                        .webSearchRead(resModel, [[\"id\", \"in\", idsInBatch]], { specification })\n                        .then(({ records }) => {\n                            const displayNames = Object.fromEntries(\n                                records.map((rec) => [rec.id, rec.display_name])\n                            );\n                            for (const resId of idsInBatch) {\n                                mapping[resId].resolve(\n                                    resId in displayNames\n                                        ? displayNames[resId]\n                                        : ERROR_INACCESSIBLE_OR_MISSING\n                                );\n                            }\n                        });\n                }\n            }\n            const names = await Promise.all(proms);\n            return Object.fromEntries(zip(resIds, names));\n        }\n\n        return { addDisplayNames, clearCache, loadDisplayNames };\n    },\n};\n\nregistry.category(\"services\").add(\"name\", nameService);\n", "import { useEffect, useRef } from \"@odoo/owl\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { scrollTo } from \"@web/core/utils/scrolling\";\nimport { debounce, throttleForAnimation } from \"@web/core/utils/timing\";\n\nconst ACTIVE_ELEMENT_CLASS = \"focus\";\nconst throttledElementFocus = throttleForAnimation((el) => el?.focus());\n\nfunction focusElement(el) {\n    throttledElementFocus.cancel();\n    throttledElementFocus(el);\n}\n\nclass NavigationItem {\n    constructor({ index, el, setActiveItem, options }) {\n        this.index = index;\n        this.options = options;\n        this.setActiveItem = setActiveItem;\n\n        this.el = el;\n        if (options.shouldFocusChildInput) {\n            const subInput = el.querySelector(\":scope input, :scope button, :scope textarea\");\n            this.target = subInput || el;\n        } else {\n            this.target = el;\n        }\n\n        const focus = () => this.focus(true);\n        const onMouseEnter = (ev) => this.onMouseEnter(ev);\n\n        this.target.addEventListener(\"focus\", focus);\n        this.target.addEventListener(\"mouseenter\", onMouseEnter);\n        this.removeListeners = () => {\n            this.target.removeEventListener(\"focus\", focus);\n            this.target.removeEventListener(\"mouseenter\", onMouseEnter);\n        };\n    }\n\n    select() {\n        this.focus();\n        this.target.click();\n    }\n\n    focus(skipRealFocus = false) {\n        scrollTo(this.target);\n        this.setActiveItem(this.index, this);\n        this.target.classList.add(ACTIVE_ELEMENT_CLASS);\n\n        if (!skipRealFocus && !this.options.virtualFocus) {\n            focusElement(this.target);\n        }\n    }\n\n    defocus() {\n        this.target.classList.remove(ACTIVE_ELEMENT_CLASS);\n    }\n\n    onMouseEnter() {\n        this.focus(true);\n        this.options.onMouseEnter?.(this);\n    }\n}\n\nclass Navigator {\n    /**\n     * @param {*} containerRef\n     * @param {NavigationOptions} options\n     */\n    constructor(containerRef, options, hotkeyService) {\n        this.enabled = false;\n        this.containerRef = containerRef;\n\n        const focusAt = (increment) => {\n            const isFocused = this.activeItem?.el.isConnected;\n            const index = this.currentActiveIndex + increment;\n            if (isFocused && index >= 0) {\n                return this.items[index % this.items.length]?.focus();\n            } else if (!isFocused && increment >= 0) {\n                return this.items[0]?.focus();\n            } else {\n                return this.items.at(-1)?.focus();\n            }\n        };\n\n        this.options = {\n            shouldFocusChildInput: true,\n            virtualFocus: false,\n            itemsSelector: \":scope .o-navigable\",\n            focusInitialElementOnDisabled: () => true,\n            ...options,\n\n            hotkeys: {\n                home: (index, items) => items[0]?.focus(),\n                end: (index, items) => items.at(-1)?.focus(),\n                tab: () => focusAt(+1),\n                \"shift+tab\": () => focusAt(-1),\n                arrowdown: () => focusAt(+1),\n                arrowup: () => focusAt(-1),\n                enter: (index, items) => {\n                    const item = items[index] || items[0];\n                    item?.select();\n                },\n                ...(options?.hotkeys || {}),\n            },\n        };\n\n        /**@type {Array<NavigationItem>} */\n        this.items = [];\n\n        /**@type {NavigationItem|undefined}*/\n        this.activeItem = undefined;\n        this.currentActiveIndex = -1;\n\n        this.initialFocusElement = undefined;\n        this.debouncedUpdate = debounce(() => this.update(), 100);\n\n        this.hotkeyRemoves = [];\n        this.hotkeyService = hotkeyService;\n\n        this.allowedInEditableHotkeys = [\"arrowup\", \"arrowdown\", \"enter\", \"tab\", \"shift+tab\"];\n    }\n\n    enable() {\n        if (!this.containerRef.el || this.targetObserver) {\n            return;\n        }\n\n        for (const [hotkey, callback] of Object.entries(this.options.hotkeys)) {\n            if (!callback) {\n                continue;\n            }\n\n            this.hotkeyRemoves.push(\n                this.hotkeyService.add(\n                    hotkey,\n                    () => callback(this.currentActiveIndex, this.items),\n                    {\n                        allowRepeat: true,\n                        bypassEditableProtection: this.allowedInEditableHotkeys.includes(hotkey),\n                    }\n                )\n            );\n        }\n\n        this.targetObserver = new MutationObserver(() => this.debouncedUpdate());\n        this.targetObserver.observe(this.containerRef.el, {\n            childList: true,\n            subtree: true,\n        });\n\n        this.initialFocusElement = document.activeElement;\n        this.currentActiveIndex = -1;\n        this.update();\n\n        if (this.options.onEnabled) {\n            this.options.onEnabled(this.items);\n        } else if (this.items.length > 0) {\n            this.items[0]?.focus();\n        }\n\n        this.enabled = true;\n    }\n\n    disable() {\n        if (!this.enabled) {\n            return;\n        }\n\n        if (this.targetObserver) {\n            this.targetObserver.disconnect();\n            this.targetObserver = undefined;\n        }\n\n        this.clearItems();\n        for (const removeHotkey of this.hotkeyRemoves) {\n            removeHotkey();\n        }\n        this.hotkeyRemoves = [];\n\n        if (this.options.focusInitialElementOnDisabled()) {\n            focusElement(this.initialFocusElement);\n        }\n\n        this.enabled = false;\n    }\n\n    update() {\n        if (!this.containerRef.el) {\n            return;\n        }\n        const oldItemsLength = this.items.length;\n        this.clearItems();\n\n        const elements = [...this.containerRef.el.querySelectorAll(this.options.itemsSelector)];\n        this.items = elements.map((el, index) => {\n            return new NavigationItem({\n                index,\n                el,\n                options: this.options,\n                setActiveItem: (index, el) => this.setActiveItem(index, el),\n            });\n        });\n\n        if (oldItemsLength != this.items.length && this.currentActiveIndex >= this.items.length) {\n            this.items.at(-1)?.focus();\n        }\n    }\n\n    setActiveItem(index, item) {\n        if (this.activeItem) {\n            this.activeItem.el.classList.remove(ACTIVE_ELEMENT_CLASS);\n        }\n        this.activeItem = item;\n        this.currentActiveIndex = index;\n    }\n\n    clearItems() {\n        for (const item of this.items) {\n            item.removeListeners();\n        }\n        this.items = [];\n    }\n}\n\n/**\n * @typedef {Object} NavigationOptions\n * @property {NavigationHotkeys} hotkeys\n * @property {Function} onEnabled\n * @property {Function} onMouseEnter\n * @property {Boolean} [virtualFocus=false] - If true, items are only visually\n * focused so the actual focus can be kept on another input.\n * @property {string} [itemsSelector=\":scope .o-navigable\"] - The selector used to get the list\n * of navigable elements.\n * @property {Function} focusInitialElementOnDisabled\n * @property {Boolean} [shouldFocusChildInput=false] - If true, elements like inputs or buttons\n * inside of the items are focused instead of the items themselves.\n */\n\n/**\n * @typedef {{\n *  home: keyHandlerCallback|undefined,\n *  end: keyHandlerCallback|undefined,\n *  tab: keyHandlerCallback|undefined,\n *  \"shift+tab\": keyHandlerCallback|undefined,\n *  arrowup: keyHandlerCallback|undefined,\n *  arrowdown: keyHandlerCallback|undefined,\n *  enter: keyHandlerCallback|undefined,\n *  arrowleft: keyHandlerCallback|undefined,\n *  arrowright: keyHandlerCallback|undefined,\n *  escape: keyHandlerCallback|undefined,\n *  space: keyHandlerCallback|undefined,\n * }} NavigationHotkeys\n */\n\n/**\n * Callback used to override the behaviour of a specific\n * key input.\n *\n * @callback keyHandlerCallback\n * @param {number} index                Current index.\n * @param {Array<NavigationItem>} items List of all navigation items.\n */\n\n/**\n * @typedef NavigationHook\n * @method enable\n * @method disable\n */\n\n/**\n * This hook adds keyboard navigation to items contained in an element.\n * It's purpose is to improve navigation in constrained context such\n * as dropdown and menus.\n *\n * This hook also has the following features:\n * - Hotkeys override and customization\n * - Navigation between inputs elements\n * - Optional virtual focus\n * - Focus on mouse enter\n *\n * @param {string|Object} containerRef\n * @param {NavigationOptions} options\n * @returns {NavigationHook}\n */\nexport function useNavigation(containerRef, options = {}) {\n    const hotkeyService = useService(\"hotkey\");\n    containerRef = typeof containerRef === \"string\" ? useRef(containerRef) : containerRef;\n    const navigator = new Navigator(containerRef, options, hotkeyService);\n\n    useEffect(\n        (container) => {\n            if (container) {\n                navigator.enable();\n            } else if (navigator) {\n                navigator.disable();\n            }\n        },\n        () => [containerRef.el]\n    );\n\n    return {\n        enable: () => navigator.enable(),\n        disable: () => navigator.disable(),\n    };\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { makeErrorFromResponse, ConnectionLostError } from \"@web/core/network/rpc\";\nimport { browser } from \"@web/core/browser/browser\";\n\n/* eslint-disable */\n/**\n * The following sections are from libraries, they have been slightly modified\n * to allow patching them during tests, but should not be linted, so that we can\n * keep a minimal diff that is easy to reapply when upgrading\n */\n// -----------------------------------------------------------------------------\n// Content Disposition Library\n// -----------------------------------------------------------------------------\n\n/*\n(The MIT License)\nCopyright (c) 2014-2017 Douglas Christopher Wilson\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n/**\n * Stripped down to only parsing/decoding.\n * Slightly changed for export and lint compliance\n */\n\n/**\n * RegExp to match percent encoding escape.\n * @private\n */\nconst HEX_ESCAPE_REPLACE_REGEXP = /%([0-9A-Fa-f]{2})/g;\n\n/**\n * RegExp to match non-latin1 characters.\n * @private\n */\nconst NON_LATIN1_REGEXP = /[^\\x20-\\x7e\\xa0-\\xff]/g;\n\n/**\n * RegExp to match quoted-pair in RFC 2616\n *\n * quoted-pair = \"\\\" CHAR\n * CHAR        = <any US-ASCII character (octets 0 - 127)>\n * @private\n */\nconst QESC_REGEXP = /\\\\([\\u0000-\\u007f])/g;\n\n/**\n * RegExp for various RFC 2616 grammar\n *\n * parameter     = token \"=\" ( token | quoted-string )\n * token         = 1*<any CHAR except CTLs or separators>\n * separators    = \"(\" | \")\" | \"<\" | \">\" | \"@\"\n *               | \",\" | \";\" | \":\" | \"\\\" | <\">\n *               | \"/\" | \"[\" | \"]\" | \"?\" | \"=\"\n *               | \"{\" | \"}\" | SP | HT\n * quoted-string = ( <\"> *(qdtext | quoted-pair ) <\"> )\n * qdtext        = <any TEXT except <\">>\n * quoted-pair   = \"\\\" CHAR\n * CHAR          = <any US-ASCII character (octets 0 - 127)>\n * TEXT          = <any OCTET except CTLs, but including LWS>\n * LWS           = [CRLF] 1*( SP | HT )\n * CRLF          = CR LF\n * CR            = <US-ASCII CR, carriage return (13)>\n * LF            = <US-ASCII LF, linefeed (10)>\n * SP            = <US-ASCII SP, space (32)>\n * HT            = <US-ASCII HT, horizontal-tab (9)>\n * CTL           = <any US-ASCII control character (octets 0 - 31) and DEL (127)>\n * OCTET         = <any 8-bit sequence of data>\n * @private\n */\nconst PARAM_REGEXP = /;[\\x09\\x20]*([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\\x09\\x20]*=[\\x09\\x20]*(\"(?:[\\x20!\\x23-\\x5b\\x5d-\\x7e\\x80-\\xff]|\\\\[\\x20-\\x7e])*\"|[!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\\x09\\x20]*/g;\n\n/**\n * RegExp for various RFC 5987 grammar\n *\n * ext-value     = charset  \"'\" [ language ] \"'\" value-chars\n * charset       = \"UTF-8\" / \"ISO-8859-1\" / mime-charset\n * mime-charset  = 1*mime-charsetc\n * mime-charsetc = ALPHA / DIGIT\n *               / \"!\" / \"#\" / \"$\" / \"%\" / \"&\"\n *               / \"+\" / \"-\" / \"^\" / \"_\" / \"`\"\n *               / \"{\" / \"}\" / \"~\"\n * language      = ( 2*3ALPHA [ extlang ] )\n *               / 4ALPHA\n *               / 5*8ALPHA\n * extlang       = *3( \"-\" 3ALPHA )\n * value-chars   = *( pct-encoded / attr-char )\n * pct-encoded   = \"%\" HEXDIG HEXDIG\n * attr-char     = ALPHA / DIGIT\n *               / \"!\" / \"#\" / \"$\" / \"&\" / \"+\" / \"-\" / \".\"\n *               / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n * @private\n */\nconst EXT_VALUE_REGEXP = /^([A-Za-z0-9!#$%&+\\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+.^_`|~-])+)$/;\n\n/**\n * RegExp for various RFC 6266 grammar\n *\n * disposition-type = \"inline\" | \"attachment\" | disp-ext-type\n * disp-ext-type    = token\n * disposition-parm = filename-parm | disp-ext-parm\n * filename-parm    = \"filename\" \"=\" value\n *                  | \"filename*\" \"=\" ext-value\n * disp-ext-parm    = token \"=\" value\n *                  | ext-token \"=\" ext-value\n * ext-token        = <the characters in token, followed by \"*\">\n * @private\n */\nconst DISPOSITION_TYPE_REGEXP = /^([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\\x09\\x20]*(?:$|;)/;\n\n/**\n * Decode a RFC 6987 field value (gracefully).\n *\n * @param {string} str\n * @return {string}\n * @private\n */\nfunction decodefield(str) {\n    const match = EXT_VALUE_REGEXP.exec(str);\n\n    if (!match) {\n        throw new TypeError(\"invalid extended field value\");\n    }\n\n    const charset = match[1].toLowerCase();\n    const encoded = match[2];\n\n    switch (charset) {\n        case \"iso-8859-1\":\n            return encoded\n                .replace(HEX_ESCAPE_REPLACE_REGEXP, pdecode)\n                .replace(NON_LATIN1_REGEXP, \"?\");\n        case \"utf-8\":\n            return decodeURIComponent(encoded);\n        default:\n            throw new TypeError(\"unsupported charset in extended field\");\n    }\n}\n\n/**\n * Parse Content-Disposition header string.\n *\n * @param {string} string\n * @return {ContentDisposition}\n * @public\n */\nfunction parse(string) {\n    if (!string || typeof string !== \"string\") {\n        throw new TypeError(\"argument string is required\");\n    }\n\n    let match = DISPOSITION_TYPE_REGEXP.exec(string);\n\n    if (!match) {\n        throw new TypeError(\"invalid type format\");\n    }\n\n    // normalize type\n    let index = match[0].length;\n    const type = match[1].toLowerCase();\n\n    let key;\n    const names = [];\n    const params = {};\n    let value;\n\n    // calculate index to start at\n    index = PARAM_REGEXP.lastIndex = match[0].substr(-1) === \";\" ? index - 1 : index;\n\n    // match parameters\n    while ((match = PARAM_REGEXP.exec(string))) {\n        if (match.index !== index) {\n            throw new TypeError(\"invalid parameter format\");\n        }\n\n        index += match[0].length;\n        key = match[1].toLowerCase();\n        value = match[2];\n\n        if (names.indexOf(key) !== -1) {\n            throw new TypeError(\"invalid duplicate parameter\");\n        }\n\n        names.push(key);\n\n        if (key.indexOf(\"*\") + 1 === key.length) {\n            // decode extended value\n            key = key.slice(0, -1);\n            value = decodefield(value);\n\n            // overwrite existing value\n            params[key] = value;\n            continue;\n        }\n\n        if (typeof params[key] === \"string\") {\n            continue;\n        }\n\n        if (value[0] === '\"') {\n            // remove quotes and escapes\n            value = value.substr(1, value.length - 2).replace(QESC_REGEXP, \"$1\");\n        }\n\n        params[key] = value;\n    }\n\n    if (index !== -1 && index !== string.length) {\n        throw new TypeError(\"invalid parameter format\");\n    }\n\n    return new ContentDisposition(type, params);\n}\n\n/**\n * Percent decode a single character.\n *\n * @param {string} str\n * @param {string} hex\n * @return {string}\n * @private\n */\nfunction pdecode(str, hex) {\n    return String.fromCharCode(parseInt(hex, 16));\n}\n\n/**\n * Class for parsed Content-Disposition header for v8 optimization\n *\n * @public\n * @param {string} type\n * @param {object} parameters\n * @constructor\n */\nfunction ContentDisposition(type, parameters) {\n    this.type = type;\n    this.parameters = parameters;\n}\n\n// -----------------------------------------------------------------------------\n// download.js library\n// -----------------------------------------------------------------------------\n\n/*\nMIT License\nCopyright (c) 2016 dandavis\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n */\n\n/**\n * download.js v4.2, by dandavis; 2008-2018. [MIT] see http://danml.com/download.html for tests/usage\n * v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime\n * v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs\n * v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.\n * v4 adds AMD/UMD, commonJS, and plain browser support\n * v4.1 adds url download capability via solo URL argument (same domain/CORS only)\n * v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors\n *\n * Slightly modified for export and lint compliance\n *\n * @param {Blob | File | String} data\n * @param {String} [filename]\n * @param {String} [mimetype]\n */\nfunction _download(data, filename, mimetype) {\n    let self = window, // this script is only for browsers anyway...\n        defaultMime = \"application/octet-stream\", // this default mime also triggers iframe downloads\n        mimeType = mimetype || defaultMime,\n        payload = data,\n        url = !filename && !mimetype && payload,\n        anchor = document.createElement(\"a\"),\n        toString = function (a) {\n            return String(a);\n        },\n        myBlob = self.Blob || self.MozBlob || self.WebKitBlob || toString,\n        fileName = filename || \"download\",\n        blob,\n        reader;\n    myBlob = myBlob.call ? myBlob.bind(self) : Blob;\n\n    if (String(this) === \"true\") {\n        //reverse arguments, allowing download.bind(true, \"text/xml\", \"export.xml\") to act as a callback\n        payload = [payload, mimeType];\n        mimeType = payload[0];\n        payload = payload[1];\n    }\n\n    if (url && url.length < 2048) {\n        // if no filename and no mime, assume a url was passed as the only argument\n        fileName = url.split(\"/\").pop().split(\"?\")[0];\n        anchor.href = url; // assign href prop to temp anchor\n        if (anchor.href.indexOf(url) !== -1) {\n            // if the browser determines that it's a potentially valid url path:\n            return new Promise((resolve, reject) => {\n                let xhr = new browser.XMLHttpRequest();\n                xhr.open(\"GET\", url, true);\n                configureBlobDownloadXHR(xhr, {\n                    onSuccess: resolve,\n                    onFailure: reject,\n                    url\n                });\n                xhr.send();\n            });\n        }\n    }\n\n    //go ahead and download dataURLs right away\n    if (/^data:[\\w+\\-]+\\/[\\w+\\-]+[,;]/.test(payload)) {\n        if (payload.length > 1024 * 1024 * 1.999 && myBlob !== toString) {\n            payload = dataUrlToBlob(payload);\n            mimeType = payload.type || defaultMime;\n        } else {\n            return navigator.msSaveBlob // IE10 can't do a[download], only Blobs:\n                ? navigator.msSaveBlob(dataUrlToBlob(payload), fileName)\n                : saver(payload); // everyone else can save dataURLs un-processed\n        }\n    }\n\n    blob = payload instanceof myBlob ? payload : new myBlob([payload], { type: mimeType });\n\n    function dataUrlToBlob(strUrl) {\n        let parts = strUrl.split(/[:;,]/),\n            type = parts[1],\n            decoder = parts[2] === \"base64\" ? atob : decodeURIComponent,\n            binData = decoder(parts.pop()),\n            mx = binData.length,\n            i = 0,\n            uiArr = new Uint8Array(mx);\n\n        for (i; i < mx; ++i) {\n            uiArr[i] = binData.charCodeAt(i);\n        }\n\n        return new myBlob([uiArr], { type });\n    }\n\n    function saver(url, winMode) {\n        if (\"download\" in anchor) {\n            //html5 A[download]\n            anchor.href = url;\n            anchor.setAttribute(\"download\", fileName);\n            anchor.className = \"download-js-link\";\n            anchor.innerText = _t(\"downloading...\");\n            anchor.style.display = \"none\";\n            document.body.appendChild(anchor);\n            setTimeout(() => {\n                anchor.click();\n                document.body.removeChild(anchor);\n                if (winMode === true) {\n                    setTimeout(() => {\n                        self.URL.revokeObjectURL(anchor.href);\n                    }, 250);\n                }\n            }, 66);\n            return true;\n        }\n\n        // handle non-a[download] safari as best we can:\n        if (/(Version)\\/(\\d+)\\.(\\d+)(?:\\.(\\d+))?.*Safari\\//.test(navigator.userAgent)) {\n            url = url.replace(/^data:([\\w\\/\\-+]+)/, defaultMime);\n            if (!window.open(url)) {\n                // popup blocked, offer direct download:\n                if (\n                    confirm(\n                        \"Displaying New Document\\n\\nUse Save As... to download, then click back to return to this page.\"\n                    )\n                ) {\n                    location.href = url;\n                }\n            }\n            return true;\n        }\n\n        //do iframe dataURL download (old ch+FF):\n        let f = document.createElement(\"iframe\");\n        document.body.appendChild(f);\n\n        if (!winMode) {\n            // force a mime that will download:\n            url = `data:${url.replace(/^data:([\\w\\/\\-+]+)/, defaultMime)}`;\n        }\n        f.src = url;\n        setTimeout(() => {\n            document.body.removeChild(f);\n        }, 333);\n    }\n\n    if (navigator.msSaveBlob) {\n        // IE10+ : (has Blob, but not a[download] or URL)\n        return navigator.msSaveBlob(blob, fileName);\n    }\n\n    if (self.URL) {\n        // simple fast and modern way using Blob and URL:\n        saver(self.URL.createObjectURL(blob), true);\n    } else {\n        // handle non-Blob()+non-URL browsers:\n        if (typeof blob === \"string\" || blob.constructor === toString) {\n            try {\n                return saver(`data:${mimeType};base64,${self.btoa(blob)}`);\n            } catch {\n                return saver(`data:${mimeType},${encodeURIComponent(blob)}`);\n            }\n        }\n\n        // Blob but not URL support:\n        reader = new FileReader();\n        reader.onload = function () {\n            saver(this.result);\n        };\n        reader.readAsDataURL(blob);\n    }\n    return true;\n}\n/* eslint-enable */\n\n// -----------------------------------------------------------------------------\n// Exported download functions\n// -----------------------------------------------------------------------------\n\n/**\n * Download data as a file\n *\n * @param {Object} data\n * @param {String} filename\n * @param {String} mimetype\n * @returns {Boolean}\n *\n * Note: the actual implementation is certainly unconventional, but sadly\n * necessary to be able to test code using the download function\n */\nexport function downloadFile(data, filename, mimetype) {\n    return downloadFile._download(data, filename, mimetype);\n}\ndownloadFile._download = _download;\n\n/**\n * Download a file from form or server url\n *\n * This function is meant to call a controller with some data\n * and download the response.\n *\n * Note: the actual implementation is certainly unconventional, but sadly\n * necessary to be able to test code using the download function\n *\n * @param {*} options\n * @returns {Promise<any>}\n */\nexport function download(options) {\n    return download._download(options);\n}\n\ndownload._download = (options) => {\n    return new Promise((resolve, reject) => {\n        const xhr = new browser.XMLHttpRequest();\n        let data;\n        if (Object.prototype.hasOwnProperty.call(options, \"form\")) {\n            xhr.open(options.form.method, options.form.action);\n            data = new FormData(options.form);\n        } else {\n            xhr.open(\"POST\", options.url);\n            data = new FormData();\n            Object.entries(options.data).forEach((entry) => {\n                const [key, value] = entry;\n                data.append(key, value);\n            });\n        }\n        data.append(\"token\", \"dummy-because-api-expects-one\");\n        if (odoo.csrf_token) {\n            data.append(\"csrf_token\", odoo.csrf_token);\n        }\n        configureBlobDownloadXHR(xhr, {\n            onSuccess: resolve,\n            onFailure: reject,\n            url: options.url,\n        });\n        xhr.send(data);\n    });\n};\n\n/**\n * Setup a download xhr request response handling\n * (onload, onerror, responseType), with hooks when the download succeeds or\n * fails.\n *\n * @param {XMLHttpRequest} xhr\n * @param {object} [options]\n * @param {(filename: string) => void} [options.onSuccess]\n * @param {(Error) => void} [options.onFailure]\n * @param {string} [options.url]\n */\nexport function configureBlobDownloadXHR(\n    xhr,\n    { onSuccess = () => {}, onFailure = () => {}, url } = {}\n) {\n    xhr.responseType = \"blob\";\n    xhr.onload = () => {\n        const mimetype = xhr.response.type;\n        const header = (xhr.getResponseHeader(\"Content-Disposition\") || \"\").replace(/;$/, \"\");\n        // replace because apparently we send some C-D headers with a trailing \";\"\n        const filename = header ? parse(header).parameters.filename : null;\n        // In Odoo, the default mimetype, including for JSON errors is text/html (ref: http.py:Root.get_response )\n        // in that case, in order to also be able to download html files, we check if we get a proper filename to be able to download\n        if (xhr.status === 200 && (mimetype !== \"text/html\" || filename)) {\n            _download(xhr.response, filename, mimetype);\n            onSuccess(filename);\n        } else if (xhr.status === 502) {\n            // If Odoo is behind another server (nginx)\n            onFailure(new ConnectionLostError(url));\n        } else {\n            const decoder = new FileReader();\n            decoder.onload = () => {\n                const contents = decoder.result;\n                const doc = new DOMParser().parseFromString(contents, \"text/html\");\n                const nodes =\n                    doc.body.children.length === 0 ? [doc.body] : doc.body.children;\n\n                let error;\n                try {\n                    // a Serialized python Error\n                    const node = nodes[1] || nodes[0];\n                    error = JSON.parse(node.textContent);\n                } catch {\n                    error = {\n                        message: \"Arbitrary Uncaught Python Exception\",\n                        data: {\n                            debug:\n                                `${xhr.status}` +\n                                `\\n` +\n                                `${nodes.length > 0 ? nodes[0].textContent : \"\"}\n                                ${nodes.length > 1 ? nodes[1].textContent : \"\"}`,\n                        },\n                    };\n                }\n                error = makeErrorFromResponse(error);\n                onFailure(error);\n            };\n            decoder.readAsText(xhr.response);\n        }\n    };\n    xhr.onerror = () => {\n        onFailure(new ConnectionLostError(url));\n    };\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"../registry\";\n\nfunction checkResponseStatus(response) {\n    if (response.status === 502) {\n        throw new Error(\"Failed to fetch\");\n    }\n}\n\nexport async function get(route, readMethod = \"json\") {\n    const response = await browser.fetch(route, { method: \"GET\" });\n    checkResponseStatus(response);\n    return response[readMethod]();\n}\n\nexport async function post(route, params = {}, readMethod = \"json\") {\n    let formData = params;\n    if (!(formData instanceof FormData)) {\n        formData = new FormData();\n        for (const key in params) {\n            const value = params[key];\n            if (Array.isArray(value) && value.length) {\n                for (const val of value) {\n                    formData.append(key, val);\n                }\n            } else {\n                formData.append(key, value);\n            }\n        }\n    }\n    const response = await browser.fetch(route, {\n        body: formData,\n        method: \"POST\",\n    });\n    checkResponseStatus(response);\n    return response[readMethod]();\n}\n\nexport const httpService = {\n    start() {\n        return { get, post };\n    },\n};\n\nregistry.category(\"services\").add(\"http\", httpService);\n", "import { EventBus } from \"@odoo/owl\";\nimport { browser } from \"../browser/browser\";\n\nexport const rpcBus = new EventBus();\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\nexport class RPCError extends Error {\n    constructor() {\n        super(...arguments);\n        this.name = \"RPC_ERROR\";\n        this.type = \"server\";\n        this.code = null;\n        this.data = null;\n        this.exceptionName = null;\n        this.subType = null;\n    }\n}\n\nexport class ConnectionLostError extends Error {\n    constructor(url, ...args) {\n        super(`Connection to \"${url}\" couldn't be established or was interrupted`, ...args);\n        this.url = url;\n    }\n}\n\nexport class ConnectionAbortedError extends Error {}\n\nexport function makeErrorFromResponse(reponse) {\n    // Odoo returns error like this, in a error field instead of properly\n    // using http error codes...\n    const { code, data: errorData, message, type: subType } = reponse;\n    const error = new RPCError();\n    error.exceptionName = errorData.name;\n    error.subType = subType;\n    error.data = errorData;\n    error.message = message;\n    error.code = code;\n    return error;\n}\n\n// -----------------------------------------------------------------------------\n// Main RPC method\n// -----------------------------------------------------------------------------\nlet rpcId = 0;\nexport function rpc(url, params = {}, settings = {}) {\n    return rpc._rpc(url, params, settings);\n}\n// such that it can be overriden in tests\nrpc._rpc = function (url, params, settings) {\n    const XHR = browser.XMLHttpRequest;\n    const data = {\n        id: rpcId++,\n        jsonrpc: \"2.0\",\n        method: \"call\",\n        params: params,\n    };\n    const request = settings.xhr || new XHR();\n    let rejectFn;\n    const promise = new Promise((resolve, reject) => {\n        rejectFn = reject;\n        rpcBus.trigger(\"RPC:REQUEST\", { data, url, settings });\n        // handle success\n        request.addEventListener(\"load\", () => {\n            if (request.status === 502) {\n                // If Odoo is behind another server (eg.: nginx)\n                const error = new ConnectionLostError(url);\n                rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, error });\n                reject(error);\n                return;\n            }\n            let params;\n            try {\n                params = JSON.parse(request.response);\n            } catch {\n                // the response isn't json parsable, which probably means that the rpc request could\n                // not be handled by the server, e.g. PoolError('The Connection Pool Is Full')\n                const error = new ConnectionLostError(url);\n                rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, error });\n                return reject(error);\n            }\n            const { error: responseError, result: responseResult } = params;\n            if (!params.error) {\n                rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, result: params.result });\n                return resolve(responseResult);\n            }\n            const error = makeErrorFromResponse(responseError);\n            error.id = data.id;\n            error.model = data.params.model;\n            rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, error });\n            reject(error);\n        });\n        // handle failure\n        request.addEventListener(\"error\", () => {\n            const error = new ConnectionLostError(url);\n            rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, error });\n            reject(error);\n        });\n        // configure and send request\n        request.open(\"POST\", url);\n        const headers = settings.headers || {};\n        headers[\"Content-Type\"] = \"application/json\";\n        for (let [header, value] of Object.entries(headers)) {\n            request.setRequestHeader(header, value);\n        }\n        request.send(JSON.stringify(data));\n    });\n    /**\n     * @param {Boolean} rejectError Returns an error if true. Allows you to cancel\n     *                  ignored rpc's in order to unblock the ui and not display an error.\n     */\n    promise.abort = function (rejectError = true) {\n        if (request.abort) {\n            request.abort();\n        }\n        const error = new ConnectionAbortedError(\"XmlHttpRequestError abort\");\n        rpcBus.trigger(\"RPC:RESPONSE\", { data, settings, error });\n        if (rejectError) {\n            rejectFn(error);\n        }\n    };\n    return promise;\n};\n", "import { scrollTo } from \"@web/core/utils/scrolling\";\n\nimport {\n    Component,\n    onWillUpdateProps,\n    useEffect,\n    useExternalListener,\n    useRef,\n    useState,\n} from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\n\n/**\n * A notebook component that will render only the current page and allow\n * switching between its pages.\n *\n * You can also set pages using a template component. Use an array with\n * the `pages` props to do such rendering.\n *\n * Pages can also specify their index in the notebook.\n *\n *      e.g.:\n *          PageTemplate.template = xml`\n                    <h1 t-esc=\"props.heading\" />\n                    <p t-esc=\"props.text\" />`;\n\n *      `pages` could be:\n *      [\n *          {\n *              Component: PageTemplate,\n *              id: 'unique_id' // optional: can be given as defaultPage props to the notebook\n *              index: 1 // optional: page position in the notebook\n *              name: 'some_name' // optional\n *              title: \"Some Title 1\", // title displayed on the tab pane\n *              props: {\n *                  heading: \"Page 1\",\n *                  text: \"Text Content 1\",\n *              },\n *          },\n *          {\n *              Component: PageTemplate,\n *              title: \"Some Title 2\",\n *              props: {\n *                  heading: \"Page 2\",\n *                  text: \"Text Content 2\",\n *              },\n *          },\n *      ]\n *\n * <Notebook pages=\"pages\">\n *    <t t-set-slot=\"Page Name 1\" title=\"Some Title\" isVisible=\"bool\">\n *      <div>Page Content 1</div>\n *    </t>\n *    <t t-set-slot=\"Page Name 2\" title=\"Some Title\" isVisible=\"bool\">\n *      <div>Page Content 2</div>\n *    </t>\n * </Notebook>\n *\n * @extends Component\n */\n\nexport class Notebook extends Component {\n    static template = \"web.Notebook\";\n    static defaultProps = {\n        className: \"\",\n        orientation: \"horizontal\",\n        onPageUpdate: () => {},\n    };\n    static props = {\n        slots: { type: Object, optional: true },\n        pages: { type: Object, optional: true },\n        class: { optional: true },\n        className: { type: String, optional: true },\n        anchors: { type: Object, optional: true },\n        defaultPage: { type: String, optional: true },\n        orientation: { type: String, optional: true },\n        icons: { type: Object, optional: true },\n        onPageUpdate: { type: Function, optional: true },\n    };\n\n    setup() {\n        this.activePane = useRef(\"activePane\");\n        this.anchorTarget = null;\n        this.pages = this.computePages(this.props);\n        this.state = useState({ currentPage: null });\n        this.state.currentPage = this.computeActivePage(this.props.defaultPage, true);\n        useExternalListener(browser, \"click\", this.onAnchorClicked);\n        useEffect(\n            () => {\n                this.props.onPageUpdate(this.state.currentPage);\n                if (this.anchorTarget) {\n                    const matchingEl = this.activePane.el.querySelector(`#${this.anchorTarget}`);\n                    scrollTo(matchingEl, { isAnchor: true });\n                    this.anchorTarget = null;\n                }\n                this.activePane.el?.classList.add(\"show\");\n            },\n            () => [this.state.currentPage]\n        );\n        onWillUpdateProps((nextProps) => {\n            const activateDefault =\n                this.props.defaultPage !== nextProps.defaultPage || !this.defaultVisible;\n            this.pages = this.computePages(nextProps);\n            this.state.currentPage = this.computeActivePage(nextProps.defaultPage, activateDefault);\n        });\n    }\n\n    get navItems() {\n        return this.pages.filter((e) => e[1].isVisible);\n    }\n\n    get page() {\n        const page = this.pages.find((e) => e[0] === this.state.currentPage)[1];\n        return page.Component && page;\n    }\n\n    onAnchorClicked(ev) {\n        if (!this.props.anchors) {\n            return;\n        }\n        const href = ev.target.closest(\"a\")?.getAttribute(\"href\");\n        if (!href) {\n            return;\n        }\n        const id = href.substring(1);\n        if (this.props.anchors[id]) {\n            if (this.state.currentPage !== this.props.anchors[id].target) {\n                ev.preventDefault();\n                this.anchorTarget = id;\n                this.state.currentPage = this.props.anchors[id].target;\n            }\n        }\n    }\n\n    activatePage(pageIndex) {\n        if (!this.disabledPages.includes(pageIndex) && this.state.currentPage !== pageIndex) {\n            this.activePane.el?.classList.remove(\"show\");\n            this.state.currentPage = pageIndex;\n        }\n    }\n\n    computePages(props) {\n        if (!props.slots && !props.pages) {\n            return [];\n        }\n        if (props.pages) {\n            for (const page of props.pages) {\n                page.isVisible = true;\n            }\n        }\n        this.disabledPages = [];\n        const pages = [];\n        const pagesWithIndex = [];\n        for (const [k, v] of Object.entries({ ...props.slots, ...props.pages })) {\n            const id = v.id || k;\n            if (v.index) {\n                pagesWithIndex.push([id, v]);\n            } else {\n                pages.push([id, v]);\n            }\n            if (v.isDisabled) {\n                this.disabledPages.push(k);\n            }\n        }\n        for (const page of pagesWithIndex) {\n            pages.splice(page[1].index, 0, page);\n        }\n        return pages;\n    }\n\n    computeActivePage(defaultPage, activateDefault) {\n        if (!this.pages.length) {\n            return null;\n        }\n        const pages = this.pages.filter((e) => e[1].isVisible).map((e) => e[0]);\n\n        if (defaultPage) {\n            if (!pages.includes(defaultPage)) {\n                this.defaultVisible = false;\n            } else {\n                this.defaultVisible = true;\n                if (activateDefault) {\n                    return defaultPage;\n                }\n            }\n        }\n        const current = this.state.currentPage;\n        if (!current || (current && !pages.includes(current))) {\n            return pages[0];\n        }\n\n        return current;\n    }\n}\n", "import { Component } from \"@odoo/owl\";\n\nexport class Notification extends Component {\n    static template = \"web.NotificationWowl\";\n    static props = {\n        message: {\n            validate: (m) => {\n                return (\n                    typeof m === \"string\" ||\n                    (typeof m === \"object\" && typeof m.toString === \"function\")\n                );\n            },\n        },\n        title: { type: [String, Boolean, { toString: Function }], optional: true },\n        type: {\n            type: String,\n            optional: true,\n            validate: (t) => [\"warning\", \"danger\", \"success\", \"info\"].includes(t),\n        },\n        className: { type: String, optional: true },\n        buttons: {\n            type: Array,\n            element: {\n                type: Object,\n                shape: {\n                    name: { type: String },\n                    icon: { type: String, optional: true },\n                    primary: { type: Boolean, optional: true },\n                    onClick: Function,\n                },\n            },\n            optional: true,\n        },\n        close: { type: Function },\n        refresh: { type: Function },\n        freeze: { type: Function },\n    };\n    static defaultProps = {\n        buttons: [],\n        className: \"\",\n        type: \"warning\",\n    };\n}\n", "import { Notification } from \"./notification\";\nimport { Transition } from \"@web/core/transition\";\n\nimport { Component, xml, useState } from \"@odoo/owl\";\n\nexport class NotificationContainer extends Component {\n    static props = {\n        notifications: Object,\n    };\n\n    static template = xml`\n        <div class=\"o_notification_manager\">\n            <t t-foreach=\"notifications\" t-as=\"notification\" t-key=\"notification\">\n                <Transition leaveDuration=\"0\" immediate=\"true\" name=\"'o_notification_fade'\" t-slot-scope=\"transition\">\n                    <Notification t-props=\"notification_value.props\" className=\"(notification_value.props.className || '') + ' ' + transition.className\"/>\n                </Transition>\n            </t>\n        </div>`;\n    static components = { Notification, Transition };\n\n    setup() {\n        this.notifications = useState(this.props.notifications);\n    }\n}\n", "import { browser } from \"../browser/browser\";\nimport { registry } from \"../registry\";\nimport { NotificationContainer } from \"./notification_container\";\n\nimport { reactive } from \"@odoo/owl\";\n\nconst AUTOCLOSE_DELAY = 4000;\n\n/**\n * @typedef {Object} NotificationButton\n * @property {string} name\n * @property {string} [icon]\n * @property {boolean} [primary=false]\n * @property {function(): void} onClick\n *\n * @typedef {Object} NotificationOptions\n * @property {string} [title]\n * @property {number} [autocloseDelay=4000]\n * @property {\"warning\" | \"danger\" | \"success\" | \"info\"} [type]\n * @property {boolean} [sticky=false]\n * @property {string} [className]\n * @property {function(): void} [onClose]\n * @property {NotificationButton[]} [buttons]\n */\n\nexport const notificationService = {\n    notificationContainer: NotificationContainer,\n\n    start() {\n        let notifId = 0;\n        const notifications = reactive({});\n\n        registry.category(\"main_components\").add(\n            this.notificationContainer.name,\n            {\n                Component: this.notificationContainer,\n                props: { notifications },\n            },\n            { sequence: 100 }\n        );\n\n        /**\n         * @param {string} message\n         * @param {NotificationOptions} [options]\n         */\n        function add(message, options = {}) {\n            const id = ++notifId;\n            const closeFn = () => close(id);\n            const props = Object.assign({}, options, { message, close: closeFn });\n            const autocloseDelay = options.autocloseDelay ?? AUTOCLOSE_DELAY;\n            const sticky = props.sticky;\n            delete props.sticky;\n            delete props.onClose;\n            delete props.autocloseDelay;\n            let closeTimeout;\n            const refresh = sticky\n                ? () => {}\n                : () => {\n                      closeTimeout = browser.setTimeout(closeFn, autocloseDelay);\n                  };\n            const freeze = sticky\n                ? () => {}\n                : () => {\n                      browser.clearTimeout(closeTimeout);\n                  };\n            props.refresh = refreshAll;\n            props.freeze = freezeAll;\n            const notification = {\n                id,\n                props,\n                onClose: options.onClose,\n                refresh,\n                freeze,\n            };\n            notifications[id] = notification;\n            if (!sticky) {\n                closeTimeout = browser.setTimeout(closeFn, autocloseDelay);\n            }\n            return closeFn;\n        }\n\n        function refreshAll() {\n            for (const id in notifications) {\n                notifications[id].refresh();\n            }\n        }\n\n        function freezeAll() {\n            for (const id in notifications) {\n                notifications[id].freeze();\n            }\n        }\n\n        function close(id) {\n            if (notifications[id]) {\n                const notification = notifications[id];\n                if (notification.onClose) {\n                    notification.onClose();\n                }\n                delete notifications[id];\n            }\n        }\n\n        return { add };\n    },\n};\n\nregistry.category(\"services\").add(\"notification\", notificationService);\n", "import { registry } from \"@web/core/registry\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { user } from \"@web/core/user\";\n\n/**\n * This ORM service is the standard way to interact with the ORM in python from\n * the javascript codebase.\n */\n\n// -----------------------------------------------------------------------------\n// ORM\n// -----------------------------------------------------------------------------\n\n/**\n * One2many and Many2many fields expect a special command to manipulate the\n * relation they implement.\n *\n * Internally, each command is a 3-elements tuple where the first element is a\n * mandatory integer that identifies the command, the second element is either\n * the related record id to apply the command on (commands update, delete,\n * unlink and link) either 0 (commands create, clear and set), the third\n * element is either the ``values`` to write on the record (commands create\n * and update) either the new ``ids`` list of related records (command set),\n * either 0 (commands delete, unlink, link, and clear).\n */\nexport const x2ManyCommands = {\n    // (0, virtualID | false, { values })\n    CREATE: 0,\n    create(virtualID, values) {\n        delete values.id;\n        return [x2ManyCommands.CREATE, virtualID || false, values];\n    },\n    // (1, id, { values })\n    UPDATE: 1,\n    update(id, values) {\n        delete values.id;\n        return [x2ManyCommands.UPDATE, id, values];\n    },\n    // (2, id[, _])\n    DELETE: 2,\n    delete(id) {\n        return [x2ManyCommands.DELETE, id, false];\n    },\n    // (3, id[, _]) removes relation, but not linked record itself\n    UNLINK: 3,\n    unlink(id) {\n        return [x2ManyCommands.UNLINK, id, false];\n    },\n    // (4, id[, _])\n    LINK: 4,\n    link(id) {\n        return [x2ManyCommands.LINK, id, false];\n    },\n    // (5[, _[, _]])\n    CLEAR: 5,\n    clear() {\n        return [x2ManyCommands.CLEAR, false, false];\n    },\n    // (6, _, ids) replaces all linked records with provided ids\n    SET: 6,\n    set(ids) {\n        return [x2ManyCommands.SET, false, ids];\n    },\n};\n\nfunction validateModel(value) {\n    if (typeof value !== \"string\" || value.length === 0) {\n        throw new Error(`Invalid model name: ${value}`);\n    }\n}\nfunction validatePrimitiveList(name, type, value) {\n    if (!Array.isArray(value) || value.some((val) => typeof val !== type)) {\n        throw new Error(`Invalid ${name} list: ${value}`);\n    }\n}\nfunction validateObject(name, obj) {\n    if (typeof obj !== \"object\" || obj === null || Array.isArray(obj)) {\n        throw new Error(`${name} should be an object`);\n    }\n}\nfunction validateArray(name, array) {\n    if (!Array.isArray(array)) {\n        throw new Error(`${name} should be an array`);\n    }\n}\n\nexport const UPDATE_METHODS = [\n    \"unlink\",\n    \"create\",\n    \"write\",\n    \"web_save\",\n    \"action_archive\",\n    \"action_unarchive\",\n];\n\nexport class ORM {\n    constructor() {\n        this.rpc = rpc; // to be overridable by the SampleORM\n        /** @protected */\n        this._silent = false;\n    }\n\n    /** @returns {ORM} */\n    get silent() {\n        return Object.assign(Object.create(this), { _silent: true });\n    }\n\n    /**\n     * @param {string} model\n     * @param {string} method\n     * @param {any[]} [args=[]]\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any>}\n     */\n    call(model, method, args = [], kwargs = {}) {\n        validateModel(model);\n        const url = `/web/dataset/call_kw/${model}/${method}`;\n        const fullContext = Object.assign({}, user.context, kwargs.context || {});\n        const fullKwargs = Object.assign({}, kwargs, { context: fullContext });\n        const params = {\n            model,\n            method,\n            args,\n            kwargs: fullKwargs,\n        };\n        return this.rpc(url, params, { silent: this._silent });\n    }\n\n    /**\n     * @param {string} model\n     * @param {any[]} records\n     * @param {any} [kwargs=[]]\n     * @returns {Promise<number>}\n     */\n    create(model, records, kwargs = {}) {\n        validateArray(\"records\", records);\n        for (const record of records) {\n            validateObject(\"record\", record);\n        }\n        return this.call(model, \"create\", [records], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {number[]} ids\n     * @param {string[]} fields\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    read(model, ids, fields, kwargs = {}) {\n        validatePrimitiveList(\"ids\", \"number\", ids);\n        if (fields) {\n            validatePrimitiveList(\"fields\", \"string\", fields);\n        }\n        if (!ids.length) {\n            return Promise.resolve([]);\n        }\n        return this.call(model, \"read\", [ids, fields], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {string[]} fields\n     * @param {string[]} groupby\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    readGroup(model, domain, fields, groupby, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        validatePrimitiveList(\"fields\", \"string\", fields);\n        validatePrimitiveList(\"groupby\", \"string\", groupby);\n        groupby = [...new Set(groupby)];\n        return this.call(model, \"read_group\", [], { ...kwargs, domain, fields, groupby });\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    search(model, domain, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        return this.call(model, \"search\", [domain], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {string[]} fields\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    searchRead(model, domain, fields, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        if (fields) {\n            validatePrimitiveList(\"fields\", \"string\", fields);\n        }\n        return this.call(model, \"search_read\", [], { ...kwargs, domain, fields });\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {any} [kwargs={}]\n     * @returns {Promise<number>}\n     */\n    searchCount(model, domain, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        return this.call(model, \"search_count\", [domain], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {number[]} ids\n     * @param {any} [kwargs={}]\n     * @returns {Promise<boolean>}\n     */\n    unlink(model, ids, kwargs = {}) {\n        validatePrimitiveList(\"ids\", \"number\", ids);\n        if (!ids.length) {\n            return Promise.resolve(true);\n        }\n        return this.call(model, \"unlink\", [ids], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {string[]} fields\n     * @param {string[]} groupby\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    webReadGroup(model, domain, fields, groupby, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        validatePrimitiveList(\"fields\", \"string\", fields);\n        validatePrimitiveList(\"groupby\", \"string\", groupby);\n        return this.call(model, \"web_read_group\", [], {\n            ...kwargs,\n            groupby,\n            domain,\n            fields,\n        });\n    }\n\n    /**\n     * @param {string} model\n     * @param {number[]} ids\n     * @param {any} [kwargs={}]\n     * @param {Object} [kwargs.specification]\n     * @param {Object} [kwargs.context]\n     * @returns {Promise<any[]>}\n     */\n    webRead(model, ids, kwargs = {}) {\n        validatePrimitiveList(\"ids\", \"number\", ids);\n        return this.call(model, \"web_read\", [ids], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {import(\"@web/core/domain\").DomainListRepr} domain\n     * @param {any} [kwargs={}]\n     * @returns {Promise<any[]>}\n     */\n    webSearchRead(model, domain, kwargs = {}) {\n        validateArray(\"domain\", domain);\n        return this.call(model, \"web_search_read\", [], { ...kwargs, domain });\n    }\n\n    /**\n     * @param {string} model\n     * @param {number[]} ids\n     * @param {any} data\n     * @param {any} [kwargs={}]\n     * @returns {Promise<boolean>}\n     */\n    write(model, ids, data, kwargs = {}) {\n        validatePrimitiveList(\"ids\", \"number\", ids);\n        validateObject(\"data\", data);\n        return this.call(model, \"write\", [ids, data], kwargs);\n    }\n\n    /**\n     * @param {string} model\n     * @param {number[]} ids\n     * @param {any} data\n     * @param {any} [kwargs={}]\n     * @param {Object} [kwargs.specification]\n     * @param {Object} [kwargs.context]\n     * @returns {Promise<any[]>}\n     */\n    webSave(model, ids, data, kwargs = {}) {\n        validatePrimitiveList(\"ids\", \"number\", ids);\n        validateObject(\"data\", data);\n        return this.call(model, \"web_save\", [ids, data], kwargs);\n    }\n}\n\n/**\n * Note:\n *\n * when we will need a way to configure a rpc (for example, to setup a \"shadow\"\n * flag, or some way of not displaying errors), we can use the following api:\n *\n * this.orm = useService('orm');\n *\n * ...\n *\n * const result = await this.orm.withOption({shadow: true}).read('res.partner', [id]);\n */\nexport const ormService = {\n    async: [\n        \"call\",\n        \"create\",\n        \"nameGet\",\n        \"read\",\n        \"readGroup\",\n        \"search\",\n        \"searchRead\",\n        \"unlink\",\n        \"webSearchRead\",\n        \"write\",\n    ],\n    start() {\n        return new ORM();\n    },\n};\n\nregistry.category(\"services\").add(\"orm\", ormService);\n", "import { Component, onWillDestroy, useChildSubEnv, useEffect, useRef, useState } from \"@odoo/owl\";\nimport { sortBy } from \"@web/core/utils/arrays\";\nimport { ErrorHandler } from \"@web/core/utils/components\";\n\nconst OVERLAY_ITEMS = [];\nexport const OVERLAY_SYMBOL = Symbol(\"Overlay\");\n\nclass OverlayItem extends Component {\n    static template = \"web.OverlayContainer.Item\";\n    static components = {};\n    static props = {\n        component: { type: Function },\n        props: { type: Object },\n        env: { type: Object, optional: true },\n    };\n\n    setup() {\n        this.rootRef = useRef(\"rootRef\");\n\n        OVERLAY_ITEMS.push(this);\n        onWillDestroy(() => {\n            const index = OVERLAY_ITEMS.indexOf(this);\n            OVERLAY_ITEMS.splice(index, 1);\n        });\n\n        if (this.props.env) {\n            this.__owl__.childEnv = this.props.env;\n        }\n\n        useChildSubEnv({\n            [OVERLAY_SYMBOL]: {\n                contains: (target) => this.contains(target),\n            },\n        });\n    }\n\n    get subOverlays() {\n        return OVERLAY_ITEMS.slice(OVERLAY_ITEMS.indexOf(this));\n    }\n\n    contains(target) {\n        return (\n            this.rootRef.el?.contains(target) ||\n            this.subOverlays.some((oi) => oi.rootRef.el?.contains(target))\n        );\n    }\n}\n\nexport class OverlayContainer extends Component {\n    static template = \"web.OverlayContainer\";\n    static components = { ErrorHandler, OverlayItem };\n    static props = { overlays: Object };\n\n    setup() {\n        this.root = useRef(\"root\");\n        this.state = useState({ rootEl: null });\n        useEffect(\n            () => {\n                this.state.rootEl = this.root.el;\n            },\n            () => [this.root.el]\n        );\n    }\n\n    get sortedOverlays() {\n        return sortBy(Object.values(this.props.overlays), (overlay) => overlay.sequence);\n    }\n\n    isVisible(overlay) {\n        return overlay.rootId === this.state.rootEl?.getRootNode()?.host?.id;\n    }\n\n    handleError(overlay, error) {\n        overlay.remove();\n        Promise.resolve().then(() => {\n            throw error;\n        });\n    }\n}\n", "import { markRaw, reactive } from \"@odoo/owl\";\nimport { registry } from \"../registry\";\nimport { OverlayContainer } from \"./overlay_container\";\n\nconst mainComponents = registry.category(\"main_components\");\nconst services = registry.category(\"services\");\n\n/**\n * @typedef {{\n *  env?: object;\n *  onRemove?: () => void;\n *  sequence?: number;\n *  rootId?: string;\n * }} OverlayServiceAddOptions\n */\n\nexport const overlayService = {\n    start() {\n        let nextId = 0;\n        const overlays = reactive({});\n\n        mainComponents.add(\"OverlayContainer\", {\n            Component: OverlayContainer,\n            props: { overlays },\n        });\n\n        const remove = (id, onRemove = () => {}) => {\n            if (id in overlays) {\n                onRemove();\n                delete overlays[id];\n            }\n        };\n\n        /**\n         * @param {typeof Component} component\n         * @param {object} props\n         * @param {OverlayServiceAddOptions} [options]\n         * @returns {() => void}\n         */\n        const add = (component, props, options = {}) => {\n            const id = ++nextId;\n            const removeCurrentOverlay = () => remove(id, options.onRemove);\n            overlays[id] = {\n                id,\n                component,\n                env: options.env && markRaw(options.env),\n                props,\n                remove: removeCurrentOverlay,\n                sequence: options.sequence ?? 50,\n                rootId: options.rootId,\n            };\n            return removeCurrentOverlay;\n        };\n\n        return { add, overlays };\n    },\n};\n\nservices.add(\"overlay\", overlayService);\n", "import { useAutofocus } from \"../utils/hooks\";\nimport { clamp } from \"../utils/numbers\";\n\nimport { Component, useExternalListener, useState, EventBus } from \"@odoo/owl\";\n\nexport const PAGER_UPDATED_EVENT = \"PAGER:UPDATED\";\nexport const pagerBus = new EventBus();\n\n/**\n * Pager\n *\n * The pager goes from 1 to total (included).\n * The current value is minimum if limit === 1 or the interval:\n *      [minimum, minimum + limit[ if limit > 1].\n * The value can be manually changed by clicking on the pager value and giving\n * an input matching the pattern: min[,max] (in which the comma can be a dash\n * or a semicolon).\n * The pager also provides two buttons to quickly change the current page (next\n * or previous).\n * @extends Component\n */\nexport class Pager extends Component {\n    static template = \"web.Pager\";\n    static defaultProps = {\n        isEditable: true,\n        withAccessKey: true,\n    };\n    static props = {\n        offset: Number,\n        limit: Number,\n        total: Number,\n        onUpdate: Function,\n        isEditable: { type: Boolean, optional: true },\n        withAccessKey: { type: Boolean, optional: true },\n        updateTotal: { type: Function, optional: true },\n    };\n\n    setup() {\n        this.state = useState({\n            isEditing: false,\n            isDisabled: false,\n        });\n        this.inputRef = useAutofocus();\n        useExternalListener(document, \"mousedown\", this.onClickAway, { capture: true });\n    }\n\n    /**\n     * @returns {number}\n     */\n    get minimum() {\n        return this.props.offset + 1;\n    }\n    /**\n     * @returns {number}\n     */\n    get maximum() {\n        return Math.min(this.props.offset + this.props.limit, this.props.total);\n    }\n    /**\n     * @returns {string}\n     */\n    get value() {\n        const parts = [this.minimum];\n        if (this.props.limit > 1) {\n            parts.push(this.maximum);\n        }\n        return parts.join(\"-\");\n    }\n    /**\n     * Note: returns false if we received the props \"updateTotal\", as in this case we don't know\n     * the real total so we can't assert that there's a single page.\n     * @returns {boolean} true if there is only one page\n     */\n    get isSinglePage() {\n        return !this.props.updateTotal && this.minimum === 1 && this.maximum === this.props.total;\n    }\n    /**\n     * @param {-1 | 1} direction\n     */\n    async navigate(direction) {\n        let minimum = this.props.offset + this.props.limit * direction;\n        let total = this.props.total;\n        if (this.props.updateTotal && minimum < 0) {\n            // we must know the real total to be able to loop by doing \"previous\"\n            total = await this.props.updateTotal();\n        }\n        if (minimum >= total) {\n            if (!this.props.updateTotal) {\n                // only loop forward if we know the real total, otherwise let the minimum\n                // go out of range\n                minimum = 0;\n            }\n        } else if (minimum < 0 && this.props.limit === 1) {\n            minimum = total - 1;\n        } else if (minimum < 0 && this.props.limit > 1) {\n            minimum = total - (total % this.props.limit || this.props.limit);\n        }\n        this.update(minimum, this.props.limit, true);\n    }\n    /**\n     * @param {string} value\n     * @returns {{ minimum: number, maximum: number }}\n     */\n    async parse(value) {\n        let [minimum, maximum] = value.trim().split(/\\s*[-\\s,;]\\s*/);\n        minimum = parseInt(minimum, 10);\n        maximum = maximum ? parseInt(maximum, 10) : minimum;\n        if (this.props.updateTotal) {\n            // we don't know the real total, so we can't clamp\n            return { minimum: minimum - 1, maximum };\n        }\n        return {\n            minimum: clamp(minimum, 1, this.props.total) - 1,\n            maximum: clamp(maximum, 1, this.props.total),\n        };\n    }\n    /**\n     * @param {string} value\n     */\n    async setValue(value) {\n        const { minimum, maximum } = await this.parse(value);\n\n        if (!isNaN(minimum) && !isNaN(maximum) && minimum < maximum) {\n            this.update(minimum, maximum - minimum);\n        }\n    }\n    /**\n     * @param {number} offset\n     * @param {number} limit\n     * @param {Boolean} hasNavigated\n     */\n    async update(offset, limit, hasNavigated) {\n        this.state.isDisabled = true;\n        try {\n            await this.props.onUpdate({ offset, limit }, hasNavigated);\n        } finally {\n            if (this.env.isSmall) {\n                pagerBus.trigger(PAGER_UPDATED_EVENT, {\n                    value: this.value,\n                    total: this.props.total,\n                });\n            }\n            this.state.isDisabled = false;\n            this.state.isEditing = false;\n        }\n    }\n\n    async updateTotal() {\n        if (!this.state.isDisabled) {\n            this.state.isDisabled = true;\n            await this.props.updateTotal();\n            this.state.isDisabled = false;\n        }\n    }\n\n    /**\n     * @param {MouseEvent} ev\n     */\n    onClickAway(ev) {\n        if (ev.target !== this.inputRef.el) {\n            this.state.isEditing = false;\n        }\n    }\n    onInputBlur() {\n        this.state.isEditing = false;\n    }\n    /**\n     * @param {Event} ev\n     */\n    onInputChange(ev) {\n        this.setValue(ev.target.value);\n        if (!this.state.isDisabled) {\n            ev.preventDefault();\n        }\n    }\n    /**\n     * @param {KeyboardEvent} ev\n     */\n    onInputKeydown(ev) {\n        switch (ev.key) {\n            case \"Enter\":\n                ev.preventDefault();\n                ev.stopPropagation();\n                this.setValue(ev.currentTarget.value);\n                break;\n            case \"Escape\":\n                ev.preventDefault();\n                ev.stopPropagation();\n                this.state.isEditing = false;\n                break;\n        }\n    }\n    onValueClick() {\n        if (this.props.isEditable && !this.state.isEditing && !this.state.isDisabled) {\n            if (this.inputRef.el) {\n                this.inputRef.el.focus();\n            }\n            this.state.isEditing = true;\n        }\n    }\n}\n", "import { browser } from \"../browser/browser\";\nimport { registry } from \"../registry\";\nimport { Transition } from \"../transition\";\nimport { useBus } from \"../utils/hooks\";\n\nimport { Component, useState } from \"@odoo/owl\";\nimport { PAGER_UPDATED_EVENT, pagerBus } from \"./pager\";\n\nexport class PagerIndicator extends Component {\n    static template = \"web.PagerIndicator\";\n    static components = { Transition };\n    static props = {};\n\n    setup() {\n        this.state = useState({\n            show: false,\n            value: \"-\",\n            total: 0,\n        });\n        this.startShowTimer = null;\n        useBus(pagerBus, PAGER_UPDATED_EVENT, this.pagerUpdate);\n    }\n\n    pagerUpdate({ detail }) {\n        this.state.value = detail.value;\n        this.state.total = detail.total;\n        browser.clearTimeout(this.startShowTimer);\n        this.state.show = true;\n        this.startShowTimer = browser.setTimeout(() => {\n            this.state.show = false;\n        }, 1400);\n    }\n}\n\nregistry.category(\"main_components\").add(\"PagerIndicator\", {\n    Component: PagerIndicator,\n});\n", "import { Component, onMounted, onWillDestroy, useComponent, useRef } from \"@odoo/owl\";\nimport { useHotkey } from \"@web/core/hotkeys/hotkey_hook\";\nimport { OVERLAY_SYMBOL } from \"@web/core/overlay/overlay_container\";\nimport { usePosition } from \"@web/core/position/position_hook\";\nimport { useActiveElement } from \"@web/core/ui/ui_service\";\nimport { addClassesToElement, mergeClasses } from \"@web/core/utils/classname\";\nimport { useForwardRefToParent } from \"@web/core/utils/hooks\";\n\nfunction useEarlyExternalListener(target, eventName, handler, eventParams) {\n    const component = useComponent();\n    const boundHandler = handler.bind(component);\n    target.addEventListener(eventName, boundHandler, eventParams);\n    onWillDestroy(() => target.removeEventListener(eventName, boundHandler, eventParams));\n}\n\n/**\n * Will trigger the callback when the window is clicked, giving\n * the clicked element as parameter.\n *\n * This also handles the case where an iframe is clicked.\n *\n * @param {Function} callback\n */\nfunction useClickAway(callback) {\n    const pointerDownHandler = (event) => {\n        callback(event.composedPath()[0]);\n    };\n\n    const blurHandler = (ev) => {\n        const target = ev.relatedTarget || document.activeElement;\n        if (target?.tagName === \"IFRAME\") {\n            callback(target);\n        }\n    };\n\n    useEarlyExternalListener(window, \"pointerdown\", pointerDownHandler, { capture: true });\n    useEarlyExternalListener(window, \"blur\", blurHandler, { capture: true });\n}\n\nconst POPOVERS = new WeakMap();\n/**\n * Can be used to retrieve the popover element for a given target.\n * @param {HTMLElement} target\n * @returns {HTMLElement | undefined} the popover element if it exists\n */\nexport function getPopoverForTarget(target) {\n    return POPOVERS.get(target);\n}\n\nexport class Popover extends Component {\n    static template = \"web.Popover\";\n    static defaultProps = {\n        animation: true,\n        arrow: true,\n        class: \"\",\n        closeOnClickAway: () => true,\n        closeOnEscape: true,\n        componentProps: {},\n        fixedPosition: false,\n        position: \"bottom\",\n        setActiveElement: false,\n    };\n    static props = {\n        // Main props\n        component: { type: Function },\n        componentProps: { optional: true, type: Object },\n        target: {\n            validate: (target) => {\n                // target may be inside an iframe, so get the Element constructor\n                // to test against from its owner document's default view\n                const Element = target?.ownerDocument?.defaultView?.Element;\n                return (\n                    (Boolean(Element) &&\n                        (target instanceof Element || target instanceof window.Element)) ||\n                    (typeof target === \"object\" && target?.constructor?.name?.endsWith(\"Element\"))\n                );\n            },\n        },\n\n        // Styling and semantical props\n        animation: { optional: true, type: Boolean },\n        arrow: { optional: true, type: Boolean },\n        class: { optional: true },\n        role: { optional: true, type: String },\n\n        // Positioning props\n        fixedPosition: { optional: true, type: Boolean },\n        holdOnHover: { optional: true, type: Boolean },\n        onPositioned: { optional: true, type: Function },\n        position: {\n            optional: true,\n            type: String,\n            validate: (p) => {\n                const [d, v = \"middle\"] = p.split(\"-\");\n                return (\n                    [\"top\", \"bottom\", \"left\", \"right\"].includes(d) &&\n                    [\"start\", \"middle\", \"end\", \"fit\"].includes(v)\n                );\n            },\n        },\n\n        // Control props\n        close: { optional: true, type: Function },\n        closeOnClickAway: { optional: true, type: Function },\n        closeOnEscape: { optional: true, type: Boolean },\n        setActiveElement: { optional: true, type: Boolean },\n\n        // Technical props\n        ref: { optional: true, type: Function },\n        slots: { optional: true, type: Object },\n    };\n\n    static animationTime = 200;\n    setup() {\n        if (this.props.setActiveElement) {\n            useActiveElement(\"ref\");\n        }\n\n        useForwardRefToParent(\"ref\");\n        this.popoverRef = useRef(\"ref\");\n\n        let shouldAnimate = this.props.animation;\n        this.position = usePosition(\"ref\", () => this.props.target, {\n            onPositioned: (el, solution) => {\n                (this.props.onPositioned || this.onPositioned.bind(this))(el, solution);\n                if (this.props.arrow && this.props.onPositioned) {\n                    this.onPositioned.bind(this)(el, solution);\n                }\n\n                // opening animation\n                if (shouldAnimate) {\n                    shouldAnimate = false; // animate only once\n                    const transform = {\n                        top: [\"translateY(-5%)\", \"translateY(0)\"],\n                        right: [\"translateX(5%)\", \"translateX(0)\"],\n                        bottom: [\"translateY(5%)\", \"translateY(0)\"],\n                        left: [\"translateX(-5%)\", \"translateX(0)\"],\n                    }[solution.direction];\n                    this.position.lock();\n                    const animation = el.animate(\n                        { opacity: [0, 1], transform },\n                        this.constructor.animationTime\n                    );\n                    animation.finished.then(this.position.unlock);\n                }\n\n                if (this.props.fixedPosition) {\n                    // Prevent further positioning updates if fixed position is wanted\n                    this.position.lock();\n                }\n            },\n            position: this.props.position,\n        });\n\n        onMounted(() => POPOVERS.set(this.props.target, this.popoverRef.el));\n        onWillDestroy(() => POPOVERS.delete(this.props.target));\n\n        if (!this.props.close) {\n            return;\n        }\n        if (this.props.target.isConnected) {\n            useClickAway((target) => this.onClickAway(target));\n\n            if (this.props.closeOnEscape) {\n                useHotkey(\"escape\", () => this.props.close());\n            }\n            const targetObserver = new MutationObserver(this.onTargetMutate.bind(this));\n            targetObserver.observe(this.props.target.parentElement, { childList: true });\n            onWillDestroy(() => targetObserver.disconnect());\n        } else {\n            this.props.close();\n        }\n    }\n\n    get defaultClassObj() {\n        return mergeClasses(\n            \"o_popover popover mw-100\",\n            { \"o-popover--with-arrow\": this.props.arrow },\n            this.props.class\n        );\n    }\n\n    isInside(target) {\n        return (\n            this.props.target?.contains(target) ||\n            this.popoverRef?.el?.contains(target) ||\n            this.env[OVERLAY_SYMBOL]?.contains(target)\n        );\n    }\n\n    onClickAway(target) {\n        if (this.props.closeOnClickAway(target) && !this.isInside(target)) {\n            this.props.close();\n        }\n    }\n\n    onTargetMutate() {\n        if (!this.props.target.isConnected) {\n            this.props.close();\n        }\n    }\n\n    onPositioned(el, { direction, variant }) {\n        const position = `${direction[0]}${variant[0]}`;\n\n        // reset all popover classes\n        el.classList = [];\n        const directionMap = {\n            top: \"top\",\n            bottom: \"bottom\",\n            left: \"start\",\n            right: \"end\",\n        };\n        addClassesToElement(\n            el,\n            this.defaultClassObj,\n            `bs-popover-${directionMap[direction]}`,\n            `o-popover-${direction}`,\n            `o-popover--${position}`\n        );\n\n        if (this.props.arrow) {\n            const arrowEl = el.querySelector(\":scope > .popover-arrow\");\n            // reset all arrow classes\n            arrowEl.className = \"popover-arrow\";\n            switch (position) {\n                case \"tm\": // top-middle\n                case \"bm\": // bottom-middle\n                case \"tf\": // top-fit\n                case \"bf\": // bottom-fit\n                    arrowEl.classList.add(\"start-0\", \"end-0\", \"mx-auto\");\n                    break;\n                case \"lm\": // left-middle\n                case \"rm\": // right-middle\n                case \"lf\": // left-fit\n                case \"rf\": // right-fit\n                    arrowEl.classList.add(\"top-0\", \"bottom-0\", \"my-auto\");\n                    break;\n                case \"ts\": // top-start\n                case \"bs\": // bottom-start\n                    arrowEl.classList.add(\"end-auto\");\n                    break;\n                case \"te\": // top-end\n                case \"be\": // bottom-end\n                    arrowEl.classList.add(\"start-auto\");\n                    break;\n                case \"ls\": // left-start\n                case \"rs\": // right-start\n                    arrowEl.classList.add(\"bottom-auto\");\n                    break;\n                case \"le\": // left-end\n                case \"re\": // right-end\n                    arrowEl.classList.add(\"top-auto\");\n                    break;\n            }\n        }\n    }\n}\n", "import { onWillUnmount, status, useComponent } from \"@odoo/owl\";\nimport { useService } from \"@web/core/utils/hooks\";\n\n/**\n * @typedef {import(\"@web/core/popover/popover_service\").PopoverServiceAddFunction} PopoverServiceAddFunction\n * @typedef {import(\"@web/core/popover/popover_service\").PopoverServiceAddOptions} PopoverServiceAddOptions\n */\n\n/**\n * @typedef PopoverHookReturnType\n * @property {(target: string | HTMLElement, props: object) => void} open\n *  - Signals the manager to open the configured popover\n *    component on the target, with the given props.\n * @property {() => void} close\n *  - Signals the manager to remove the popover.\n * @property {boolean} isOpen\n *  - Whether the popover is currently open.\n */\n\n/**\n * @param {PopoverServiceAddFunction} addFn\n * @param {typeof import(\"@odoo/owl\").Component} component\n * @param {PopoverServiceAddOptions} options\n * @returns {PopoverHookReturnType}\n */\nexport function makePopover(addFn, component, options) {\n    let removeFn = null;\n    function close() {\n        removeFn?.();\n    }\n    return {\n        open(target, props) {\n            close();\n            const newOptions = Object.create(options);\n            newOptions.onClose = () => {\n                removeFn = null;\n                options.onClose?.();\n            };\n            removeFn = addFn(target, component, props, newOptions);\n        },\n        close,\n        get isOpen() {\n            return Boolean(removeFn);\n        },\n    };\n}\n\n/**\n * Manages a component to be used as a popover.\n *\n * @param {typeof import(\"@odoo/owl\").Component} component\n * @param {PopoverServiceAddOptions} [options]\n * @returns {PopoverHookReturnType}\n */\nexport function usePopover(component, options = {}) {\n    const popoverService = useService(\"popover\");\n    const owner = useComponent();\n    const newOptions = Object.create(options);\n    newOptions.onClose = () => {\n        if (status(owner) !== \"destroyed\") {\n            options.onClose?.();\n        }\n    };\n    const popover = makePopover(popoverService.add, component, newOptions);\n    onWillUnmount(popover.close);\n    return popover;\n}\n", "import { markRaw } from \"@odoo/owl\";\nimport { Popover } from \"@web/core/popover/popover\";\nimport { registry } from \"@web/core/registry\";\n\n/**\n * @typedef {{\n *   animation?: Boolean;\n *   arrow?: Boolean;\n *   closeOnClickAway?: boolean | (target: HTMLElement) => boolean;\n *   closeOnEscape?: boolean;\n *   env?: object;\n *   fixedPosition?: boolean;\n *   onClose?: () => void;\n *   onPositioned?: import(\"@web/core/position/position_hook\").UsePositionOptions[\"onPositioned\"];\n *   popoverClass?: string;\n *   popoverRole?: string;\n *   position?: import(\"@web/core/position/position_hook\").UsePositionOptions[\"position\"];\n *   ref?: Function;\n * }} PopoverServiceAddOptions\n *\n * @typedef {ReturnType<popoverService[\"start\"]>[\"add\"]} PopoverServiceAddFunction\n */\n\nexport const popoverService = {\n    dependencies: [\"overlay\"],\n    start(_, { overlay }) {\n        /**\n         * Signals the manager to add a popover.\n         *\n         * @param {HTMLElement} target\n         * @param {typeof import(\"@odoo/owl\").Component} component\n         * @param {object} [props]\n         * @param {PopoverServiceAddOptions} [options]\n         * @returns {() => void}\n         */\n        const add = (target, component, props = {}, options = {}) => {\n            const closeOnClickAway =\n                typeof options.closeOnClickAway === \"function\"\n                    ? options.closeOnClickAway\n                    : () => options.closeOnClickAway ?? true;\n            const remove = overlay.add(\n                Popover,\n                {\n                    target,\n                    close: () => remove(),\n                    closeOnClickAway,\n                    closeOnEscape: options.closeOnEscape,\n                    component,\n                    componentProps: markRaw(props),\n                    ref: options.ref,\n                    class: options.popoverClass,\n                    animation: options.animation,\n                    arrow: options.arrow,\n                    role: options.popoverRole,\n                    position: options.position,\n                    onPositioned: options.onPositioned,\n                    fixedPosition: options.fixedPosition,\n                    holdOnHover: options.holdOnHover,\n                    setActiveElement: options.setActiveElement ?? true,\n                },\n                {\n                    env: options.env,\n                    onRemove: options.onClose,\n                    rootId: target.getRootNode()?.host?.id,\n                }\n            );\n\n            return remove;\n        };\n\n        return { add };\n    },\n};\n\nregistry.category(\"services\").add(\"popover\", popoverService);\n", "import { reposition } from \"@web/core/position/utils\";\nimport { omit } from \"@web/core/utils/objects\";\nimport { useThrottleForAnimation } from \"@web/core/utils/timing\";\nimport {\n    EventBus,\n    onWillDestroy,\n    useChildSubEnv,\n    useComponent,\n    useEffect,\n    useRef,\n} from \"@odoo/owl\";\n\n/**\n * @typedef {import(\"@web/core/position/utils\").ComputePositionOptions} ComputePositionOptions\n * @typedef {import(\"@web/core/position/utils\").PositioningSolution} PositioningSolution\n *\n * @typedef {Object} UsePositionOptionsExtensionType\n * @property {(popperElement: HTMLElement, solution: PositioningSolution) => void} [onPositioned]\n *  callback called when the positioning is done.\n * @typedef {ComputePositionOptions & UsePositionOptionsExtensionType} UsePositionOptions\n *\n * @typedef PositioningControl\n * @property {() => void} lock prevents further positioning updates\n * @property {() => void} unlock allows further positioning updates (triggers an update right away)\n */\n\n/** @type {UsePositionOptions} */\nconst DEFAULTS = {\n    margin: 0,\n    position: \"bottom\",\n};\n\nconst POSITION_BUS = Symbol(\"position-bus\");\n\n/**\n * Makes sure that the `popper` element is always\n * placed at `position` from the `target` element.\n * If doing so the `popper` element is clipped off `container`,\n * sensible fallback positions are tried.\n * If all of fallback positions are also clipped off `container`,\n * the original position is used.\n *\n * Note: The popper element should be indicated in your template\n *       with a t-ref reference matching the refName argument.\n *\n * @param {string} refName\n *  name of the reference to the popper element in the template.\n * @param {() => HTMLElement} getTarget\n * @param {UsePositionOptions} [options={}] the options to be used for positioning\n * @returns {PositioningControl}\n *  control object to lock/unlock the positioning.\n */\nexport function usePosition(refName, getTarget, options = {}) {\n    const ref = useRef(refName);\n    let lock = false;\n    const update = () => {\n        const targetEl = getTarget();\n        if (!ref.el || !targetEl?.isConnected || lock) {\n            // No compute needed\n            return;\n        }\n        const repositionOptions = { ...DEFAULTS, ...omit(options, \"onPositioned\") };\n        const solution = reposition(ref.el, targetEl, repositionOptions);\n        options.onPositioned?.(ref.el, solution);\n    };\n\n    const component = useComponent();\n    const bus = component.env[POSITION_BUS] || new EventBus();\n\n    let executingUpdate = false;\n    const batchedUpdate = async () => {\n        // not same as batch, here we're executing once and then awaiting\n        if (!executingUpdate) {\n            executingUpdate = true;\n            update();\n            await Promise.resolve();\n            executingUpdate = false;\n        }\n    };\n    bus.addEventListener(\"update\", batchedUpdate);\n    onWillDestroy(() => bus.removeEventListener(\"update\", batchedUpdate));\n\n    const isTopmost = !(POSITION_BUS in component.env);\n    if (isTopmost) {\n        useChildSubEnv({ [POSITION_BUS]: bus });\n    }\n\n    const throttledUpdate = useThrottleForAnimation(() => bus.trigger(\"update\"));\n    useEffect(() => {\n        // Reposition\n        bus.trigger(\"update\");\n\n        if (isTopmost) {\n            // Attach listeners to keep the positioning up to date\n            const scrollListener = (e) => {\n                if (ref.el?.contains(e.target)) {\n                    // In case the scroll event occurs inside the popper, do not reposition\n                    return;\n                }\n                throttledUpdate();\n            };\n            const targetDocument = getTarget()?.ownerDocument;\n            targetDocument?.addEventListener(\"scroll\", scrollListener, { capture: true });\n            targetDocument?.addEventListener(\"load\", throttledUpdate, { capture: true });\n            window.addEventListener(\"resize\", throttledUpdate);\n            return () => {\n                targetDocument?.removeEventListener(\"scroll\", scrollListener, { capture: true });\n                targetDocument?.removeEventListener(\"load\", throttledUpdate, { capture: true });\n                window.removeEventListener(\"resize\", throttledUpdate);\n            };\n        }\n    });\n\n    return {\n        lock: () => {\n            lock = true;\n        },\n        unlock: () => {\n            lock = false;\n            bus.trigger(\"update\");\n        },\n    };\n}\n", "import { localization } from \"@web/core/l10n/localization\";\n\n/**\n * @typedef {\"top\" | \"left\" | \"bottom\" | \"right\"} Direction\n * @typedef {\"start\" | \"middle\" | \"end\" | \"fit\"} Variant\n *\n * @typedef {{[direction in Direction]: string}} DirectionFlipOrder\n *  string values should match regex /^[tbrl]+$/m\n *\n * @typedef {{[variant in Variant]: string}} VariantFlipOrder\n *  string values should match regex /^[smef]+$/m\n *\n * @typedef {{\n *  top: number,\n *  left: number,\n *  direction: Direction,\n *  variant: Variant,\n * }} PositioningSolution\n *\n * @typedef ComputePositionOptions\n * @property {HTMLElement | () => HTMLElement} [container] container element\n * @property {number} [margin=0]\n *  margin in pixels between the popper and the target.\n * @property {Direction | `${Direction}-${Variant}`} [position=\"bottom\"]\n *  position of the popper relative to the target\n */\n\n/** @type {{[d: string]: Direction}} */\nconst DIRECTIONS = { t: \"top\", r: \"right\", b: \"bottom\", l: \"left\" };\n/** @type {{[v: string]: Variant}} */\nconst VARIANTS = { s: \"start\", m: \"middle\", e: \"end\", f: \"fit\" };\n/** @type DirectionFlipOrder */\nconst DIRECTION_FLIP_ORDER = { top: \"tbrl\", right: \"rltb\", bottom: \"btrl\", left: \"lrbt\" };\n/** @type VariantFlipOrder */\nconst VARIANT_FLIP_ORDER = { start: \"sme\", middle: \"mse\", end: \"ems\", fit: \"f\" };\n/** @type DirectionFlipOrder */\nconst FIT_FLIP_ORDER = { top: \"tb\", right: \"rl\", bottom: \"bt\", left: \"lr\" };\n\n/**\n * @param {HTMLElement} popperEl\n * @param {HTMLElement} targetEl\n * @returns {HTMLIFrameElement?}\n */\nfunction getIFrame(popperEl, targetEl) {\n    return [...popperEl.ownerDocument.getElementsByTagName(\"iframe\")].find((iframe) =>\n        iframe.contentDocument?.contains(targetEl)\n    );\n}\n\n/**\n * Returns the best positioning solution staying in the container or falls back\n * to the requested position.\n * The positioning data used to determine each possible position is based on\n * the target, popper, and container sizes.\n * Particularly, a popper must not overflow the container in any direction.\n * The popper will stay at `margin` distance from its target. One could also\n * use the CSS margins of the popper element to achieve the same result.\n *\n * Pre-condition: the popper element must have a fixed positioning\n *                with top and left set to 0px.\n *\n * @param {HTMLElement} popper\n * @param {HTMLElement} target\n * @param {ComputePositionOptions} options\n * @returns {PositioningSolution} the best positioning solution, relative to\n *                                the containing block of the popper.\n *                                => can be applied to popper.style.(top|left)\n */\nfunction computePosition(popper, target, { container, margin, position }) {\n    // Retrieve directions and variants\n    let [direction, variant = \"middle\"] = position.split(\"-\");\n    if (localization.direction === \"rtl\") {\n        if ([\"left\", \"right\"].includes(direction)) {\n            direction = direction === \"left\" ? \"right\" : \"left\";\n        } else if ([\"start\", \"end\"].includes(variant)) {\n            // here direction is either \"top\" or \"bottom\"\n            variant = variant === \"start\" ? \"end\" : \"start\";\n        }\n    }\n    const directions =\n        variant === \"fit\" ? FIT_FLIP_ORDER[direction] : DIRECTION_FLIP_ORDER[direction];\n    const variants = VARIANT_FLIP_ORDER[variant];\n\n    // Retrieve container\n    if (!container) {\n        container = popper.ownerDocument.documentElement;\n    } else if (typeof container === \"function\") {\n        container = container();\n    }\n\n    // Account for popper actual margins\n    const popperStyle = getComputedStyle(popper);\n    const { marginTop, marginLeft, marginRight, marginBottom } = popperStyle;\n    const popMargins = {\n        top: parseFloat(marginTop),\n        left: parseFloat(marginLeft),\n        right: parseFloat(marginRight),\n        bottom: parseFloat(marginBottom),\n    };\n\n    // IFrame\n    const shouldAccountForIFrame = popper.ownerDocument !== target.ownerDocument;\n    const iframe = shouldAccountForIFrame ? getIFrame(popper, target) : null;\n\n    // Boxes\n    const popBox = popper.getBoundingClientRect();\n    const targetBox = target.getBoundingClientRect();\n    const contBox = container.getBoundingClientRect();\n    const iframeBox = iframe?.getBoundingClientRect() ?? { top: 0, left: 0 };\n\n    const containerIsHTMLNode = container === container.ownerDocument.firstElementChild;\n\n    // Compute positioning data\n    const directionsData = {\n        t: iframeBox.top + targetBox.top - popMargins.bottom - margin - popBox.height,\n        b: iframeBox.top + targetBox.bottom + popMargins.top + margin,\n        r: iframeBox.left + targetBox.right + popMargins.left + margin,\n        l: iframeBox.left + targetBox.left - popMargins.right - margin - popBox.width,\n    };\n    const variantsData = {\n        vf: iframeBox.left + targetBox.left,\n        vs: iframeBox.left + targetBox.left + popMargins.left,\n        vm: iframeBox.left + targetBox.left + targetBox.width / 2 - popBox.width / 2,\n        ve: iframeBox.left + targetBox.right - popMargins.right - popBox.width,\n        hf: iframeBox.top + targetBox.top,\n        hs: iframeBox.top + targetBox.top + popMargins.top,\n        hm: iframeBox.top + targetBox.top + targetBox.height / 2 - popBox.height / 2,\n        he: iframeBox.top + targetBox.bottom - popMargins.bottom - popBox.height,\n    };\n\n    function getPositioningData(d = directions[0], v = variants[0], containerRestricted = false) {\n        const vertical = [\"t\", \"b\"].includes(d);\n        const variantPrefix = vertical ? \"v\" : \"h\";\n        const directionValue = directionsData[d];\n        const variantValue = variantsData[variantPrefix + v];\n\n        if (containerRestricted) {\n            const [directionSize, variantSize] = vertical\n                ? [popBox.height, popBox.width]\n                : [popBox.width, popBox.height];\n            let [directionMin, directionMax] = vertical\n                ? [contBox.top, contBox.bottom]\n                : [contBox.left, contBox.right];\n            let [variantMin, variantMax] = vertical\n                ? [contBox.left, contBox.right]\n                : [contBox.top, contBox.bottom];\n\n            if (containerIsHTMLNode) {\n                if (vertical) {\n                    directionMin += container.scrollTop;\n                    directionMax += container.scrollTop;\n                } else {\n                    variantMin += container.scrollTop;\n                    variantMax += container.scrollTop;\n                }\n            }\n\n            // Abort if outside container boundaries\n            const directionOverflow =\n                Math.ceil(directionValue) < Math.floor(directionMin) ||\n                Math.floor(directionValue + directionSize) > Math.ceil(directionMax);\n            const variantOverflow =\n                Math.ceil(variantValue) < Math.floor(variantMin) ||\n                Math.floor(variantValue + variantSize) > Math.ceil(variantMax);\n            if (directionOverflow || variantOverflow) {\n                return null;\n            }\n        }\n\n        const positioning = vertical\n            ? { top: directionValue, left: variantValue }\n            : { top: variantValue, left: directionValue };\n        return {\n            // Subtract the offsets of the containing block (relative to the\n            // viewport). It can be done like that because the style top and\n            // left were reset to 0px in `reposition`\n            // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n            top: positioning.top - popBox.top,\n            left: positioning.left - popBox.left,\n            direction: DIRECTIONS[d],\n            variant: VARIANTS[v],\n        };\n    }\n\n    // Find best solution\n    for (const d of directions) {\n        for (const v of variants) {\n            const match = getPositioningData(d, v, true);\n            if (match) {\n                // Position match have been found.\n                return match;\n            }\n        }\n    }\n\n    // Fallback to default position if no best solution found\n    return getPositioningData();\n}\n\n/**\n * Repositions the popper element relatively to the target element (according to options).\n * The positioning strategy is always a fixed positioning with top and left.\n *\n * The positioning solution is returned by the `computePosition` function.\n * It will get applied to the popper element and then returned for convenience.\n *\n * @param {HTMLElement} popper\n * @param {HTMLElement} target\n * @param {ComputePositionOptions} options\n * @returns {PositioningSolution} the applied positioning solution.\n */\nexport function reposition(popper, target, options) {\n    // Reset popper style\n    popper.style.position = \"fixed\";\n    popper.style.top = \"0px\";\n    popper.style.left = \"0px\";\n\n    // Compute positioning solution\n    const solution = computePosition(popper, target, options);\n\n    // Apply it\n    const { top, left, direction, variant } = solution;\n    popper.style.top = `${top}px`;\n    popper.style.left = `${left}px`;\n    if (variant === \"fit\") {\n        const styleProperty = [\"top\", \"bottom\"].includes(direction) ? \"width\" : \"height\";\n        popper.style[styleProperty] = target.getBoundingClientRect()[styleProperty] + \"px\";\n    }\n\n    return solution;\n}\n", "import { Component } from \"@odoo/owl\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { isIOS } from \"@web/core/browser/feature_detection\";\n\nexport class InstallPrompt extends Component {\n    static props = {\n        close: true,\n        onClose: { type: Function },\n    };\n    static components = {\n        Dialog,\n    };\n    static template = \"web.InstallPrompt\";\n\n    get isMobileSafari() {\n        return isIOS();\n    }\n\n    onClose() {\n        this.props.close();\n        this.props.onClose();\n    }\n}\n", "import { reactive } from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\nimport {\n    isDisplayStandalone,\n    isIOS,\n    isMacOS,\n    isBrowserSafari,\n} from \"@web/core/browser/feature_detection\";\nimport { get } from \"@web/core/network/http_service\";\nimport { registry } from \"@web/core/registry\";\nimport { InstallPrompt } from \"./install_prompt\";\n\nconst serviceRegistry = registry.category(\"services\");\n\n/* Ideally, the service would directly add the event listener. Unfortunately, it happens sometimes that\n * the browser would trigger the event before the webclient (services, components, etc.) is even ready.\n * In that case, we have to get this event as soon as possible. The service can then verify if the event\n * is already stored in this variable, or add an event listener itself, to make sure the `_handleBeforeInstallPrompt`\n * function is called at the right moment, and can give the correct information to the service.\n */\nlet BEFOREINSTALLPROMPT_EVENT;\nlet REGISTER_BEFOREINSTALLPROMPT_EVENT;\n\nbrowser.addEventListener(\"beforeinstallprompt\", (ev) => {\n    // This event is only triggered by the browser when the native prompt to install can be shown\n    // This excludes incognito tabs, as well as visiting the website while the app is installed\n    if (REGISTER_BEFOREINSTALLPROMPT_EVENT) {\n        // service has been started before the event was triggered, update the service\n        return REGISTER_BEFOREINSTALLPROMPT_EVENT(ev);\n    } else {\n        // store the event for later use\n        BEFOREINSTALLPROMPT_EVENT = ev;\n    }\n});\n\nconst pwaService = {\n    dependencies: [\"dialog\"],\n    start(env, { dialog }) {\n        let _manifest;\n        let nativePrompt;\n\n        const state = reactive({\n            canPromptToInstall: false,\n            isAvailable: false,\n            isScopedApp: browser.location.href.includes(\"/scoped_app\"),\n            isSupportedOnBrowser: false,\n            startUrl: \"/odoo\",\n            decline,\n            getManifest,\n            hasScopeBeenInstalled,\n            show,\n        });\n\n        function _getInstallationState(scope = state.startUrl) {\n            const installationState = browser.localStorage.getItem(\"pwaService.installationState\");\n            return installationState ? JSON.parse(installationState)[scope] : \"\";\n        }\n\n        function _setInstallationState(value) {\n            const ls = JSON.parse(\n                browser.localStorage.getItem(\"pwaService.installationState\") || \"{}\"\n            );\n            ls[state.startUrl] = value;\n            browser.localStorage.setItem(\"pwaService.installationState\", JSON.stringify(ls));\n        }\n\n        function _removeInstallationState() {\n            const ls = JSON.parse(browser.localStorage.getItem(\"pwaService.installationState\"));\n            delete ls[state.startUrl];\n            browser.localStorage.setItem(\"pwaService.installationState\", JSON.stringify(ls));\n        }\n\n        if (state.isScopedApp) {\n            if (browser.location.pathname === \"/scoped_app\") {\n                // Installation page, use the path parameter in the URL\n                state.startUrl = \"/\" + new URL(browser.location.href).searchParams.get(\"path\");\n            } else {\n                state.startUrl = browser.location.pathname;\n            }\n        }\n\n        // The PWA can only be installed if the app is not already launched (display-mode standalone)\n        // For Apple devices, PWA are supported on any mobile version of Safari, or in desktop since version 17\n        // On Safari devices, the check is also done on the display-mode and we rely on the installationState to\n        // decide whether we must show the prompt or not\n        state.isSupportedOnBrowser =\n            browser.BeforeInstallPromptEvent !== undefined ||\n            (isBrowserSafari() &&\n                !isDisplayStandalone() &&\n                (isIOS() ||\n                    (isMacOS() && browser.navigator.userAgent.match(/Version\\/(\\d+)/)[1] >= 17)));\n\n        const installationState = _getInstallationState();\n\n        if (state.isSupportedOnBrowser) {\n            if (BEFOREINSTALLPROMPT_EVENT) {\n                _handleBeforeInstallPrompt(BEFOREINSTALLPROMPT_EVENT, installationState);\n                BEFOREINSTALLPROMPT_EVENT = null; // clear this variable as it is no longer useful\n            }\n            // If a user declines the prompt, the browser would triggered it once again. We must be able to catch it\n            REGISTER_BEFOREINSTALLPROMPT_EVENT = (ev) => {\n                _handleBeforeInstallPrompt(ev, installationState);\n            };\n            if (isBrowserSafari()) {\n                // since those platforms don't rely on the beforeinstallprompt event, we handle it ourselves\n                state.canPromptToInstall = installationState !== \"dismissed\";\n                state.isAvailable = true;\n            }\n        }\n\n        function _handleBeforeInstallPrompt(ev, installationState) {\n            nativePrompt = ev;\n            if (installationState === \"accepted\") {\n                // If this event is triggered with the installationState stored, it means that the app has been\n                // removed since its installation. The prompt can be displayed, and the installation state is reset.\n                if (!isDisplayStandalone()) {\n                    // In Scoped Apps, the event might be triggered if a manifest with a different scope is available\n                    _removeInstallationState();\n                }\n            }\n            state.canPromptToInstall = installationState !== \"dismissed\";\n            state.isAvailable = true;\n        }\n\n        async function getManifest() {\n            if (!_manifest) {\n                const manifest = await get(\n                    document.querySelector(\"link[rel=manifest\")?.getAttribute(\"href\"),\n                    \"text\"\n                );\n                _manifest = JSON.parse(manifest);\n            }\n            return _manifest;\n        }\n\n        // This function don't guarantee the scope is still currently installed on the device\n        // The only way to know that is by relying on the BeforeInstallPrompt event from the\n        // page linking the app manifest. This only serves to indicate that the app has previously\n        // been installed\n        function hasScopeBeenInstalled(scope) {\n            return _getInstallationState(scope) === \"accepted\";\n        }\n\n        async function show({ onDone } = {}) {\n            if (!state.isAvailable) {\n                return;\n            }\n            if (nativePrompt) {\n                const res = await nativePrompt.prompt();\n                _setInstallationState(res.outcome);\n                state.canPromptToInstall = false;\n                if (onDone) {\n                    onDone(res);\n                }\n            } else if (isBrowserSafari()) {\n                // since those platforms don't support a native installation prompt yet, we\n                // show a custom dialog to explain how to pin the app to the application menu\n                dialog.add(InstallPrompt, {\n                    onClose: () => {\n                        if (onDone) {\n                            onDone({});\n                        }\n                        this.decline();\n                    },\n                });\n            }\n        }\n\n        function decline() {\n            _setInstallationState(\"dismissed\");\n            state.canPromptToInstall = false;\n        }\n\n        return state;\n    },\n};\nserviceRegistry.add(\"pwa\", pwaService);\n", "import { evaluate } from \"./py_interpreter\";\nimport { parse } from \"./py_parser\";\nimport { tokenize } from \"./py_tokenizer\";\n\nexport { evaluate } from \"./py_interpreter\";\nexport { parse } from \"./py_parser\";\nexport { tokenize } from \"./py_tokenizer\";\nexport { formatAST } from \"./py_utils\";\n\n/**\n * @typedef { import(\"./py_tokenizer\").Token } Token\n * @typedef { import(\"./py_parser\").AST } AST\n */\n\n/**\n * Parses an expression into a valid AST representation\n\n * @param {string} expr\n * @returns { AST }\n */\nexport function parseExpr(expr) {\n    const tokens = tokenize(expr);\n    return parse(tokens);\n}\n\n/**\n * Evaluates a python expression\n *\n * @param {string} expr\n * @param {Object} [context]\n * @returns {any}\n */\nexport function evaluateExpr(expr, context = {}) {\n    let ast;\n    try {\n        ast = parseExpr(expr);\n    } catch (error) {\n        throw new EvalError(`Can not parse python expression: (${expr})\\nError: ${error.message}`);\n    }\n    try {\n        return evaluate(ast, context);\n    } catch (error) {\n        throw new EvalError(`Can not evaluate python expression: (${expr})\\nError: ${error.message}`);\n    }\n}\n\n/**\n * Evaluates a python expression to return a boolean.\n *\n * @param {string} expr\n * @param {Object} [context]\n * @returns {any}\n */\nexport function evaluateBooleanExpr(expr, context = {}) {\n    if (!expr || expr === 'False' || expr === '0') {\n        return false;\n    }\n    if (expr === 'True' || expr === '1') {\n        return true;\n    }\n    return evaluateExpr(`bool(${expr})`, context);\n}\n", "import { PyDate, PyDateTime, PyRelativeDelta, PyTime, PyTimeDelta } from \"./py_date\";\n\nexport class EvaluationError extends Error {}\n\n/**\n * @param {any} iterable\n * @param {Function} func\n */\nexport function execOnIterable(iterable, func) {\n    if (iterable === null) {\n        // new Set(null) is fine in js but set(None) (-> new Set(null))\n        // is not in Python\n        throw new EvaluationError(`value not iterable`);\n    }\n    if (typeof iterable === \"object\" && !Array.isArray(iterable) && !(iterable instanceof Set)) {\n        // dicts are considered as iterable in Python\n        iterable = Object.keys(iterable);\n    }\n    if (typeof iterable?.[Symbol.iterator] !== \"function\") {\n        // rules out undefined and other values not iterable\n        throw new EvaluationError(`value not iterable`);\n    }\n    return func(iterable);\n}\n\nexport const BUILTINS = {\n    /**\n     * @param {any} value\n     * @returns {boolean}\n     */\n    bool(value) {\n        switch (typeof value) {\n            case \"number\":\n                return value !== 0;\n            case \"string\":\n                return value !== \"\";\n            case \"boolean\":\n                return value;\n            case \"object\":\n                if (value === null || value === undefined) {\n                    return false;\n                }\n                if (value.isTrue) {\n                    return value.isTrue();\n                }\n                if (value instanceof Array) {\n                    return !!value.length;\n                }\n                if (value instanceof Set) {\n                    return !!value.size;\n                }\n                return Object.keys(value).length !== 0;\n        }\n        return true;\n    },\n\n    set(iterable) {\n        if (arguments.length > 2) {\n            // we always receive at least one argument: kwargs (return fnValue(...args, kwargs); in FunctionCall case)\n            throw new EvaluationError(\n                `set expected at most 1 argument, got (${arguments.length - 1}`\n            );\n        }\n        return execOnIterable(iterable, (iterable) => {\n            return new Set(iterable);\n        });\n    },\n\n    max(...args) {\n        // kwargs are not supported by Math.max.\n        return Math.max(...args.slice(0, -1));\n    },\n\n    min(...args) {\n        // kwargs are not supported by Math.min.\n        return Math.min(...args.slice(0, -1));\n    },\n\n    time: {\n        strftime(format) {\n            return PyDateTime.now().strftime(format);\n        },\n    },\n\n    context_today() {\n        return PyDate.today();\n    },\n\n    get current_date() {\n        // deprecated: today should be prefered\n        return this.today;\n    },\n\n    get today() {\n        return PyDate.today().strftime(\"%Y-%m-%d\");\n    },\n\n    get now() {\n        return PyDateTime.now().strftime(\"%Y-%m-%d %H:%M:%S\");\n    },\n\n    datetime: {\n        time: PyTime,\n        timedelta: PyTimeDelta,\n        datetime: PyDateTime,\n        date: PyDate,\n    },\n\n    relativedelta: PyRelativeDelta,\n\n    true: true,\n    false: false,\n};\n", "import { parseArgs } from \"./py_parser\";\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\n\nexport class AssertionError extends Error {}\nexport class ValueError extends Error {}\nexport class NotSupportedError extends Error {}\n\n// -----------------------------------------------------------------------------\n// helpers\n// -----------------------------------------------------------------------------\n\nfunction fmt2(n) {\n    return String(n).padStart(2, \"0\");\n}\nfunction fmt4(n) {\n    return String(n).padStart(4, \"0\");\n}\n\n/**\n * computes (Math.floor(a/b), a%b and passes that to the callback.\n *\n * returns the callback's result\n */\nfunction divmod(a, b, fn) {\n    let mod = a % b;\n    // in python, sign(a % b) === sign(b). Not in JS. If wrong side, add a\n    // round of b\n    if ((mod > 0 && b < 0) || (mod < 0 && b > 0)) {\n        mod += b;\n    }\n    return fn(Math.floor(a / b), mod);\n}\n\nfunction assert(bool, message = \"AssertionError\") {\n    if (!bool) {\n        throw new AssertionError(message);\n    }\n}\n\nconst DAYS_IN_MONTH = [null, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst DAYS_BEFORE_MONTH = [null];\n\nfor (let dbm = 0, i = 1; i < DAYS_IN_MONTH.length; ++i) {\n    DAYS_BEFORE_MONTH.push(dbm);\n    dbm += DAYS_IN_MONTH[i];\n}\n\nfunction daysInMonth(year, month) {\n    if (month === 2 && isLeap(year)) {\n        return 29;\n    }\n    return DAYS_IN_MONTH[month];\n}\n\nfunction isLeap(year) {\n    return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n}\n\nfunction daysBeforeYear(year) {\n    const y = year - 1;\n    return y * 365 + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400);\n}\n\nfunction daysBeforeMonth(year, month) {\n    const postLeapFeb = month > 2 && isLeap(year);\n    return DAYS_BEFORE_MONTH[month] + (postLeapFeb ? 1 : 0);\n}\n\nfunction ymd2ord(year, month, day) {\n    const dim = daysInMonth(year, month);\n    if (!(1 <= day && day <= dim)) {\n        throw new ValueError(`day must be in 1..${dim}`);\n    }\n    return daysBeforeYear(year) + daysBeforeMonth(year, month) + day;\n}\n\nconst DI400Y = daysBeforeYear(401);\nconst DI100Y = daysBeforeYear(101);\nconst DI4Y = daysBeforeYear(5);\n\nfunction ord2ymd(n) {\n    --n;\n    let n400, n100, n4, n1, n0;\n    divmod(n, DI400Y, function (_n400, n) {\n        n400 = _n400;\n        divmod(n, DI100Y, function (_n100, n) {\n            n100 = _n100;\n            divmod(n, DI4Y, function (_n4, n) {\n                n4 = _n4;\n                divmod(n, 365, function (_n1, n) {\n                    n1 = _n1;\n                    n0 = n;\n                });\n            });\n        });\n    });\n\n    n = n0;\n    const year = n400 * 400 + 1 + n100 * 100 + n4 * 4 + n1;\n    if (n1 == 4 || n100 == 100) {\n        assert(n0 === 0);\n        return {\n            year: year - 1,\n            month: 12,\n            day: 31,\n        };\n    }\n\n    const leapyear = n1 === 3 && (n4 !== 24 || n100 == 3);\n    assert(leapyear == isLeap(year));\n    let month = (n + 50) >> 5;\n    let preceding = DAYS_BEFORE_MONTH[month] + (month > 2 && leapyear ? 1 : 0);\n    if (preceding > n) {\n        --month;\n        preceding -= DAYS_IN_MONTH[month] + (month === 2 && leapyear ? 1 : 0);\n    }\n    n -= preceding;\n    return {\n        year: year,\n        month: month,\n        day: n + 1,\n    };\n}\n\n/**\n * Converts the stuff passed in into a valid date, applying overflows as needed\n */\nfunction tmxxx(year, month, day, hour, minute, second, microsecond) {\n    hour = hour || 0;\n    minute = minute || 0;\n    second = second || 0;\n    microsecond = microsecond || 0;\n\n    if (microsecond < 0 || microsecond > 999999) {\n        divmod(microsecond, 1000000, function (carry, ms) {\n            microsecond = ms;\n            second += carry;\n        });\n    }\n    if (second < 0 || second > 59) {\n        divmod(second, 60, function (carry, s) {\n            second = s;\n            minute += carry;\n        });\n    }\n    if (minute < 0 || minute > 59) {\n        divmod(minute, 60, function (carry, m) {\n            minute = m;\n            hour += carry;\n        });\n    }\n    if (hour < 0 || hour > 23) {\n        divmod(hour, 24, function (carry, h) {\n            hour = h;\n            day += carry;\n        });\n    }\n    // That was easy.  Now it gets muddy:  the proper range for day\n    // can't be determined without knowing the correct month and year,\n    // but if day is, e.g., plus or minus a million, the current month\n    // and year values make no sense (and may also be out of bounds\n    // themselves).\n    // Saying 12 months == 1 year should be non-controversial.\n    if (month < 1 || month > 12) {\n        divmod(month - 1, 12, function (carry, m) {\n            month = m + 1;\n            year += carry;\n        });\n    }\n    // Now only day can be out of bounds (year may also be out of bounds\n    // for a datetime object, but we don't care about that here).\n    // If day is out of bounds, what to do is arguable, but at least the\n    // method here is principled and explainable.\n    const dim = daysInMonth(year, month);\n    if (day < 1 || day > dim) {\n        // Move day-1 days from the first of the month.  First try to\n        // get off cheap if we're only one day out of range (adjustments\n        // for timezone alone can't be worse than that).\n        if (day === 0) {\n            --month;\n            if (month > 0) {\n                day = daysInMonth(year, month);\n            } else {\n                --year;\n                month = 12;\n                day = 31;\n            }\n        } else if (day == dim + 1) {\n            ++month;\n            day = 1;\n            if (month > 12) {\n                month = 1;\n                ++year;\n            }\n        } else {\n            const r = ord2ymd(ymd2ord(year, month, 1) + (day - 1));\n            year = r.year;\n            month = r.month;\n            day = r.day;\n        }\n    }\n    return {\n        year: year,\n        month: month,\n        day: day,\n        hour: hour,\n        minute: minute,\n        second: second,\n        microsecond: microsecond,\n    };\n}\n\n// -----------------------------------------------------------------------------\n// Date/Time and related classes\n// -----------------------------------------------------------------------------\n\nexport class PyDate {\n    /**\n     * @returns {PyDate}\n     */\n    static today() {\n        return this.convertDate(new Date());\n    }\n\n    /**\n     * Convert a date object into PyDate\n     * @param {Date} date\n     * @returns {PyDate}\n     */\n    static convertDate(date) {\n        const year = date.getFullYear();\n        const month = date.getMonth() + 1;\n        const day = date.getDate();\n        return new PyDate(year, month, day);\n    }\n\n    /**\n     * @param {integer} year\n     * @param {integer} month\n     * @param {integer} day\n     */\n    constructor(year, month, day) {\n        this.year = year;\n        this.month = month; // 1-indexed => 1 = january, 2 = february, ...\n        this.day = day; // 1-indexed => 1 = first day of month, ...\n    }\n\n    /**\n     * @param  {...any} args\n     * @returns {PyDate}\n     */\n    static create(...args) {\n        const { year, month, day } = parseArgs(args, [\"year\", \"month\", \"day\"]);\n        return new PyDate(year, month, day);\n    }\n\n    /**\n     * @param {PyTimeDelta} timedelta\n     * @returns {PyDate}\n     */\n    add(timedelta) {\n        const s = tmxxx(this.year, this.month, this.day + timedelta.days);\n        return new PyDate(s.year, s.month, s.day);\n    }\n\n    /**\n     * @param {any} other\n     * @returns {boolean}\n     */\n    isEqual(other) {\n        if (!(other instanceof PyDate)) {\n            return false;\n        }\n        return this.year === other.year && this.month === other.month && this.day === other.day;\n    }\n\n    /**\n     * @param {string} format\n     * @returns {string}\n     */\n    strftime(format) {\n        return format.replace(/%([A-Za-z])/g, (m, c) => {\n            switch (c) {\n                case \"Y\":\n                    return fmt4(this.year);\n                case \"m\":\n                    return fmt2(this.month);\n                case \"d\":\n                    return fmt2(this.day);\n            }\n            throw new ValueError(`No known conversion for ${m}`);\n        });\n    }\n\n    /**\n     * @param {PyTimeDelta | PyDate} other\n     * @returns {PyDate | PyTimeDelta}\n     */\n    substract(other) {\n        if (other instanceof PyTimeDelta) {\n            return this.add(other.negate());\n        }\n        if (other instanceof PyDate) {\n            return PyTimeDelta.create(this.toordinal() - other.toordinal());\n        }\n        throw NotSupportedError();\n    }\n\n    /**\n     * @returns {string}\n     */\n    toJSON() {\n        return this.strftime(\"%Y-%m-%d\");\n    }\n\n    /**\n     * @returns {integer}\n     */\n    toordinal() {\n        return ymd2ord(this.year, this.month, this.day);\n    }\n}\n\nexport class PyDateTime {\n    /**\n     * @returns {PyDateTime}\n     */\n    static now() {\n        return this.convertDate(new Date());\n    }\n\n    /**\n     * Convert a date object into PyDateTime\n     * @param {Date} date\n     * @returns {PyDateTime}\n     */\n    static convertDate(date) {\n        const year = date.getFullYear();\n        const month = date.getMonth() + 1;\n        const day = date.getDate();\n        const hour = date.getHours();\n        const minute = date.getMinutes();\n        const second = date.getSeconds();\n        return new PyDateTime(year, month, day, hour, minute, second, 0);\n    }\n\n    /**\n     * @param  {...any} args\n     * @returns {PyDateTime}\n     */\n    static create(...args) {\n        const namedArgs = parseArgs(args, [\n            \"year\",\n            \"month\",\n            \"day\",\n            \"hour\",\n            \"minute\",\n            \"second\",\n            \"microsecond\",\n        ]);\n        const year = namedArgs.year;\n        const month = namedArgs.month;\n        const day = namedArgs.day;\n        const hour = namedArgs.hour || 0;\n        const minute = namedArgs.minute || 0;\n        const second = namedArgs.second || 0;\n        const ms = namedArgs.micro / 1000 || 0;\n        return new PyDateTime(year, month, day, hour, minute, second, ms);\n    }\n\n    /**\n     * @param  {...any} args\n     * @returns {PyDateTime}\n     */\n    static combine(...args) {\n        const { date, time } = parseArgs(args, [\"date\", \"time\"]);\n        // not sure. should we go through constructor instead? what about args normalization?\n        return PyDateTime.create(\n            date.year,\n            date.month,\n            date.day,\n            time.hour,\n            time.minute,\n            time.second\n        );\n    }\n\n    /**\n     * @param {integer} year\n     * @param {integer} month\n     * @param {integer} day\n     * @param {integer} hour\n     * @param {integer} minute\n     * @param {integer} second\n     * @param {integer} microsecond\n     */\n    constructor(year, month, day, hour, minute, second, microsecond) {\n        this.year = year;\n        this.month = month; // 1-indexed => 1 = january, 2 = february, ...\n        this.day = day; // 1-indexed => 1 = first day of month, ...\n        this.hour = hour;\n        this.minute = minute;\n        this.second = second;\n        this.microsecond = microsecond;\n    }\n\n    /**\n     * @param {PyTimeDelta} timedelta\n     * @returns {PyDate}\n     */\n    add(timedelta) {\n        const s = tmxxx(\n            this.year,\n            this.month,\n            this.day + timedelta.days,\n            this.hour,\n            this.minute,\n            this.second + timedelta.seconds,\n            this.microsecond + timedelta.microseconds\n        );\n        // does not seem to closely follow python implementation.\n        return new PyDateTime(s.year, s.month, s.day, s.hour, s.minute, s.second, s.microsecond);\n    }\n\n    /**\n     * @param {any} other\n     * @returns {boolean}\n     */\n    isEqual(other) {\n        if (!(other instanceof PyDateTime)) {\n            return false;\n        }\n        return (\n            this.year === other.year &&\n            this.month === other.month &&\n            this.day === other.day &&\n            this.hour === other.hour &&\n            this.minute === other.minute &&\n            this.second === other.second &&\n            this.microsecond === other.microsecond\n        );\n    }\n\n    /**\n     * @param {string} format\n     * @returns {string}\n     */\n    strftime(format) {\n        return format.replace(/%([A-Za-z])/g, (m, c) => {\n            switch (c) {\n                case \"Y\":\n                    return fmt4(this.year);\n                case \"m\":\n                    return fmt2(this.month);\n                case \"d\":\n                    return fmt2(this.day);\n                case \"H\":\n                    return fmt2(this.hour);\n                case \"M\":\n                    return fmt2(this.minute);\n                case \"S\":\n                    return fmt2(this.second);\n            }\n            throw new ValueError(`No known conversion for ${m}`);\n        });\n    }\n\n    /**\n     * @param {PyTimeDelta} timedelta\n     * @returns {PyDateTime}\n     */\n    substract(timedelta) {\n        return this.add(timedelta.negate());\n    }\n\n    /**\n     * @returns {string}\n     */\n    toJSON() {\n        return this.strftime(\"%Y-%m-%d %H:%M:%S\");\n    }\n\n    /**\n     * @returns {PyDateTime}\n     */\n    to_utc() {\n        const d = new Date(this.year, this.month -1, this.day, this.hour, this.minute, this.second);\n        const timedelta = PyTimeDelta.create({ minutes: d.getTimezoneOffset() });\n        return this.add(timedelta);\n    }\n}\n\nexport class PyTime extends PyDate {\n    /**\n     * @param  {...any} args\n     * @returns {PyTime}\n     */\n    static create(...args) {\n        const namedArgs = parseArgs(args, [\"hour\", \"minute\", \"second\"]);\n        const hour = namedArgs.hour || 0;\n        const minute = namedArgs.minute || 0;\n        const second = namedArgs.second || 0;\n        return new PyTime(hour, minute, second);\n    }\n\n    constructor(hour, minute, second) {\n        const now = new Date();\n        const year = now.getFullYear();\n        const month = now.getMonth();\n        const day = now.getDate();\n        super(year, month, day);\n        this.hour = hour;\n        this.minute = minute;\n        this.second = second;\n    }\n\n    /**\n     * @param {string} format\n     * @returns {string}\n     */\n    strftime(format) {\n        return format.replace(/%([A-Za-z])/g, (m, c) => {\n            switch (c) {\n                case \"Y\":\n                    return fmt4(this.year);\n                case \"m\":\n                    return fmt2(this.month + 1);\n                case \"d\":\n                    return fmt2(this.day);\n                case \"H\":\n                    return fmt2(this.hour);\n                case \"M\":\n                    return fmt2(this.minute);\n                case \"S\":\n                    return fmt2(this.second);\n            }\n            throw new ValueError(`No known conversion for ${m}`);\n        });\n    }\n\n    toJSON() {\n        return this.strftime(\"%H:%M:%S\");\n    }\n}\n\n/*\n * This list is intended to be of that shape (32 days in december), it is used by\n * the algorithm that computes \"relativedelta yearday\". The algorithm was adapted\n * from the one in python (https://github.com/dateutil/dateutil/blob/2.7.3/dateutil/relativedelta.py#L199)\n */\nconst DAYS_IN_YEAR = [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366];\n\nconst TIME_PERIODS = [\"hour\", \"minute\", \"second\"];\nconst PERIODS = [\"year\", \"month\", \"day\", ...TIME_PERIODS];\n\nconst RELATIVE_KEYS = \"years months weeks days hours minutes seconds microseconds leapdays\".split(\n    \" \"\n);\nconst ABSOLUTE_KEYS = \"year month day hour minute second microsecond weekday nlyearday yearday\".split(\n    \" \"\n);\n\nconst argsSpec = [\"dt1\", \"dt2\"]; // all other arguments are kwargs\nexport class PyRelativeDelta {\n    /**\n     * @param  {...any} args\n     * @returns {PyRelativeDelta}\n     */\n    static create(...args) {\n        const params = parseArgs(args, argsSpec);\n        if (\"dt1\" in params) {\n            throw new Error(\"relativedelta(dt1, dt2) is not supported for now\");\n        }\n        for (const period of PERIODS) {\n            if (period in params) {\n                const val = params[period];\n                assert(val >= 0, `${period} ${val} is out of range`);\n            }\n        }\n\n        for (const key of RELATIVE_KEYS) {\n            params[key] = params[key] || 0;\n        }\n        for (const key of ABSOLUTE_KEYS) {\n            params[key] = key in params ? params[key] : null;\n        }\n        params.days += 7 * params.weeks;\n\n        let yearDay = 0;\n        if (params.nlyearday) {\n            yearDay = params.nlyearday;\n        } else if (params.yearday) {\n            yearDay = params.yearday;\n            if (yearDay > 59) {\n                params.leapDays = -1;\n            }\n        }\n\n        if (yearDay) {\n            for (let monthIndex = 0; monthIndex < DAYS_IN_YEAR.length; monthIndex++) {\n                if (yearDay <= DAYS_IN_YEAR[monthIndex]) {\n                    params.month = monthIndex + 1;\n                    if (monthIndex === 0) {\n                        params.day = yearDay;\n                    } else {\n                        params.day = yearDay - DAYS_IN_YEAR[monthIndex - 1];\n                    }\n                    break;\n                }\n            }\n        }\n\n        return new PyRelativeDelta(params);\n    }\n\n    /**\n     * @param {PyDateTime|PyDate} date\n     * @param {PyRelativeDelta} delta\n     * @returns {PyDateTime|PyDate}\n     */\n    static add(date, delta) {\n        if (!(date instanceof PyDate || date instanceof PyDateTime)) {\n            throw NotSupportedError();\n        }\n\n        // First pass: we want to determine which is our target year and if we will apply leap days\n        const s = tmxxx(\n            (delta.year || date.year) + delta.years,\n            (delta.month || date.month) + delta.months,\n            delta.day || date.day,\n            delta.hour || date.hour || 0,\n            delta.minute || date.minute || 0,\n            delta.second || date.seconds || 0,\n            delta.microseconds || date.microseconds || 0\n        );\n\n        const newDateTime = new PyDateTime(\n            s.year,\n            s.month,\n            s.day,\n            s.hour,\n            s.minute,\n            s.second,\n            s.microsecond\n        );\n\n        let leapDays = 0;\n        if (delta.leapDays && newDateTime.month > 2 && isLeap(newDateTime.year)) {\n            leapDays = delta.leapDays;\n        }\n\n        // Second pass: apply the difference in days, and the difference in time values\n        const temp = newDateTime.add(\n            PyTimeDelta.create({\n                days: delta.days + leapDays,\n                hours: delta.hours,\n                minutes: delta.minutes,\n                seconds: delta.seconds,\n                microseconds: delta.microseconds,\n            })\n        );\n\n        // Determine the right return type:\n        // First we look at the type of the incoming date object,\n        // then we look at the actual time values held by the computed date.\n        const hasTime = Boolean(temp.hour || temp.minute || temp.second || temp.microsecond);\n        const returnDate =\n            !hasTime && date instanceof PyDate ? new PyDate(temp.year, temp.month, temp.day) : temp;\n\n        // Final pass: target the wanted day of the week (if necessary)\n        if (delta.weekday !== null) {\n            const wantedDow = delta.weekday + 1; // python: Monday is 0 ; JS: Monday is 1;\n            const _date = new Date(returnDate.year, returnDate.month - 1, returnDate.day);\n            const days = (7 - _date.getDay() + wantedDow) % 7;\n            return returnDate.add(new PyTimeDelta(days, 0, 0));\n        }\n        return returnDate;\n    }\n\n    /**\n     * @param {PyDateTime|PyDate} date\n     * @param {PyRelativeDelta} delta\n     * @returns {PyDateTime|PyDate}\n     */\n    static substract(date, delta) {\n        return PyRelativeDelta.add(date, delta.negate());\n    }\n\n    /**\n     * @param {Object} params\n     * @param {+1|-1} sign\n     */\n    constructor(params = {}, sign = +1) {\n        this.years = sign * params.years;\n        this.months = sign * params.months;\n        this.days = sign * params.days;\n        this.hours = sign * params.hours;\n        this.minutes = sign * params.minutes;\n        this.seconds = sign * params.seconds;\n        this.microseconds = sign * params.microseconds;\n\n        this.leapDays = params.leapDays;\n\n        this.year = params.year;\n        this.month = params.month;\n        this.day = params.day;\n        this.hour = params.hour;\n        this.minute = params.minute;\n        this.second = params.second;\n        this.microsecond = params.microsecond;\n\n        this.weekday = params.weekday;\n    }\n\n    /**\n     * @returns {PyRelativeDelta}\n     */\n    negate() {\n        return new PyRelativeDelta(this, -1);\n    }\n\n    isEqual(other) {\n        // For now we don't do normalization in the constructor (or create method).\n        // That is, we only compute the overflows at the time we add or substract.\n        // This is why we can't support isEqual for now.\n        throw new NotSupportedError();\n    }\n}\n\nconst TIME_DELTA_KEYS = \"weeks days hours minutes seconds milliseconds microseconds\".split(\" \");\n\n/**\n * Returns a \"pair\" with the fractional and integer parts of x\n * @param {float}\n * @returns {[float,integer]}\n */\nfunction modf(x) {\n    const mod = x % 1;\n    return [mod < 0 ? mod + 1 : mod, Math.floor(x)];\n}\n\nexport class PyTimeDelta {\n    /**\n     * @param  {...any} args\n     * @returns {PyTimeDelta}\n     */\n    static create(...args) {\n        const namedArgs = parseArgs(args, [\"days\", \"seconds\", \"microseconds\"]);\n        for (const key of TIME_DELTA_KEYS) {\n            namedArgs[key] = namedArgs[key] || 0;\n        }\n\n        // a timedelta can be created using TIME_DELTA_KEYS with float/integer values\n        // but only days, seconds, microseconds are kept internally.\n        // --> some normalization occurs here\n\n        let d = 0;\n        let s = 0;\n        let us = 0; // ~ \u03bcs standard notation for microseconds\n\n        const days = namedArgs.days + namedArgs.weeks * 7;\n        let seconds = namedArgs.seconds + 60 * namedArgs.minutes + 3600 * namedArgs.hours;\n        let microseconds = namedArgs.microseconds + 1000 * namedArgs.milliseconds;\n\n        const [dFrac, dInt] = modf(days);\n        d = dInt;\n        let daysecondsfrac = 0;\n        if (dFrac) {\n            const [dsFrac, dsInt] = modf(dFrac * 24 * 3600);\n            s = dsInt;\n            daysecondsfrac = dsFrac;\n        }\n\n        const [sFrac, sInt] = modf(seconds);\n        seconds = sInt;\n        const secondsfrac = sFrac + daysecondsfrac;\n\n        divmod(seconds, 24 * 3600, (days, seconds) => {\n            d += days;\n            s += seconds;\n        });\n\n        microseconds += secondsfrac * 1e6;\n        divmod(microseconds, 1000000, (seconds, microseconds) => {\n            divmod(seconds, 24 * 3600, (days, seconds) => {\n                d += days;\n                s += seconds;\n                us += Math.round(microseconds);\n            });\n        });\n\n        return new PyTimeDelta(d, s, us);\n    }\n\n    /**\n     * @param {integer} days\n     * @param {integer} seconds\n     * @param {integer} microseconds\n     */\n    constructor(days, seconds, microseconds) {\n        this.days = days;\n        this.seconds = seconds;\n        this.microseconds = microseconds;\n    }\n\n    /**\n     * @param {PyTimeDelta} other\n     * @returns {PyTimeDelta}\n     */\n    add(other) {\n        return PyTimeDelta.create({\n            days: this.days + other.days,\n            seconds: this.seconds + other.seconds,\n            microseconds: this.microseconds + other.microseconds,\n        });\n    }\n\n    /**\n     * @param {integer} n\n     * @returns {PyTimeDelta}\n     */\n    divide(n) {\n        const us = (this.days * 24 * 3600 + this.seconds) * 1e6 + this.microseconds;\n        return PyTimeDelta.create({ microseconds: Math.floor(us / n) });\n    }\n\n    /**\n     * @param {any} other\n     * @returns {boolean}\n     */\n    isEqual(other) {\n        if (!(other instanceof PyTimeDelta)) {\n            return false;\n        }\n        return (\n            this.days === other.days &&\n            this.seconds === other.seconds &&\n            this.microseconds === other.microseconds\n        );\n    }\n\n    /**\n     * @returns {boolean}\n     */\n    isTrue() {\n        return this.days !== 0 || this.seconds !== 0 || this.microseconds !== 0;\n    }\n\n    /**\n     * @param {float} n\n     * @returns {PyTimeDelta}\n     */\n    multiply(n) {\n        return PyTimeDelta.create({\n            days: n * this.days,\n            seconds: n * this.seconds,\n            microseconds: n * this.microseconds,\n        });\n    }\n\n    /**\n     * @returns {PyTimeDelta}\n     */\n    negate() {\n        return PyTimeDelta.create({\n            days: -this.days,\n            seconds: -this.seconds,\n            microseconds: -this.microseconds,\n        });\n    }\n\n    /**\n     * @param {PyTimeDelta} other\n     * @returns {PyTimeDelta}\n     */\n    substract(other) {\n        return PyTimeDelta.create({\n            days: this.days - other.days,\n            seconds: this.seconds - other.seconds,\n            microseconds: this.microseconds - other.microseconds,\n        });\n    }\n\n    /**\n     * @returns {float}\n     */\n    total_seconds() {\n        return this.days * 86400 + this.seconds + this.microseconds / 1000000;\n    }\n}\n", "import { BUILTINS, EvaluationError, execOnIterable } from \"./py_builtin\";\nimport {\n    NotSupportedError,\n    PyDate,\n    PyDateTime,\n    PyRelativeDelta,\n    PyTime,\n    PyTimeDelta,\n} from \"./py_date\";\nimport { PY_DICT, toPyDict } from \"./py_utils\";\nimport { parseArgs } from \"./py_parser\";\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @typedef { import(\"./py_parser\").AST } AST\n */\n\n// -----------------------------------------------------------------------------\n// Constants and helpers\n// -----------------------------------------------------------------------------\n\nconst isTrue = BUILTINS.bool;\n\n/**\n * @param {AST} ast\n * @param {Object} context\n * @returns {any}\n */\nfunction applyUnaryOp(ast, context) {\n    const value = evaluate(ast.right, context);\n    switch (ast.op) {\n        case \"-\":\n            if (value instanceof Object && value.negate) {\n                return value.negate();\n            }\n            return -value;\n        case \"+\":\n            return value;\n        case \"not\":\n            return !isTrue(value);\n    }\n    throw new EvaluationError(`Unknown unary operator: ${ast.op}`);\n}\n\n/**\n * We want to maintain this order:\n *   None < number (boolean) < dict < string < list < dict\n * So, each type is mapped to a number to represent that order\n *\n * @param {any} val\n * @returns {number} index type\n */\nfunction pytypeIndex(val) {\n    switch (typeof val) {\n        case \"object\":\n            // None, List, Object, Dict\n            return val === null ? 1 : Array.isArray(val) ? 5 : 3;\n        case \"number\":\n            return 2;\n        case \"string\":\n            return 4;\n    }\n    throw new EvaluationError(`Unknown type: ${typeof val}`);\n}\n\n/**\n * @param {Object} obj\n * @returns {boolean}\n */\nfunction isConstructor(obj) {\n    return !!obj.prototype && !!obj.prototype.constructor.name;\n}\n\n/**\n * Compare two values\n *\n * @param {any} left\n * @param {any} right\n * @returns {boolean}\n */\nfunction isLess(left, right) {\n    if (typeof left === \"number\" && typeof right === \"number\") {\n        return left < right;\n    }\n    if (typeof left === \"boolean\") {\n        left = left ? 1 : 0;\n    }\n    if (typeof right === \"boolean\") {\n        right = right ? 1 : 0;\n    }\n    const leftIndex = pytypeIndex(left);\n    const rightIndex = pytypeIndex(right);\n    if (leftIndex === rightIndex) {\n        return left < right;\n    }\n    return leftIndex < rightIndex;\n}\n\n/**\n * @param {any} left\n * @param {any} right\n * @returns {boolean}\n */\nfunction isEqual(left, right) {\n    if (typeof left !== typeof right) {\n        if (typeof left === \"boolean\" && typeof right === \"number\") {\n            return right === (left ? 1 : 0);\n        }\n        if (typeof left === \"number\" && typeof right === \"boolean\") {\n            return left === (right ? 1 : 0);\n        }\n        return false;\n    }\n    if (left instanceof Object && left.isEqual) {\n        return left.isEqual(right);\n    }\n    return left === right;\n}\n\n/**\n * @param {any} left\n * @param {any} right\n * @returns {boolean}\n */\nfunction isIn(left, right) {\n    if (Array.isArray(right)) {\n        return right.includes(left);\n    }\n    if (typeof right === \"string\" && typeof left === \"string\") {\n        return right.includes(left);\n    }\n    if (typeof right === \"object\") {\n        return left in right;\n    }\n    return false;\n}\n\n/**\n * @param {AST} ast\n * @param {object} context\n * @returns {any}\n */\nfunction applyBinaryOp(ast, context) {\n    const left = evaluate(ast.left, context);\n    const right = evaluate(ast.right, context);\n    switch (ast.op) {\n        case \"+\": {\n            const relativeDeltaOnLeft = left instanceof PyRelativeDelta;\n            const relativeDeltaOnRight = right instanceof PyRelativeDelta;\n            if (relativeDeltaOnLeft || relativeDeltaOnRight) {\n                const date = relativeDeltaOnLeft ? right : left;\n                const delta = relativeDeltaOnLeft ? left : right;\n                return PyRelativeDelta.add(date, delta);\n            }\n\n            const timeDeltaOnLeft = left instanceof PyTimeDelta;\n            const timeDeltaOnRight = right instanceof PyTimeDelta;\n            if (timeDeltaOnLeft && timeDeltaOnRight) {\n                return left.add(right);\n            }\n            if (timeDeltaOnLeft) {\n                if (right instanceof PyDate || right instanceof PyDateTime) {\n                    return right.add(left);\n                } else {\n                    throw NotSupportedError();\n                }\n            }\n            if (timeDeltaOnRight) {\n                if (left instanceof PyDate || left instanceof PyDateTime) {\n                    return left.add(right);\n                } else {\n                    throw NotSupportedError();\n                }\n            }\n            if (left instanceof Array && right instanceof Array) {\n                return [...left, ...right];\n            }\n\n            return left + right;\n        }\n        case \"-\": {\n            const isRightDelta = right instanceof PyRelativeDelta;\n            if (isRightDelta) {\n                return PyRelativeDelta.substract(left, right);\n            }\n\n            const timeDeltaOnRight = right instanceof PyTimeDelta;\n            if (timeDeltaOnRight) {\n                if (left instanceof PyTimeDelta) {\n                    return left.substract(right);\n                } else if (left instanceof PyDate || left instanceof PyDateTime) {\n                    return left.substract(right);\n                } else {\n                    throw NotSupportedError();\n                }\n            }\n\n            if (left instanceof PyDate) {\n                return left.substract(right);\n            }\n            return left - right;\n        }\n        case \"*\": {\n            const timeDeltaOnLeft = left instanceof PyTimeDelta;\n            const timeDeltaOnRight = right instanceof PyTimeDelta;\n            if (timeDeltaOnLeft || timeDeltaOnRight) {\n                const number = timeDeltaOnLeft ? right : left;\n                const delta = timeDeltaOnLeft ? left : right;\n                return delta.multiply(number); // check number type?\n            }\n\n            return left * right;\n        }\n        case \"/\":\n            return left / right;\n        case \"%\":\n            return left % right;\n        case \"//\":\n            if (left instanceof PyTimeDelta) {\n                return left.divide(right); // check number type?\n            }\n            return Math.floor(left / right);\n        case \"**\":\n            return left ** right;\n        case \"==\":\n            return isEqual(left, right);\n        case \"<>\":\n        case \"!=\":\n            return !isEqual(left, right);\n        case \"<\":\n            return isLess(left, right);\n        case \">\":\n            return isLess(right, left);\n        case \">=\":\n            return isEqual(left, right) || isLess(right, left);\n        case \"<=\":\n            return isEqual(left, right) || isLess(left, right);\n        case \"in\":\n            return isIn(left, right);\n        case \"not in\":\n            return !isIn(left, right);\n    }\n    throw new EvaluationError(`Unknown binary operator: ${ast.op}`);\n}\n\nconst DICT = {\n    get(...args) {\n        const { key, defValue } = parseArgs(args, [\"key\", \"defValue\"]);\n        if (key in this) {\n            return this[key];\n        } else if (defValue) {\n            return defValue;\n        }\n        return null;\n    },\n};\n\nconst STRING = {\n    lower() {\n        return this.toLowerCase();\n    },\n    upper() {\n        return this.toUpperCase();\n    },\n};\n\nfunction applyFunc(key, func, set, ...args) {\n    // we always receive at least one argument: kwargs (return fnValue(...args, kwargs); in FunctionCall case)\n    if (args.length === 1) {\n        return new Set(set);\n    }\n    if (args.length > 2) {\n        throw new EvaluationError(\n            `${key}: py_js supports at most 1 argument, got (${args.length - 1})`\n        );\n    }\n    return execOnIterable(args[0], func);\n}\n\nconst SET = {\n    intersection(...args) {\n        return applyFunc(\n            \"intersection\",\n            (iterable) => {\n                const intersection = new Set();\n                for (const i of iterable) {\n                    if (this.has(i)) {\n                        intersection.add(i);\n                    }\n                }\n                return intersection;\n            },\n            this,\n            ...args\n        );\n    },\n    difference(...args) {\n        return applyFunc(\n            \"difference\",\n            (iterable) => {\n                iterable = new Set(iterable);\n                const difference = new Set();\n                for (const e of this) {\n                    if (!iterable.has(e)) {\n                        difference.add(e);\n                    }\n                }\n                return difference;\n            },\n            this,\n            ...args\n        );\n    },\n    union(...args) {\n        return applyFunc(\n            \"union\",\n            (iterable) => {\n                return new Set([...this, ...iterable]);\n            },\n            this,\n            ...args\n        );\n    },\n};\n\n// -----------------------------------------------------------------------------\n// Evaluate function\n// -----------------------------------------------------------------------------\n\n/**\n * @param {Function} _class the class whose methods we want\n * @returns {Function[]} an array containing the methods defined on the class,\n *  including the constructor\n */\nfunction methods(_class) {\n    return Object.getOwnPropertyNames(_class.prototype).map((prop) => _class.prototype[prop]);\n}\n\nconst allowedFns = new Set([\n    BUILTINS.time.strftime,\n    BUILTINS.set,\n    BUILTINS.bool,\n    BUILTINS.min,\n    BUILTINS.max,\n    BUILTINS.context_today,\n    BUILTINS.datetime.datetime.now,\n    BUILTINS.datetime.datetime.combine,\n    BUILTINS.datetime.date.today,\n    ...methods(BUILTINS.relativedelta),\n    ...Object.values(BUILTINS.datetime).flatMap((obj) => methods(obj)),\n    ...Object.values(SET),\n    ...Object.values(DICT),\n    ...Object.values(STRING),\n]);\n\nconst unboundFn = Symbol(\"unbound function\");\n\n/**\n * @param {AST} ast\n * @param {Object} context\n * @returns {any}\n */\nexport function evaluate(ast, context = {}) {\n    const dicts = new Set();\n    let pyContext;\n    const evalContext = Object.create(context);\n    if (!evalContext.context) {\n        Object.defineProperty(evalContext, \"context\", {\n            get() {\n                if (!pyContext) {\n                    pyContext = toPyDict(context);\n                }\n                return pyContext;\n            },\n        });\n    }\n\n    function _innerEvaluate(ast) {\n        switch (ast.type) {\n            case 0 /* Number */:\n            case 1 /* String */:\n                return ast.value;\n            case 5 /* Name */:\n                if (ast.value in evalContext) {\n                    return evalContext[ast.value];\n                } else if (ast.value in BUILTINS) {\n                    return BUILTINS[ast.value];\n                } else {\n                    throw new EvaluationError(`Name '${ast.value}' is not defined`);\n                }\n            case 3 /* None */:\n                return null;\n            case 2 /* Boolean */:\n                return ast.value;\n            case 6 /* UnaryOperator */:\n                return applyUnaryOp(ast, evalContext);\n            case 7 /* BinaryOperator */:\n                return applyBinaryOp(ast, evalContext);\n            case 14 /* BooleanOperator */: {\n                const left = _evaluate(ast.left);\n                if (ast.op === \"and\") {\n                    return isTrue(left) ? _evaluate(ast.right) : left;\n                } else {\n                    return isTrue(left) ? left : _evaluate(ast.right);\n                }\n            }\n            case 4 /* List */:\n            case 10 /* Tuple */:\n                return ast.value.map(_evaluate);\n            case 11 /* Dictionary */: {\n                const dict = {};\n                for (const key in ast.value) {\n                    dict[key] = _evaluate(ast.value[key]);\n                }\n                dicts.add(dict);\n                return dict;\n            }\n            case 8 /* FunctionCall */: {\n                const fnValue = _evaluate(ast.fn);\n                const args = ast.args.map(_evaluate);\n                const kwargs = {};\n                for (const kwarg in ast.kwargs) {\n                    kwargs[kwarg] = _evaluate(ast.kwargs[kwarg]);\n                }\n                if (\n                    fnValue === PyDate ||\n                    fnValue === PyDateTime ||\n                    fnValue === PyTime ||\n                    fnValue === PyRelativeDelta ||\n                    fnValue === PyTimeDelta\n                ) {\n                    return fnValue.create(...args, kwargs);\n                }\n                return fnValue(...args, kwargs);\n            }\n            case 12 /* Lookup */: {\n                const dict = _evaluate(ast.target);\n                const key = _evaluate(ast.key);\n                return dict[key];\n            }\n            case 13 /* If */: {\n                if (isTrue(_evaluate(ast.condition))) {\n                    return _evaluate(ast.ifTrue);\n                } else {\n                    return _evaluate(ast.ifFalse);\n                }\n            }\n            case 15 /* ObjLookup */: {\n                let left = _evaluate(ast.obj);\n                let result;\n                if (dicts.has(left) || Object.isPrototypeOf.call(PY_DICT, left)) {\n                    // this is a dictionary => need to apply dict methods\n                    result = DICT[ast.key];\n                } else if (typeof left === \"string\") {\n                    result = STRING[ast.key];\n                } else if (left instanceof Set) {\n                    result = SET[ast.key];\n                } else if (ast.key == \"get\" && typeof left === \"object\") {\n                    result = DICT[ast.key];\n                    left = toPyDict(left);\n                } else {\n                    result = left[ast.key];\n                }\n                if (typeof result === \"function\") {\n                    if (!isConstructor(result)) {\n                        const bound = result.bind(left);\n                        bound[unboundFn] = result;\n                        return bound;\n                    }\n                }\n                return result;\n            }\n        }\n        throw new EvaluationError(`AST of type ${ast.type} cannot be evaluated`);\n    }\n\n    /**\n     * @param {AST} ast\n     */\n    function _evaluate(ast) {\n        const val = _innerEvaluate(ast);\n        if (typeof val === \"function\" && !allowedFns.has(val) && !allowedFns.has(val[unboundFn])) {\n            throw new Error(\"Invalid Function Call\");\n        }\n        return val;\n    }\n    return _evaluate(ast);\n}\n", "import { binaryOperators, comparators } from \"./py_tokenizer\";\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @typedef { import(\"./py_tokenizer\").Token } Token\n */\n\n/**\n * @typedef {{type: 0, value: number}} ASTNumber\n * @typedef {{type: 1, value: string}} ASTString\n * @typedef {{type: 2, value: boolean}} ASTBoolean\n * @typedef {{type: 3}} ASTNone\n * @typedef {{type: 4, value: AST[]}} ASTList\n * @typedef {{type: 5, value: string}} ASTName\n * @typedef {{type: 6, op: string, right: AST}} ASTUnaryOperator\n * @typedef {{type: 7, op: string, left: AST, right: AST}} ASTBinaryOperator\n * @typedef {{type: 8, fn: AST, args: AST[], kwargs: {[key: string]: AST}}} ASTFunctionCall\n * @typedef {{type: 9, name: ASTName, value: AST}} ASTAssignment\n * @typedef {{type: 10, value: AST[]}} ASTTuple\n * @typedef {{type: 11, value: { [key: string]: AST}}} ASTDictionary\n * @typedef {{type: 12, target: AST, key: AST}} ASTLookup\n * @typedef {{type: 13, condition: AST, ifTrue: AST, ifFalse: AST}} ASTIf\n * @typedef {{type: 14, op: string, left: AST, right: AST}} ASTBooleanOperator\n * @typedef {{type: 15, obj: AST, key: string}} ASTObjLookup\n *\n * @typedef { ASTNumber | ASTString | ASTBoolean | ASTNone | ASTList | ASTName | ASTUnaryOperator | ASTBinaryOperator | ASTFunctionCall | ASTAssignment | ASTTuple | ASTDictionary |ASTLookup | ASTIf | ASTBooleanOperator | ASTObjLookup} AST\n */\n\nexport class ParserError extends Error {}\n\n// -----------------------------------------------------------------------------\n// Constants and helpers\n// -----------------------------------------------------------------------------\n\nconst chainedOperators = new Set(comparators);\nconst infixOperators = new Set(binaryOperators.concat(comparators));\n\n/**\n * Compute the \"binding power\" of a symbol\n *\n * @param {string} symbol\n * @returns {number}\n */\nexport function bp(symbol) {\n    switch (symbol) {\n        case \"=\":\n            return 10;\n        case \"if\":\n            return 20;\n        case \"in\":\n        case \"not in\":\n        case \"is\":\n        case \"is not\":\n        case \"<\":\n        case \"<=\":\n        case \">\":\n        case \">=\":\n        case \"<>\":\n        case \"==\":\n        case \"!=\":\n            return 60;\n        case \"or\":\n            return 30;\n        case \"and\":\n            return 40;\n        case \"not\":\n            return 50;\n        case \"|\":\n            return 70;\n        case \"^\":\n            return 80;\n        case \"&\":\n            return 90;\n        case \"<<\":\n        case \">>\":\n            return 100;\n        case \"+\":\n        case \"-\":\n            return 110;\n        case \"*\":\n        case \"/\":\n        case \"//\":\n        case \"%\":\n            return 120;\n        case \"**\":\n            return 140;\n        case \".\":\n        case \"(\":\n        case \"[\":\n            return 150;\n    }\n    return 0;\n}\n\n/**\n * Compute binding power of a symbol\n *\n * @param {Token} token\n * @returns {number}\n */\nfunction bindingPower(token) {\n    return token.type === 2 /* Symbol */ ? bp(token.value) : 0;\n}\n\n/**\n * Check if a token is a symbol of a given value\n *\n * @param {Token} token\n * @param {string} value\n * @returns {boolean}\n */\nfunction isSymbol(token, value) {\n    return token.type === 2 /* Symbol */ && token.value === value;\n}\n\n/**\n * @param {Token} current\n * @param {Token[]} tokens\n * @returns {AST}\n */\nfunction parsePrefix(current, tokens) {\n    switch (current.type) {\n        case 0 /* Number */:\n            return { type: 0 /* Number */, value: current.value };\n        case 1 /* String */:\n            return { type: 1 /* String */, value: current.value };\n        case 4 /* Constant */:\n            if (current.value === \"None\") {\n                return { type: 3 /* None */ };\n            } else {\n                return { type: 2 /* Boolean */, value: current.value === \"True\" };\n            }\n        case 3 /* Name */:\n            return { type: 5 /* Name */, value: current.value };\n        case 2 /* Symbol */:\n            switch (current.value) {\n                case \"-\":\n                case \"+\":\n                case \"~\":\n                    return {\n                        type: 6 /* UnaryOperator */,\n                        op: current.value,\n                        right: _parse(tokens, 130),\n                    };\n                case \"not\":\n                    return {\n                        type: 6 /* UnaryOperator */,\n                        op: current.value,\n                        right: _parse(tokens, 50),\n                    };\n                case \"(\": {\n                    const content = [];\n                    let isTuple = false;\n                    while (tokens[0] && !isSymbol(tokens[0], \")\")) {\n                        content.push(_parse(tokens, 0));\n                        if (tokens[0]) {\n                            if (tokens[0] && isSymbol(tokens[0], \",\")) {\n                                isTuple = true;\n                                tokens.shift();\n                            } else if (!isSymbol(tokens[0], \")\")) {\n                                throw new ParserError(\"parsing error\");\n                            }\n                        } else {\n                            throw new ParserError(\"parsing error\");\n                        }\n                    }\n                    if (!tokens[0] || !isSymbol(tokens[0], \")\")) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    tokens.shift();\n                    isTuple = isTuple || content.length === 0;\n                    return isTuple ? { type: 10 /* Tuple */, value: content } : content[0];\n                }\n                case \"[\": {\n                    const value = [];\n                    while (tokens[0] && !isSymbol(tokens[0], \"]\")) {\n                        value.push(_parse(tokens, 0));\n                        if (tokens[0]) {\n                            if (isSymbol(tokens[0], \",\")) {\n                                tokens.shift();\n                            } else if (!isSymbol(tokens[0], \"]\")) {\n                                throw new ParserError(\"parsing error\");\n                            }\n                        }\n                    }\n                    if (!tokens[0] || !isSymbol(tokens[0], \"]\")) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    tokens.shift();\n                    return { type: 4 /* List */, value };\n                }\n                case \"{\": {\n                    const dict = {};\n                    while (tokens[0] && !isSymbol(tokens[0], \"}\")) {\n                        const key = _parse(tokens, 0);\n                        if (\n                            (key.type !== 1 /* String */ && key.type !== 0) /* Number */ ||\n                            !tokens[0] ||\n                            !isSymbol(tokens[0], \":\")\n                        ) {\n                            throw new ParserError(\"parsing error\");\n                        }\n                        tokens.shift();\n                        const value = _parse(tokens, 0);\n                        dict[key.value] = value;\n                        if (isSymbol(tokens[0], \",\")) {\n                            tokens.shift();\n                        }\n                    }\n                    // remove the } token\n                    if (!tokens.shift()) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    return { type: 11 /* Dictionary */, value: dict };\n                }\n            }\n    }\n    throw new ParserError(\"Token cannot be parsed\");\n}\n\n/**\n * @param {AST} ast\n * @param {Token} current\n * @param {Token[]} tokens\n * @returns {AST}\n */\nfunction parseInfix(left, current, tokens) {\n    switch (current.type) {\n        case 2 /* Symbol */:\n            if (infixOperators.has(current.value)) {\n                let right = _parse(tokens, bindingPower(current));\n                if (current.value === \"and\" || current.value === \"or\") {\n                    return {\n                        type: 14 /* BooleanOperator */,\n                        op: current.value,\n                        left,\n                        right,\n                    };\n                } else if (current.value === \".\") {\n                    if (right.type === 5 /* Name */) {\n                        return {\n                            type: 15 /* ObjLookup */,\n                            obj: left,\n                            key: right.value,\n                        };\n                    } else {\n                        throw new ParserError(\"invalid obj lookup\");\n                    }\n                }\n                let op = {\n                    type: 7 /* BinaryOperator */,\n                    op: current.value,\n                    left,\n                    right,\n                };\n                while (\n                    chainedOperators.has(current.value) &&\n                    tokens[0] &&\n                    tokens[0].type === 2 /* Symbol */ &&\n                    chainedOperators.has(tokens[0].value)\n                ) {\n                    const nextToken = tokens.shift();\n                    op = {\n                        type: 14 /* BooleanOperator */,\n                        op: \"and\",\n                        left: op,\n                        right: {\n                            type: 7 /* BinaryOperator */,\n                            op: nextToken.value,\n                            left: right,\n                            right: _parse(tokens, bindingPower(nextToken)),\n                        },\n                    };\n                    right = op.right.right;\n                }\n                return op;\n            }\n            switch (current.value) {\n                case \"(\": {\n                    // function call\n                    const args = [];\n                    const kwargs = {};\n                    while (tokens[0] && !isSymbol(tokens[0], \")\")) {\n                        const arg = _parse(tokens, 0);\n                        if (arg.type === 9 /* Assignment */) {\n                            kwargs[arg.name.value] = arg.value;\n                        } else {\n                            args.push(arg);\n                        }\n                        if (tokens[0] && isSymbol(tokens[0], \",\")) {\n                            tokens.shift();\n                        }\n                    }\n                    if (!tokens[0] || !isSymbol(tokens[0], \")\")) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    tokens.shift();\n                    return { type: 8 /* FunctionCall */, fn: left, args, kwargs };\n                }\n                case \"=\":\n                    if (left.type === 5 /* Name */) {\n                        return {\n                            type: 9 /* Assignment */,\n                            name: left,\n                            value: _parse(tokens, 10),\n                        };\n                    }\n                    break;\n                case \"[\": {\n                    // lookup in dictionary\n                    const key = _parse(tokens);\n                    if (!tokens[0] || !isSymbol(tokens[0], \"]\")) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    tokens.shift();\n                    return {\n                        type: 12 /* Lookup */,\n                        target: left,\n                        key: key,\n                    };\n                }\n                case \"if\": {\n                    const condition = _parse(tokens);\n                    if (!tokens[0] || !isSymbol(tokens[0], \"else\")) {\n                        throw new ParserError(\"parsing error\");\n                    }\n                    tokens.shift();\n                    const ifFalse = _parse(tokens);\n                    return {\n                        type: 13 /* If */,\n                        condition,\n                        ifTrue: left,\n                        ifFalse,\n                    };\n                }\n            }\n    }\n    throw new ParserError(\"Token cannot be parsed\");\n}\n\n/**\n * @param {Token[]} tokens\n * @param {number} [bp]\n * @returns {AST}\n */\nfunction _parse(tokens, bp = 0) {\n    const token = tokens.shift();\n    let expr = parsePrefix(token, tokens);\n    while (tokens[0] && bindingPower(tokens[0]) > bp) {\n        expr = parseInfix(expr, tokens.shift(), tokens);\n    }\n    return expr;\n}\n\n// -----------------------------------------------------------------------------\n// Parse function\n// -----------------------------------------------------------------------------\n\n/**\n * Parse a list of tokens\n *\n * @param {Token[]} tokens\n * @returns {AST}\n */\nexport function parse(tokens) {\n    if (tokens.length) {\n        const ast = _parse(tokens, 0);\n        if (tokens.length) {\n            throw new ParserError(\"Token(s) unused\");\n        }\n        return ast;\n    }\n    throw new ParserError(\"Missing token\");\n}\n\n/**\n * @param {any[]} args\n * @param {string[]} spec\n * @returns {{[name: string]: any}}\n */\nexport function parseArgs(args, spec) {\n    const last = args[args.length - 1];\n    const unnamedArgs = typeof last === \"object\" ? args.slice(0, -1) : args;\n    const kwargs = typeof last === \"object\" ? last : {};\n    for (const [index, val] of unnamedArgs.entries()) {\n        kwargs[spec[index]] = val;\n    }\n    return kwargs;\n}\n", "// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @typedef {{type: 0, value: number}} TokenNumber\n *\n * @typedef {{type: 1, value: string}} TokenString\n *\n * @typedef {{type: 2, value: string}} TokenSymbol\n *\n * @typedef {{type: 3, value: string}} TokenName\n *\n * @typedef {{type: 4, value: string}} TokenConstant\n *\n * @typedef {TokenNumber | TokenString | TokenSymbol | TokenName | TokenConstant} Token\n */\n\nexport class TokenizerError extends Error {}\n\n// -----------------------------------------------------------------------------\n// Helpers and Constants\n// -----------------------------------------------------------------------------\n\n/**\n * Directly maps a single escape code to an output character\n */\nconst directMap = {\n    \"\\\\\": \"\\\\\",\n    '\"': '\"',\n    \"'\": \"'\",\n    a: \"\\x07\",\n    b: \"\\x08\",\n    f: \"\\x0c\",\n    n: \"\\n\",\n    r: \"\\r\",\n    t: \"\\t\",\n    v: \"\\v\",\n};\n\n/**\n * Implements the decoding of Python string literals (embedded in\n * JS strings) into actual JS strings. This includes the decoding\n * of escapes into their corresponding JS\n * characters/codepoints/whatever.\n *\n * The ``unicode`` flags notes whether the literal should be\n * decoded as a bytestring literal or a unicode literal, which\n * pretty much only impacts decoding (or not) of unicode escapes\n * at this point since bytestrings are not technically handled\n * (everything is decoded to JS \"unicode\" strings)\n *\n * Eventurally, ``str`` could eventually use typed arrays, that'd\n * be interesting...\n *\n * @param {string} str\n * @param {boolean} unicode\n * @returns {string}\n */\nfunction decodeStringLiteral(str, unicode) {\n    const out = [];\n    let code;\n    for (var i = 0; i < str.length; ++i) {\n        if (str[i] !== \"\\\\\") {\n            out.push(str[i]);\n            continue;\n        }\n        var escape = str[i + 1];\n        if (escape in directMap) {\n            out.push(directMap[escape]);\n            ++i;\n            continue;\n        }\n        switch (escape) {\n            // Ignored\n            case \"\\n\":\n                ++i;\n                continue;\n            // Character named name in the Unicode database (Unicode only)\n            case \"N\":\n                if (!unicode) {\n                    break;\n                }\n                throw new TokenizerError(\"SyntaxError: \\\\N{} escape not implemented\");\n            case \"u\":\n                if (!unicode) {\n                    break;\n                }\n                var uni = str.slice(i + 2, i + 6);\n                if (!/[0-9a-f]{4}/i.test(uni)) {\n                    throw new TokenizerError(\n                        [\n                            \"SyntaxError: (unicode error) 'unicodeescape' codec\",\n                            \" can't decode bytes in position \",\n                            i,\n                            \"-\",\n                            i + 4,\n                            \": truncated \\\\uXXXX escape\",\n                        ].join(\"\")\n                    );\n                }\n                code = parseInt(uni, 16);\n                out.push(String.fromCharCode(code));\n                // escape + 4 hex digits\n                i += 5;\n                continue;\n            case \"U\":\n                if (!unicode) {\n                    break;\n                }\n                // TODO: String.fromCodePoint\n                throw new TokenizerError(\"SyntaxError: \\\\U escape not implemented\");\n            case \"x\":\n                // get 2 hex digits\n                var hex = str.slice(i + 2, i + 4);\n                if (!/[0-9a-f]{2}/i.test(hex)) {\n                    if (!unicode) {\n                        throw new TokenizerError(\"ValueError: invalid \\\\x escape\");\n                    }\n                    throw new TokenizerError(\n                        [\n                            \"SyntaxError: (unicode error) 'unicodeescape'\",\n                            \" codec can't decode bytes in position \",\n                            i,\n                            \"-\",\n                            i + 2,\n                            \": truncated \\\\xXX escape\",\n                        ].join(\"\")\n                    );\n                }\n                code = parseInt(hex, 16);\n                out.push(String.fromCharCode(code));\n                // skip escape + 2 hex digits\n                i += 3;\n                continue;\n            default:\n                // Check if octal\n                if (!/[0-8]/.test(escape)) {\n                    break;\n                }\n                var r = /[0-8]{1,3}/g;\n                r.lastIndex = i + 1;\n                var m = r.exec(str);\n                var oct = m[0];\n                code = parseInt(oct, 8);\n                out.push(String.fromCharCode(code));\n                // skip matchlength\n                i += oct.length;\n                continue;\n        }\n        out.push(\"\\\\\");\n    }\n    return out.join(\"\");\n}\n\nconst constants = new Set([\"None\", \"False\", \"True\"]);\n\nexport const comparators = [\n    \"in\",\n    \"not\",\n    \"not in\",\n    \"is\",\n    \"is not\",\n    \"<\",\n    \"<=\",\n    \">\",\n    \">=\",\n    \"<>\",\n    \"!=\",\n    \"==\",\n];\n\nexport const binaryOperators = [\n    \"or\",\n    \"and\",\n    \"|\",\n    \"^\",\n    \"&\",\n    \"<<\",\n    \">>\",\n    \"+\",\n    \"-\",\n    \"*\",\n    \"/\",\n    \"//\",\n    \"%\",\n    \"~\",\n    \"**\",\n    \".\",\n];\n\nexport const unaryOperators = [\"-\"];\n\nconst symbols = new Set([\n    ...[\"(\", \")\", \"[\", \"]\", \"{\", \"}\", \":\", \",\"],\n    ...[\"if\", \"else\", \"lambda\", \"=\"],\n    ...comparators,\n    ...binaryOperators,\n    ...unaryOperators,\n]);\n\n// Regexps\nfunction group(...args) {\n    return \"(\" + args.join(\"|\") + \")\";\n}\n\nconst Name = \"[a-zA-Z_]\\\\w*\";\nconst Whitespace = \"[ \\\\f\\\\t]*\";\nconst DecNumber = \"\\\\d+(L|l)?\";\nconst IntNumber = DecNumber;\n\nconst Exponent = \"[eE][+-]?\\\\d+\";\nconst PointFloat = group(`\\\\d+\\\\.\\\\d*(${Exponent})?`, `\\\\.\\\\d+(${Exponent})?`);\n// Exponent not optional when no decimal point\nconst FloatNumber = group(PointFloat, `\\\\d+${Exponent}`);\n\nconst Number = group(FloatNumber, IntNumber);\nconst Operator = group(\"\\\\*\\\\*=?\", \">>=?\", \"<<=?\", \"<>\", \"!=\", \"//=?\", \"[+\\\\-*/%&|^=<>]=?\", \"~\");\nconst Bracket = \"[\\\\[\\\\]\\\\(\\\\)\\\\{\\\\}]\";\nconst Special = \"[:;.,`@]\";\nconst Funny = group(Operator, Bracket, Special);\nconst ContStr = group(\n    \"([uU])?'([^\\n'\\\\\\\\]*(?:\\\\\\\\.[^\\n'\\\\\\\\]*)*)'\",\n    '([uU])?\"([^\\n\"\\\\\\\\]*(?:\\\\\\\\.[^\\n\"\\\\\\\\]*)*)\"'\n);\nconst PseudoToken = Whitespace + group(Number, Funny, ContStr, Name);\nconst NumberPattern = new RegExp(\"^\" + Number + \"$\");\nconst StringPattern = new RegExp(\"^\" + ContStr + \"$\");\nconst NamePattern = new RegExp(\"^\" + Name + \"$\");\nconst strip = new RegExp(\"^\" + Whitespace);\n\n// -----------------------------------------------------------------------------\n// Tokenize function\n// -----------------------------------------------------------------------------\n\n/**\n * Transform a string into a list of tokens\n *\n * @param {string} str\n * @returns {Token[]}\n */\nexport function tokenize(str) {\n    const tokens = [];\n    const max = str.length;\n    let start = 0;\n    let end = 0;\n    // /g flag makes repeated exec() have memory\n    const pseudoprog = new RegExp(PseudoToken, \"g\");\n    while (pseudoprog.lastIndex < max) {\n        const pseudomatch = pseudoprog.exec(str);\n        if (!pseudomatch) {\n            // if match failed on trailing whitespace, end tokenizing\n            if (/^\\s+$/.test(str.slice(end))) {\n                break;\n            }\n            throw new TokenizerError(\n                \"Failed to tokenize <<\" +\n                    str +\n                    \">> at index \" +\n                    (end || 0) +\n                    \"; parsed so far: \" +\n                    tokens\n            );\n        }\n        if (pseudomatch.index > end) {\n            if (str.slice(end, pseudomatch.index).trim()) {\n                throw new TokenizerError(\"Invalid expression\");\n            }\n        }\n        start = pseudomatch.index;\n        end = pseudoprog.lastIndex;\n        let token = str.slice(start, end).replace(strip, \"\");\n        if (NumberPattern.test(token)) {\n            tokens.push({\n                type: 0 /* Number */,\n                value: parseFloat(token),\n            });\n        } else if (StringPattern.test(token)) {\n            var m = StringPattern.exec(token);\n            tokens.push({\n                type: 1 /* String */,\n                value: decodeStringLiteral(m[3] !== undefined ? m[3] : m[5], !!(m[2] || m[4])),\n            });\n        } else if (symbols.has(token)) {\n            // transform 'not in' and 'is not' in a single token\n            if (token === \"in\" && tokens.length > 0 && tokens[tokens.length - 1].value === \"not\") {\n                token = \"not in\";\n                tokens.pop();\n            } else if (\n                token === \"not\" &&\n                tokens.length > 0 &&\n                tokens[tokens.length - 1].value === \"is\"\n            ) {\n                token = \"is not\";\n                tokens.pop();\n            }\n            tokens.push({\n                type: 2 /* Symbol */,\n                value: token,\n            });\n        } else if (constants.has(token)) {\n            tokens.push({\n                type: 4 /* Constant */,\n                value: token,\n            });\n        } else if (NamePattern.test(token)) {\n            tokens.push({\n                type: 3 /* Name */,\n                value: token,\n            });\n        } else {\n            throw new TokenizerError(\"Invalid expression\");\n        }\n    }\n    return tokens;\n}\n", "import { bp } from \"./py_parser\";\nimport { PyDate, PyDateTime } from \"./py_date\";\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @typedef { import(\"./py_parser\").AST } AST\n */\n\n// -----------------------------------------------------------------------------\n// Utils\n// -----------------------------------------------------------------------------\n\n/**\n * Represent any value as a primitive AST\n *\n * @param {any} value\n * @returns {AST}\n */\nexport function toPyValue(value) {\n    switch (typeof value) {\n        case \"string\":\n            return { type: 1 /* String */, value };\n        case \"number\":\n            return { type: 0 /* Number */, value };\n        case \"boolean\":\n            return { type: 2 /* Boolean */, value };\n        case \"object\":\n            if (Array.isArray(value)) {\n                return { type: 4 /* List */, value: value.map(toPyValue) };\n            } else if (value === null) {\n                return { type: 3 /* None */ };\n            } else if (value instanceof Date) {\n                return { type: 1, value: PyDateTime.convertDate(value) };\n            } else if (value instanceof PyDate || value instanceof PyDateTime) {\n                return { type: 1, value };\n            } else {\n                const content = {};\n                for (const key in value) {\n                    content[key] = toPyValue(value[key]);\n                }\n                return { type: 11 /* Dictionary */, value: content };\n            }\n        default:\n            throw new Error(\"Invalid type\");\n    }\n}\n\n/**\n * @param {AST} ast\n * @param {number} [lbp] left binding power\n * @return {string}\n */\nexport function formatAST(ast, lbp = 0) {\n    switch (ast.type) {\n        case 3 /* None */:\n            return \"None\";\n        case 1 /* String */:\n            return JSON.stringify(ast.value);\n        case 0 /* Number */:\n            return String(ast.value);\n        case 2 /* Boolean */:\n            return ast.value ? \"True\" : \"False\";\n        case 4 /* List */:\n            return `[${ast.value.map(formatAST).join(\", \")}]`;\n        case 6 /* UnaryOperator */:\n            if (ast.op === \"not\") {\n                return `not ` + formatAST(ast.right, 50);\n            }\n            return ast.op + formatAST(ast.right, 130);\n        case 7 /* BinaryOperator */: {\n            const abp = bp(ast.op);\n            const str = `${formatAST(ast.left, abp)} ${ast.op} ${formatAST(ast.right, abp)}`;\n            return abp < lbp ? `(${str})` : str;\n        }\n        case 11 /* Dictionary */: {\n            const pairs = [];\n            for (const k in ast.value) {\n                pairs.push(`\"${k}\": ${formatAST(ast.value[k])}`);\n            }\n            return `{` + pairs.join(\", \") + `}`;\n        }\n        case 10 /* Tuple */:\n            return `(${ast.value.map(formatAST).join(\", \")})`;\n        case 5 /* Name */:\n            return ast.value;\n        case 12 /* Lookup */: {\n            return `${formatAST(ast.target)}[${formatAST(ast.key)}]`;\n        }\n        case 13 /* If */: {\n            const { ifTrue, condition, ifFalse } = ast;\n            return `${formatAST(ifTrue)} if ${formatAST(condition)} else ${formatAST(ifFalse)}`;\n        }\n        case 14 /* BooleanOperator */: {\n            const abp = bp(ast.op);\n            const str = `${formatAST(ast.left, abp)} ${ast.op} ${formatAST(ast.right, abp)}`;\n            return abp < lbp ? `(${str})` : str;\n        }\n        case 15 /* ObjLookup */:\n            return `${formatAST(ast.obj, 150)}.${ast.key}`;\n        case 8 /* FunctionCall */: {\n            const args = ast.args.map(formatAST);\n            const kwargs = [];\n            for (const kwarg in ast.kwargs) {\n                kwargs.push(`${kwarg} = ${formatAST(ast.kwargs[kwarg])}`);\n            }\n            const argStr = args.concat(kwargs).join(\", \");\n            return `${formatAST(ast.fn)}(${argStr})`;\n        }\n    }\n    throw new Error(\"invalid expression: \" + ast);\n}\n\nexport const PY_DICT = Object.create(null);\n\n/**\n * @param {Object} obj\n * @returns {AST} a python dictionary\n */\nexport function toPyDict(obj) {\n    return new Proxy(obj, {\n        getPrototypeOf() {\n            return PY_DICT;\n        },\n    });\n}\n", "import { Component, onWillStart, onWillUpdateProps } from \"@odoo/owl\";\nimport { TagsList } from \"@web/core/tags_list/tags_list\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { RecordAutocomplete } from \"./record_autocomplete\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { useTagNavigation } from \"./tag_navigation_hook\";\n\nexport class MultiRecordSelector extends Component {\n    static props = {\n        resIds: { type: Array, element: Number },\n        resModel: String,\n        update: Function,\n        domain: { type: Array, optional: true },\n        context: { type: Object, optional: true },\n        fieldString: { type: String, optional: true },\n        placeholder: { type: String, optional: true },\n    };\n    static components = { RecordAutocomplete, TagsList };\n    static template = \"web.MultiRecordSelector\";\n\n    setup() {\n        this.nameService = useService(\"name\");\n        this.onTagKeydown = useTagNavigation(\"multiRecordSelector\", this.deleteTag.bind(this));\n        onWillStart(() => this.computeDerivedParams());\n        onWillUpdateProps((nextProps) => this.computeDerivedParams(nextProps));\n    }\n\n    async computeDerivedParams(props = this.props) {\n        const displayNames = await this.getDisplayNames(props);\n        this.tags = this.getTags(props, displayNames);\n    }\n\n    async getDisplayNames(props) {\n        const ids = this.getIds(props);\n        return this.nameService.loadDisplayNames(props.resModel, ids);\n    }\n\n    /**\n     * Placeholder should be empty if there is at least one tag. We cannot use\n     * the default behavior of the input placeholder because even if there is\n     * a tag, the input is still empty.\n     */\n    get placeholder() {\n        return this.getIds().length ? \"\" : this.props.placeholder;\n    }\n\n    getIds(props = this.props) {\n        return props.resIds;\n    }\n\n    getTags(props, displayNames) {\n        return props.resIds.map((id, index) => {\n            const text =\n                typeof displayNames[id] === \"string\"\n                    ? displayNames[id]\n                    : _t(\"Inaccessible/missing record ID: %s\", id);\n            return {\n                text,\n                onDelete: () => {\n                    this.deleteTag(index);\n                },\n                onKeydown: this.onTagKeydown,\n            };\n        });\n    }\n\n    deleteTag(index) {\n        this.props.update([\n            ...this.props.resIds.slice(0, index),\n            ...this.props.resIds.slice(index + 1),\n        ]);\n    }\n\n    update(resIds) {\n        this.props.update([...this.props.resIds, ...resIds]);\n    }\n}\n", "import { Component } from \"@odoo/owl\";\nimport { AutoComplete } from \"@web/core/autocomplete/autocomplete\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { Domain } from \"@web/core/domain\";\nimport { registry } from \"@web/core/registry\";\nimport { useOwnedDialogs, useService } from \"@web/core/utils/hooks\";\n\nconst SEARCH_LIMIT = 7;\nconst SEARCH_MORE_LIMIT = 320;\n\nexport class RecordAutocomplete extends Component {\n    static props = {\n        resModel: String,\n        update: Function,\n        multiSelect: Boolean,\n        getIds: Function,\n        value: String,\n        domain: { type: Array, optional: true },\n        context: { type: Object, optional: true },\n        className: { type: String, optional: true },\n        fieldString: { type: String, optional: true },\n        placeholder: { type: String, optional: true },\n    };\n    static components = { AutoComplete };\n    static template = \"web.RecordAutocomplete\";\n\n    setup() {\n        this.orm = useService(\"orm\");\n        this.nameService = useService(\"name\");\n        this.addDialog = useOwnedDialogs();\n        this.sources = [\n            {\n                placeholder: _t(\"Loading...\"),\n                options: this.loadOptionsSource.bind(this),\n            },\n        ];\n    }\n\n    addNames(options) {\n        const displayNames = Object.fromEntries(options);\n        this.nameService.addDisplayNames(this.props.resModel, displayNames);\n    }\n\n    getIds() {\n        return this.props.getIds();\n    }\n\n    async loadOptionsSource(name) {\n        if (this.lastProm) {\n            this.lastProm.abort(false);\n        }\n        this.lastProm = this.search(name, SEARCH_LIMIT + 1);\n        const nameGets = (await this.lastProm).map(([id, label]) => ([id, label ? label.split(\"\\n\")[0] : _t(\"Unnamed\")]));\n        this.addNames(nameGets);\n        const options = nameGets.map(([value, label]) => ({value, label}));\n        if (SEARCH_LIMIT < nameGets.length) {\n            options.push({\n                label: _t(\"Search More...\"),\n                action: this.onSearchMore.bind(this, name),\n                classList: \"o_m2o_dropdown_option\",\n            });\n        }\n        if (options.length === 0) {\n            options.push({ label: _t(\"(no result)\"), unselectable: true });\n        }\n        return options;\n    }\n\n    async onSearchMore(name) {\n        const { fieldString, multiSelect, resModel } = this.props;\n        let operator;\n        const ids = [];\n        if (name) {\n            const nameGets = await this.search(name, SEARCH_MORE_LIMIT);\n            this.addNames(nameGets);\n            operator = \"in\";\n            ids.push(...nameGets.map((nameGet) => nameGet[0]));\n        } else {\n            operator = \"not in\";\n            ids.push(...this.getIds());\n        }\n        const dynamicFilters = ids.length\n            ? [\n                  {\n                      description: _t(\"Quick search: %s\", name),\n                      domain: [[\"id\", operator, ids]],\n                  },\n              ]\n            : undefined;\n        // fine for now but we don't like this kind of dependence of core to views\n        const SelectCreateDialog = registry.category(\"dialogs\").get(\"select_create\");\n        this.addDialog(SelectCreateDialog, {\n            title: _t(\"Search: %s\", fieldString),\n            dynamicFilters,\n            domain: this.getDomain(),\n            resModel,\n            noCreate: true,\n            multiSelect,\n            context: this.props.context || {},\n            onSelected: (resId) => {\n                const resIds = Array.isArray(resId) ? resId : [resId];\n                this.props.update([...resIds]);\n            },\n        });\n    }\n\n    getDomain() {\n        const domainIds = Domain.not([[\"id\", \"in\", this.getIds()]]);\n        if (this.props.domain) {\n            return Domain.and([this.props.domain, domainIds]).toList();\n        }\n        return domainIds.toList();\n    }\n\n    onSelect({ value: resId, action }, params) {\n        if (action) {\n            return action(params);\n        }\n        this.props.update([resId]);\n    }\n\n    search(name, limit) {\n        const domain = this.getDomain();\n        return this.orm.call(this.props.resModel, \"name_search\", [], {\n            name,\n            args: domain,\n            limit,\n            context: this.props.context || {},\n        });\n    }\n\n    onChange({ inputValue }) {\n        if (!inputValue.length) {\n            this.props.update([]);\n        }\n    }\n}\n", "import { Component, onWillStart, onWillUpdateProps } from \"@odoo/owl\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { RecordAutocomplete } from \"./record_autocomplete\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport class RecordSelector extends Component {\n    static props = {\n        resId: [Number, { value: false }],\n        resModel: String,\n        update: Function,\n        domain: { type: Array, optional: true },\n        context: { type: Object, optional: true },\n        fieldString: { type: String, optional: true },\n        placeholder: { type: String, optional: true },\n    };\n    static components = { RecordAutocomplete };\n    static template = \"web.RecordSelector\";\n\n    setup() {\n        this.nameService = useService(\"name\");\n        onWillStart(() => this.computeDerivedParams());\n        onWillUpdateProps((nextProps) => this.computeDerivedParams(nextProps));\n    }\n\n    async computeDerivedParams(props = this.props) {\n        const displayNames = await this.getDisplayNames(props);\n        this.displayName = this.getDisplayName(props, displayNames);\n    }\n\n    async getDisplayNames(props) {\n        const ids = this.getIds(props);\n        return this.nameService.loadDisplayNames(props.resModel, ids);\n    }\n\n    getDisplayName(props = this.props, displayNames) {\n        const { resId } = props;\n        if (resId === false) {\n            return \"\";\n        }\n        return typeof displayNames[resId] === \"string\"\n            ? displayNames[resId]\n            : _t(\"Inaccessible/missing record ID: %s\", resId);\n    }\n\n    getIds(props = this.props) {\n        if (props.resId) {\n            return [props.resId];\n        }\n        return [];\n    }\n\n    update(resIds) {\n        this.props.update(resIds[0] || false);\n        this.render(true);\n    }\n}\n", "import { getActiveHotkey } from \"@web/core/hotkeys/hotkey_service\";\n\nimport { useEffect, useRef } from \"@odoo/owl\";\n\n/**\n * This hook allows to navigate between tags in a record selector. It also\n * allows to delete tags with the backspace key.\n * It is meant to be used in component which contains both the components\n * `Autocomplete` and `TagList`.\n *\n * @param {string} refName Name of the t-ref which contains the `Autocomplete` and `TagList` components.\n * @param {Function} deleteTag Function to be called when a tag is deleted. It should take the index of the tag to delete as parameter.\n * @returns {Function} Function to be called when a tag is focused and a key is pressed. It should be passed to the `onKeydown` prop of the `Tag` component.\n */\nexport function useTagNavigation(refName, deleteTag) {\n    const ref = useRef(refName);\n\n    useEffect(\n        (autocomplete) => {\n            if (!autocomplete) {\n                return;\n            }\n            autocomplete.addEventListener(\"keydown\", onAutoCompleteKeydown);\n            return () => {\n                autocomplete.removeEventListener(\"keydown\", onAutoCompleteKeydown);\n            };\n        },\n        () => [ref.el?.querySelector(\".o-autocomplete\")]\n    );\n\n    /**\n     * Focus the tag at the given index. If no index is given, focus the rightmost tag.\n     * @param {number|undefined} index Index of the tag to focus. If undefined, focus the rightmost tag.\n     */\n    function focusTag(index) {\n        const tags = ref.el.getElementsByClassName(\"o_tag\");\n        if (tags.length) {\n            if (index === undefined) {\n                tags[tags.length - 1].focus();\n            } else {\n                tags[index].focus();\n            }\n        }\n    }\n\n    /**\n     * Function to be called when a key is pressed in the `Autocomplete` component.\n     *\n     * @param {Event} ev\n     */\n    function onAutoCompleteKeydown(ev) {\n        if (ev.isComposing) {\n            // This case happens with an IME for example: we let it handle all key events.\n            return;\n        }\n        const hotkey = getActiveHotkey(ev);\n        const input = ev.target.closest(\".o-autocomplete--input\");\n        const autoCompleteMenuOpened = !!ref.el.querySelector(\".o-autocomplete--dropdown-menu\");\n        switch (hotkey) {\n            case \"arrowleft\": {\n                if (input.selectionStart || autoCompleteMenuOpened) {\n                    return;\n                }\n                // focus rightmost tag if any.\n                focusTag();\n                break;\n            }\n            case \"arrowright\": {\n                if (input.selectionStart !== input.value.length || autoCompleteMenuOpened) {\n                    return;\n                }\n                // focus leftmost tag if any.\n                focusTag(0);\n                break;\n            }\n            case \"backspace\": {\n                if (input.value) {\n                    return;\n                }\n                const tags = ref.el.getElementsByClassName(\"o_tag\");\n                if (tags.length) {\n                    deleteTag(tags.length - 1);\n                }\n                break;\n            }\n            default:\n                return;\n        }\n        ev.preventDefault();\n        ev.stopPropagation();\n    }\n\n    /**\n     * Function to be called when a key is pressed in the `Tag` component.\n     * It should be passed to the `onKeydown` prop of the `Tag` component.\n     *\n     * @param {Event} ev\n     */\n    function onTagKeydown(ev) {\n        const hotkey = getActiveHotkey(ev);\n        const tags = [...ref.el.getElementsByClassName(\"o_tag\")];\n        const closestTag = ev.target.closest(\".o_tag\");\n        const tagIndex = tags.indexOf(closestTag);\n        const input = ref.el.querySelector(\".o-autocomplete--input\");\n        switch (hotkey) {\n            case \"arrowleft\": {\n                if (tagIndex === 0) {\n                    input.focus();\n                } else {\n                    focusTag(tagIndex - 1);\n                }\n                break;\n            }\n            case \"arrowright\": {\n                if (tagIndex === tags.length - 1) {\n                    input.focus();\n                } else {\n                    focusTag(tagIndex + 1);\n                }\n                break;\n            }\n            case \"backspace\": {\n                input.focus();\n                deleteTag(tagIndex);\n                break;\n            }\n            default:\n                return;\n        }\n        ev.preventDefault();\n        ev.stopPropagation();\n    }\n\n    return onTagKeydown;\n}\n", "import { EventBus, validate } from \"@odoo/owl\";\n\n// -----------------------------------------------------------------------------\n// Errors\n// -----------------------------------------------------------------------------\nexport class KeyNotFoundError extends Error {}\n\nexport class DuplicatedKeyError extends Error {}\n\n// -----------------------------------------------------------------------------\n// Validation\n// -----------------------------------------------------------------------------\n\nconst validateSchema = (value, schema) => {\n    if (!odoo.debug) {\n        return;\n    }\n    validate(value, schema);\n}\n\n// -----------------------------------------------------------------------------\n// Types\n// -----------------------------------------------------------------------------\n\n/**\n * @template S\n * @template C\n * @typedef {import(\"registries\").RegistryData<S, C>} RegistryData\n */\n\n/**\n * @template T\n * @typedef {T extends RegistryData<any, any> ? T : RegistryData<T, {}>} ToRegistryData\n */\n\n/**\n * @template T\n * @typedef {ToRegistryData<T>[\"__itemShape\"]} GetRegistryItemShape\n */\n\n/**\n * @template T\n * @typedef {ToRegistryData<T>[\"__categories\"]} GetRegistryCategories\n */\n\n/**\n * Registry\n *\n * The Registry class is basically just a mapping from a string key to an object.\n * It is really not much more than an object. It is however useful for the\n * following reasons:\n *\n * 1. it let us react and execute code when someone add something to the registry\n *   (for example, the FunctionRegistry subclass this for this purpose)\n * 2. it throws an error when the get operation fails\n * 3. it provides a chained API to add items to the registry.\n *\n * @template T\n */\nexport class Registry extends EventBus {\n    /**\n     * @param {string} [name]\n     */\n    constructor(name) {\n        super();\n        /** @type {Record<string, [number, GetRegistryItemShape<T>]>}*/\n        this.content = {};\n        /** @type {{ [P in keyof GetRegistryCategories<T>]?: Registry<GetRegistryCategories<T>[P]> }} */\n        this.subRegistries = {};\n        /** @type {GetRegistryItemShape<T>[]}*/\n        this.elements = null;\n        /** @type {[string, GetRegistryItemShape<T>][]}*/\n        this.entries = null;\n        this.name = name;\n        this.validationSchema = null;\n\n        this.addEventListener(\"UPDATE\", () => {\n            this.elements = null;\n            this.entries = null;\n        });\n    }\n\n    /**\n     * Add an entry (key, value) to the registry if key is not already used. If\n     * the parameter force is set to true, an entry with same key (if any) is replaced.\n     *\n     * Note that this also returns the registry, so another add method call can\n     * be chained\n     *\n     * @param {string} key\n     * @param {GetRegistryItemShape<T>} value\n     * @param {{force?: boolean, sequence?: number}} [options]\n     * @returns {Registry<T>}\n     */\n    add(key, value, { force, sequence } = {}) {\n        if (this.validationSchema) {\n            validateSchema(value, this.validationSchema);\n        }\n        if (!force && key in this.content) {\n            throw new DuplicatedKeyError(\n                `Cannot add key \"${key}\" in the \"${this.name}\" registry: it already exists`\n            );\n        }\n        let previousSequence;\n        if (force) {\n            const elem = this.content[key];\n            previousSequence = elem && elem[0];\n        }\n        sequence = sequence === undefined ? previousSequence || 50 : sequence;\n        this.content[key] = [sequence, value];\n        const payload = { operation: \"add\", key, value };\n        this.trigger(\"UPDATE\", payload);\n        return this;\n    }\n\n    /**\n     * Get an item from the registry\n     *\n     * @param {string} key\n     * @returns {GetRegistryItemShape<T>}\n     */\n    get(key, defaultValue) {\n        if (arguments.length < 2 && !(key in this.content)) {\n            throw new KeyNotFoundError(`Cannot find key \"${key}\" in the \"${this.name}\" registry`);\n        }\n        const info = this.content[key];\n        return info ? info[1] : defaultValue;\n    }\n\n    /**\n     * Check the presence of a key in the registry\n     *\n     * @param {string} key\n     * @returns {boolean}\n     */\n    contains(key) {\n        return key in this.content;\n    }\n\n    /**\n     * Get a list of all elements in the registry. Note that it is ordered\n     * according to the sequence numbers.\n     *\n     * @returns {GetRegistryItemShape<T>[]}\n     */\n    getAll() {\n        if (!this.elements) {\n            const content = Object.values(this.content).sort((el1, el2) => el1[0] - el2[0]);\n            this.elements = content.map((elem) => elem[1]);\n        }\n        return this.elements.slice();\n    }\n\n    /**\n     * Return a list of all entries, ordered by sequence numbers.\n     *\n     * @returns {[string, GetRegistryItemShape<T>][]}\n     */\n    getEntries() {\n        if (!this.entries) {\n            const entries = Object.entries(this.content).sort((el1, el2) => el1[1][0] - el2[1][0]);\n            this.entries = entries.map(([str, elem]) => [str, elem[1]]);\n        }\n        return this.entries.slice();\n    }\n\n    /**\n     * Remove an item from the registry\n     *\n     * @param {string} key\n     */\n    remove(key) {\n        const value = this.content[key];\n        delete this.content[key];\n        const payload = { operation: \"delete\", key, value };\n        this.trigger(\"UPDATE\", payload);\n    }\n\n    /**\n     * Open a sub registry (and create it if necessary)\n     *\n     * @template {keyof GetRegistryCategories<T> & string} K\n     * @param {K} subcategory\n     * @returns {Registry<GetRegistryCategories<T>[K]>}\n     */\n    category(subcategory) {\n        if (!(subcategory in this.subRegistries)) {\n            this.subRegistries[subcategory] = new Registry(subcategory);\n        }\n        return this.subRegistries[subcategory];\n    }\n\n    addValidation(schema) {\n        if (this.validationSchema) {\n            throw new Error(\"Validation schema already set on this registry\");\n        }\n        this.validationSchema = schema;\n        for (const value of this.getAll()) {\n            validateSchema(value, schema);\n        }\n    }\n}\n\n/** @type {Registry<import(\"registries\").GlobalRegistry>} */\nexport const registry = new Registry();\n", "import { useState, onWillStart, onWillDestroy } from \"@odoo/owl\";\n\nexport function useRegistry(registry) {\n    const state = useState({ entries: registry.getEntries() });\n    const listener = ({ detail }) => {\n        const index = state.entries.findIndex(([k]) => k === detail.key);\n        if (detail.operation === \"add\" && index === -1) {\n            // push the new entry at the right place\n            const newEntries = registry.getEntries();\n            const newEntry = newEntries.find(([k]) => k === detail.key);\n            const newIndex = newEntries.indexOf(newEntry);\n            if (newIndex === newEntries.length - 1) {\n                state.entries.push(newEntry);\n            } else {\n                state.entries.splice(newIndex, 0, newEntry);\n            }\n        } else if (detail.operation === \"delete\" && index >= 0) {\n            state.entries.splice(index, 1);\n        }\n    };\n\n    onWillStart(() => registry.addEventListener(\"UPDATE\", listener));\n    onWillDestroy(() => registry.removeEventListener(\"UPDATE\", listener));\n    return state;\n}\n", "import {\n    Component,\n    onMounted,\n    onWillUpdateProps,\n    onWillUnmount,\n    useEffect,\n    useExternalListener,\n    useRef,\n    useComponent,\n} from \"@odoo/owl\";\n\nfunction useResizable({\n    containerRef,\n    handleRef,\n    initialWidth = 400,\n    getMinWidth = () => 400,\n    onResize = () => {},\n    getResizeSide = () => \"end\",\n}) {\n    containerRef = typeof containerRef == \"string\" ? useRef(containerRef) : containerRef;\n    handleRef = typeof handleRef == \"string\" ? useRef(handleRef) : handleRef;\n    const props = useComponent().props;\n\n    let minWidth = getMinWidth(props);\n    let resizeSide = getResizeSide(props);\n    let isChangingSize = false;\n\n    useExternalListener(document, \"mouseup\", () => onMouseUp());\n    useExternalListener(document, \"mousemove\", (ev) => onMouseMove(ev));\n\n    useExternalListener(window, \"resize\", () => {\n        const limit = getLimitWidth();\n        if (getContainerRect().width >= limit) {\n            resize(computeFinalWidth(limit));\n        }\n    });\n\n    let docDirection;\n    useEffect(\n        (container) => {\n            if (container) {\n                docDirection = getComputedStyle(container).direction;\n            }\n        },\n        () => [containerRef.el]\n    );\n\n    onMounted(() => {\n        if (handleRef.el) {\n            resize(initialWidth);\n            handleRef.el.addEventListener(\"mousedown\", onMouseDown);\n        }\n    });\n\n    onWillUpdateProps((nextProps) => {\n        minWidth = getMinWidth(nextProps);\n        resizeSide = getResizeSide(nextProps);\n    });\n\n    onWillUnmount(() => {\n        if (handleRef.el) {\n            handleRef.el.removeEventListener(\"mousedown\", onMouseDown);\n        }\n    });\n\n    function onMouseDown() {\n        isChangingSize = true;\n        document.body.classList.add(\"pe-none\", \"user-select-none\");\n    }\n\n    function onMouseUp() {\n        isChangingSize = false;\n        document.body.classList.remove(\"pe-none\", \"user-select-none\");\n    }\n\n    function onMouseMove(ev) {\n        if (!isChangingSize || !containerRef.el) {\n            return;\n        }\n        const direction =\n            (docDirection === \"ltr\" && resizeSide === \"end\") ||\n            (docDirection === \"rtl\" && resizeSide === \"start\")\n                ? 1\n                : -1;\n        const fixedSide = direction === 1 ? \"left\" : \"right\";\n        const containerRect = getContainerRect();\n        const newWidth = (ev.clientX - containerRect[fixedSide]) * direction;\n        resize(computeFinalWidth(newWidth));\n    }\n\n    function computeFinalWidth(targetContainerWidth) {\n        const handlerSpacing = handleRef.el ? handleRef.el.offsetWidth / 2 : 10;\n        const w = Math.max(minWidth, targetContainerWidth + handlerSpacing);\n        const limit = getLimitWidth();\n        return Math.min(w, limit - handlerSpacing);\n    }\n\n    function getContainerRect() {\n        const container = containerRef.el;\n        const offsetParent = container.offsetParent;\n        let containerRect = {};\n        if (!offsetParent) {\n            containerRect = container.getBoundingClientRect();\n        } else {\n            containerRect.left = container.offsetLeft;\n            containerRect.right = container.offsetLeft + container.offsetWidth;\n            containerRect.width = container.offsetWidth;\n        }\n        return containerRect;\n    }\n\n    function getLimitWidth() {\n        const offsetParent = containerRef.el.offsetParent;\n        return offsetParent ? offsetParent.offsetWidth : window.innerWidth;\n    }\n\n    function resize(width) {\n        containerRef.el.style.setProperty(\"width\", `${width}px`);\n        onResize(width);\n    }\n}\n\nexport class ResizablePanel extends Component {\n    static template = \"web_studio.ResizablePanel\";\n\n    static components = {};\n    static props = {\n        onResize: { type: Function, optional: true },\n        initialWidth: { type: Number, optional: true },\n        minWidth: { type: Number, optional: true },\n        class: { type: String, optional: true },\n        slots: { type: Object },\n        handleSide: {\n            validate: (val) => [\"start\", \"end\"].includes(val),\n            optional: true,\n        },\n    };\n    static defaultProps = {\n        onResize: () => {},\n        width: 400,\n        minWidth: 400,\n        class: \"\",\n        handleSide: \"end\",\n    };\n\n    setup() {\n        useResizable({\n            containerRef: \"containerRef\",\n            handleRef: \"handleRef\",\n            onResize: this.props.onResize,\n            initialWidth: this.props.initialWidth,\n            getMinWidth: (props) => props.minWidth,\n            getResizeSide: (props) => props.handleSide,\n        });\n    }\n\n    get class() {\n        const classes = this.props.class.split(\" \");\n        if (!classes.some((cls) => cls.startsWith(\"position-\"))) {\n            classes.push(\"position-relative\");\n        }\n        return classes.join(\" \");\n    }\n}\n", "import { Component, onWillUpdateProps, useEffect, useRef, useState } from \"@odoo/owl\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { TagsList } from \"@web/core/tags_list/tags_list\";\nimport { mergeClasses } from \"@web/core/utils/classname\";\nimport { useAutofocus, useChildRef } from \"@web/core/utils/hooks\";\nimport { scrollTo } from \"@web/core/utils/scrolling\";\nimport { fuzzyLookup } from \"@web/core/utils/search\";\nimport { useDebounced } from \"@web/core/utils/timing\";\n\nexport class SelectMenu extends Component {\n    static template = \"web.SelectMenu\";\n    static choiceItemTemplate = \"web.SelectMenu.ChoiceItem\";\n\n    static components = { Dropdown, DropdownItem, TagsList };\n\n    static defaultProps = {\n        value: undefined,\n        class: \"\",\n        togglerClass: \"\",\n        multiSelect: false,\n        onSelect: () => {},\n        required: false,\n        searchable: true,\n        autoSort: true,\n        searchPlaceholder: _t(\"Search...\"),\n        choices: [],\n        groups: [],\n        disabled: false,\n    };\n\n    static props = {\n        choices: {\n            optional: true,\n            type: Array,\n            element: {\n                type: Object,\n                shape: {\n                    value: true,\n                    label: { type: String },\n                    \"*\": true,\n                },\n            },\n        },\n        groups: {\n            type: Array,\n            optional: true,\n            element: {\n                type: Object,\n                shape: {\n                    label: { type: String, optional: true },\n                    choices: {\n                        type: Array,\n                        element: {\n                            type: Object,\n                            shape: {\n                                value: true,\n                                label: { type: String },\n                                \"*\": true,\n                            },\n                        },\n                    },\n                },\n            },\n        },\n        class: { type: String, optional: true },\n        menuClass: { type: String, optional: true },\n        togglerClass: { type: String, optional: true },\n        required: { type: Boolean, optional: true },\n        searchable: { type: Boolean, optional: true },\n        autoSort: { type: Boolean, optional: true },\n        placeholder: { type: String, optional: true },\n        searchPlaceholder: { type: String, optional: true },\n        value: { optional: true },\n        multiSelect: { type: Boolean, optional: true },\n        onInput: { type: Function, optional: true },\n        onSelect: { type: Function, optional: true },\n        slots: { type: Object, optional: true },\n        disabled: { type: Boolean, optional: true },\n    };\n\n    static SCROLL_SETTINGS = {\n        defaultCount: 500,\n        increaseAmount: 300,\n        distanceBeforeReload: 500,\n    };\n\n    setup() {\n        this.state = useState({\n            choices: [],\n            displayedOptions: [],\n            searchValue: \"\",\n        });\n        this.inputRef = useRef(\"inputRef\");\n        this.menuRef = useChildRef();\n        this.debouncedOnInput = useDebounced(\n            () => this.onInput(this.inputRef.el ? this.inputRef.el.value.trim() : \"\"),\n            250\n        );\n        this.isOpen = false;\n\n        this.selectedChoice = this.getSelectedChoice(this.props);\n        onWillUpdateProps((nextProps) => {\n            if (this.state.choices !== nextProps.choices) {\n                this.state.choices = nextProps.choices;\n            }\n            if (this.props.value !== nextProps.value) {\n                this.selectedChoice = this.getSelectedChoice(nextProps);\n            }\n        });\n        useEffect(\n            () => {\n                if (this.isOpen) {\n                    const groups = [{ choices: this.props.choices }, ...this.props.groups];\n                    this.filterOptions(this.state.searchValue, groups);\n                }\n            },\n            () => [this.props.choices, this.props.groups]\n        );\n        useAutofocus({ refName: \"inputRef\" });\n    }\n\n    get displayValue() {\n        return this.selectedChoice ? this.selectedChoice.label : \"\";\n    }\n\n    get canDeselect() {\n        return !this.props.required && this.selectedChoice !== undefined;\n    }\n\n    get multiSelectChoices() {\n        return this.selectedChoice.map((c) => {\n            return {\n                id: c.value,\n                text: c.label,\n                onDelete: () => {\n                    const values = [...this.props.value];\n                    values.splice(values.indexOf(c.value), 1);\n                    this.props.onSelect(values);\n                },\n            };\n        });\n    }\n\n    get menuClass() {\n        return mergeClasses(\n            {\n                \"o_select_menu_menu border bg-light\": true,\n                \"py-0\": this.props.searchable,\n                o_select_menu_multi_select: this.props.multiSelect,\n            },\n            this.props.menuClass\n        );\n    }\n\n    async onBeforeOpen() {\n        if (this.state.searchValue.length) {\n            this.state.searchValue = \"\";\n            if (this.props.onInput) {\n                // This props can be used by the parent to fetch items dynamically depending\n                // the search value. It must be called with the empty search value.\n                await this.executeOnInput(\"\");\n            }\n        }\n        this.filterOptions();\n    }\n\n    onStateChanged(open) {\n        this.isOpen = open;\n        if (open) {\n            this.menuRef.el?.addEventListener(\"scroll\", (ev) => this.onScroll(ev));\n            const selectedElement = this.menuRef.el?.querySelectorAll(\".o_select_active\")[0];\n            if (selectedElement) {\n                scrollTo(selectedElement);\n            }\n        }\n    }\n\n    isOptionSelected(choice) {\n        if (this.props.multiSelect) {\n            return this.props.value.includes(choice.value);\n        }\n        return this.props.value === choice.value;\n    }\n\n    getItemClass(choice) {\n        if (this.isOptionSelected(choice)) {\n            return \"o_select_menu_item p-2 o_select_active bg-primary fw-bolder fst-italic\";\n        } else {\n            return \"o_select_menu_item p-2\";\n        }\n    }\n\n    async executeOnInput(searchString) {\n        await this.props.onInput(searchString);\n    }\n\n    onInput(searchString) {\n        this.filterOptions(searchString);\n        this.state.searchValue = searchString;\n\n        // Get reference to dropdown container and scroll to the top.\n        const inputEl = this.inputRef.el;\n        if (inputEl && inputEl.parentNode) {\n            inputEl.parentNode.scrollTo(0, 0);\n        }\n        if (this.props.onInput) {\n            this.executeOnInput(searchString);\n        }\n    }\n\n    getSelectedChoice(props) {\n        const choices = [...props.choices, ...props.groups.flatMap((g) => g.choices)];\n        if (!this.props.multiSelect) {\n            return choices.find((c) => c.value === props.value);\n        }\n\n        const valueSet = new Set(props.value);\n        // Combine previously selected choices + newly selected choice from\n        // the searched choices and then filter the choices based on\n        // props.value i.e. valueSet.\n        return [...(this.selectedChoice || []), ...choices].filter((c, index, self) =>\n            valueSet.has(c.value)\n            && self.findIndex((t) => t.value === c.value) === index\n        );\n    }\n\n    onItemSelected(value) {\n        if (this.props.multiSelect) {\n            const values = [...this.props.value];\n            const valueIndex = values.indexOf(value);\n\n            if (valueIndex !== -1) {\n                values.splice(valueIndex, 1);\n                this.props.onSelect(values);\n            } else {\n                this.props.onSelect([...this.props.value, value]);\n            }\n        } else if (!this.selectedChoice || this.selectedChoice.value !== value) {\n            this.props.onSelect(value);\n        }\n        if (this.inputRef.el) {\n            this.inputRef.el.value = \"\";\n            this.state.searchValue = \"\";\n        }\n    }\n\n    // ==========================================================================================\n    // #                                         Search                                         #\n    // ==========================================================================================\n\n    /**\n     * Filters the choices based on the searchString and\n     * slice the result to display a reasonable amount to\n     * try to prevent any delay when opening the select.\n     *\n     * @param {String} searchString\n     */\n    filterOptions(searchString = \"\", groups) {\n        const groupsList = groups || [{ choices: this.props.choices }, ...this.props.groups];\n\n        this.state.choices = [];\n\n        for (const group of groupsList) {\n            let filteredOptions = [];\n\n            if (searchString) {\n                filteredOptions = fuzzyLookup(\n                    searchString,\n                    group.choices,\n                    (choice) => choice.label\n                );\n            } else {\n                filteredOptions = group.choices;\n                if (this.props.autoSort) {\n                    filteredOptions.sort((optionA, optionB) =>\n                        optionA.label.localeCompare(optionB.label)\n                    );\n                }\n            }\n\n            if (filteredOptions.length === 0) {\n                continue;\n            }\n\n            if (group.label) {\n                this.state.choices.push({ ...group, isGroup: true });\n            }\n            this.state.choices.push(...filteredOptions);\n        }\n\n        this.sliceDisplayedOptions();\n    }\n\n    // ==========================================================================================\n    // #                                         Scroll                                         #\n    // ==========================================================================================\n\n    /**\n     * If the user scrolls to the end of the dropdown,\n     * more choices are loaded.\n     *\n     * @param {*} event\n     */\n    onScroll(event) {\n        const el = event.target;\n        const hasReachMax = this.state.displayedOptions.length >= this.state.choices.length;\n        const remainingDistance = el.scrollHeight - el.scrollTop;\n        const distanceToReload =\n            el.clientHeight + this.constructor.SCROLL_SETTINGS.distanceBeforeReload;\n\n        if (!hasReachMax && remainingDistance < distanceToReload) {\n            const displayCount =\n                this.state.displayedOptions.length +\n                this.constructor.SCROLL_SETTINGS.increaseAmount;\n\n            this.state.displayedOptions = this.state.choices.slice(0, displayCount);\n        }\n    }\n\n    /**\n     * Finds the selected choice and set [displayOptions] to at\n     * least show the selected choice and [defaultCount] more\n     * or show at least the [defaultDisplayCount].\n     */\n    sliceDisplayedOptions() {\n        const selectedIndex = this.getSelectedOptionIndex();\n        const defaultCount = this.constructor.SCROLL_SETTINGS.defaultCount;\n\n        if (selectedIndex === -1) {\n            this.state.displayedOptions = this.state.choices.slice(0, defaultCount);\n        } else {\n            const endIndex = Math.max(\n                selectedIndex + this.constructor.SCROLL_SETTINGS.increaseAmount,\n                defaultCount\n            );\n            this.state.displayedOptions = this.state.choices.slice(0, endIndex);\n        }\n    }\n\n    getSelectedOptionIndex() {\n        let selectedIndex = -1;\n        for (let i = 0; i < this.state.choices.length; i++) {\n            if (this.isOptionSelected(this.state.choices[i])) {\n                selectedIndex = i;\n            }\n        }\n        return selectedIndex;\n    }\n}\n", "/* global SignaturePad */\n\nimport { loadJS } from \"@web/core/assets\";\nimport { isMobileOS } from \"@web/core/browser/feature_detection\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useAutofocus } from \"@web/core/utils/hooks\";\nimport { renderToString } from \"@web/core/utils/render\";\nimport { getDataURLFromFile } from \"@web/core/utils/urls\";\n\nimport { Component, useState, onWillStart, useRef, useEffect } from \"@odoo/owl\";\n\nlet htmlId = 0;\nexport class NameAndSignature extends Component {\n    static template = \"web.NameAndSignature\";\n    static components = { Dropdown, DropdownItem };\n    static props = {\n        signature: { type: Object },\n        defaultFont: { type: String, optional: true },\n        displaySignatureRatio: { type: Number, optional: true },\n        fontColor: { type: String, optional: true },\n        signatureType: { type: String, optional: true },\n        noInputName: { type: Boolean, optional: true },\n        mode: { type: String, optional: true },\n        onSignatureChange: { type: Function, optional: true },\n    };\n    static defaultProps = {\n        defaultFont: \"\",\n        displaySignatureRatio: 3.0,\n        fontColor: \"DarkBlue\",\n        signatureType: \"signature\",\n        noInputName: false,\n        onSignatureChange: () => {},\n    };\n\n    setup() {\n        this.htmlId = htmlId++;\n        this.defaultName = this.props.signature.name || \"\";\n        this.currentFont = 0;\n        this.drawTimeout = null;\n\n        this.state = useState({\n            signMode:\n                this.props.mode || (this.props.noInputName && !this.defaultName ? \"draw\" : \"auto\"),\n            showSignatureArea: !!(this.props.noInputName || this.defaultName),\n            showFontList: false,\n        });\n\n        this.signNameInputRef = useRef(\"signNameInput\");\n        this.signInputLoad = useRef(\"signInputLoad\");\n        useAutofocus({ refName: \"signNameInput\" });\n        useEffect(\n            (el) => {\n                if (el) {\n                    el.click();\n                }\n            },\n            () => [this.signInputLoad.el]\n        );\n\n        onWillStart(async () => {\n            this.fonts = await rpc(`/web/sign/get_fonts/${this.props.defaultFont}`);\n        });\n\n        onWillStart(async () => {\n            await loadJS(\"/web/static/lib/signature_pad/signature_pad.umd.js\");\n        });\n\n        this.signatureRef = useRef(\"signature\");\n        useEffect(\n            (el) => {\n                if (el) {\n                    this.signaturePad = new SignaturePad(el, {\n                        penColor: this.props.fontColor,\n                        backgroundColor: \"rgba(255,255,255,0)\",\n                        minWidth: 2,\n                        maxWidth: 2,\n                    });\n                    this.signaturePad.addEventListener(\"endStroke\", () => {\n                        this.props.signature.isSignatureEmpty = this.isSignatureEmpty;\n                        this.props.onSignatureChange(this.state.signMode);\n                    });\n                    this.resetSignature();\n                    this.props.signature.getSignatureImage = () => this.signaturePad.toDataURL();\n                    this.props.signature.resetSignature = () => this.resetSignature();\n                    if (this.state.signMode === \"auto\") {\n                        this.drawCurrentName();\n                    }\n                    if (this.props.signature.signatureImage) {\n                        this.clear();\n                        this.signaturePad.fromDataURL(this.props.signature.signatureImage);\n                    }\n                }\n            },\n            () => [this.signatureRef.el]\n        );\n    }\n\n    /**\n     * Draws the current name with the current font in the signature field.\n     */\n    async drawCurrentName() {\n        const font = this.fonts[this.currentFont];\n        const text = this.getCleanedName();\n        const canvas = this.signatureRef.el;\n        const img = this.getSVGText(font, text, canvas.width, canvas.height);\n        await this.printImage(img);\n    }\n\n    focusName() {\n        // Don't focus on mobile\n        if (!isMobileOS() && this.signNameInputRef.el) {\n            this.signNameInputRef.el.focus();\n        }\n    }\n\n    /**\n     * Clear the signature field.\n     */\n    clear() {\n        this.signaturePad.clear();\n        this.props.signature.isSignatureEmpty = this.isSignatureEmpty;\n    }\n\n    /**\n     * Returns the given name after cleaning it by removing characters that\n     * are not supposed to be used in a signature. If @see signatureType is set\n     * to 'initial', returns the first letter of each word, separated by dots.\n     *\n     * @returns {string} cleaned name\n     */\n    getCleanedName() {\n        const text = this.props.signature.name;\n        if (this.props.signatureType === \"initial\" && text) {\n            return (\n                text\n                    .split(\" \")\n                    .map(function (w) {\n                        return w[0];\n                    })\n                    .join(\".\") + \".\"\n            );\n        }\n        return text;\n    }\n\n    /**\n     * Gets an SVG matching the given parameters, output compatible with the\n     * src attribute of <img/>.\n     *\n     * @private\n     * @param {string} font: base64 encoded font to use\n     * @param {string} text: the name to draw\n     * @param {number} width: the width of the resulting image in px\n     * @param {number} height: the height of the resulting image in px\n     * @returns {string} image = mimetype + image data\n     */\n    getSVGText(font, text, width, height) {\n        const svg = renderToString(\"web.sign_svg_text\", {\n            width: width,\n            height: height,\n            font: font,\n            text: text,\n            type: this.props.signatureType,\n            color: this.props.fontColor,\n        });\n\n        return \"data:image/svg+xml,\" + encodeURI(svg);\n    }\n\n    getSVGTextFont(font) {\n        const height = 100;\n        const width = parseInt(height * this.props.displaySignatureRatio);\n        return this.getSVGText(font, this.getCleanedName(), width, height);\n    }\n\n    uploadFile() {\n        this.signInputLoad.el?.click();\n    }\n\n    /**\n     * Handles change on load file input: displays the loaded image if the\n     * format is correct, or displays an error otherwise.\n     *\n     * @see mode 'load'\n     * @private\n     * @param {Event} ev\n     * @return bool|undefined\n     */\n    async onChangeSignLoadInput(ev) {\n        var file = ev.target.files[0];\n        if (file === undefined) {\n            return false;\n        }\n        if (file.type.substr(0, 5) !== \"image\") {\n            this.clear();\n            this.state.loadIsInvalid = true;\n            return false;\n        }\n        this.state.loadIsInvalid = false;\n\n        const result = await getDataURLFromFile(file);\n        await this.printImage(result);\n    }\n\n    onClickSignAutoSelectStyle() {\n        this.state.showFontList = true;\n    }\n\n    onClickSignDrawClear() {\n        this.clear();\n        this.props.onSignatureChange(this.state.signMode);\n    }\n\n    onClickSignLoad() {\n        this.setMode(\"load\");\n    }\n\n    onClickSignAuto() {\n        this.setMode(\"auto\");\n    }\n\n    onInputSignName(ev) {\n        this.props.signature.name = ev.target.value;\n        if (!this.state.showSignatureArea && this.getCleanedName()) {\n            this.state.showSignatureArea = true;\n            return;\n        }\n        if (this.state.signMode === \"auto\") {\n            this.drawCurrentName();\n        }\n    }\n\n    onSelectFont(index) {\n        this.currentFont = index;\n        this.drawCurrentName();\n    }\n\n    /**\n     * Displays the given image in the signature field.\n     * If needed, resizes the image to fit the existing area.\n     *\n     * @param {string} imgSrc - data of the image to display\n     */\n    async printImage(imgSrc) {\n        this.clear();\n        const c = this.signaturePad.canvas;\n        const img = new Image();\n        img.onload = () => {\n            const ctx = c.getContext(\"2d\");\n            var ratio = ((img.width / img.height) > (c.width / c.height)) ? c.width / img.width : c.height / img.height;\n            ctx.drawImage( \n                img,\n                (c.width / 2) - (img.width * ratio / 2),\n                (c.height / 2) - (img.height * ratio / 2)\n                , img.width * ratio\n                , img.height * ratio\n            );\n            this.props.signature.isSignatureEmpty = this.isSignatureEmpty;\n            this.props.onSignatureChange(this.state.signMode);\n        };\n        img.src = imgSrc;\n        this.signaturePad._isEmpty = false;\n    }\n\n    /**\n     * (Re)initializes the signature area:\n     *  - set the correct width and height of the drawing based on the width\n     *      of the container and the ratio option\n     *  - empty any previous content\n     *  - correctly reset the empty state\n     *  - call @see setMode with reset\n     */\n    resetSignature() {\n        this.resizeSignature();\n        this.clear();\n        this.setMode(this.state.signMode, true);\n        this.focusName();\n    }\n\n    resizeSignature() {\n        // recompute size based on the current width\n        const width = this.signatureRef.el.clientWidth;\n        const height = parseInt(width / this.props.displaySignatureRatio);\n\n        Object.assign(this.signatureRef.el, { width, height });\n    }\n\n    /**\n     * Changes the signature mode. Toggles the display of the relevant\n     * controls and resets the drawing.\n     *\n     * @param {string} mode - the mode to use. Can be one of the following:\n     *  - 'draw': the user draws the signature manually with the mouse\n     *  - 'auto': the signature is drawn automatically using a selected font\n     *  - 'load': the signature is loaded from an image file\n     * @param {boolean} [reset=false] - Set to true to reset the elements\n     *  even if the @see mode has not changed. By default nothing happens\n     *  if the @see mode is already selected.\n     */\n    setMode(mode, reset) {\n        if (reset !== true && mode === this.signMode) {\n            // prevent flickering and unnecessary compute\n            return;\n        }\n\n        this.state.signMode = mode;\n        this.signaturePad[this.state.signMode === \"draw\" ? \"on\" : \"off\"]();\n        this.clear();\n\n        if (this.state.signMode === \"auto\") {\n            // draw based on name\n            this.drawCurrentName();\n        }\n        this.props.onSignatureChange(this.state.signMode);\n    }\n\n    /**\n     * Returns whether the drawing area is currently empty.\n     *\n     * @returns {boolean} Whether the drawing area is currently empty.\n     */\n    get isSignatureEmpty() {\n        return this.signaturePad.isEmpty();\n    }\n\n    get loadIsInvalid() {\n        return this.state.signMode === \"load\" && this.state.loadIsInvalid;\n    }\n}\n", "import { Dialog } from \"@web/core/dialog/dialog\";\nimport { NameAndSignature } from \"./name_and_signature\";\n\nimport { Component, useState } from \"@odoo/owl\";\n\nexport class SignatureDialog extends Component {\n    static template = \"web.SignatureDialog\";\n    static components = { Dialog, NameAndSignature };\n    static props = {\n        defaultName: { type: String, optional: true },\n        nameAndSignatureProps: Object,\n        uploadSignature: Function,\n        close: Function,\n    };\n    static defaultProps = {\n        defaultName: \"\",\n    };\n\n    setup() {\n        this.signature = useState({\n            name: this.props.defaultName,\n            isSignatureEmpty: true,\n        });\n    }\n\n    /**\n     * Upload the signature image when confirm.\n     *\n     * @private\n     */\n    onClickConfirm() {\n        this.props.uploadSignature({\n            name: this.signature.name,\n            signatureImage: this.signature.getSignatureImage(),\n        });\n        this.props.close();\n    }\n\n    get nameAndSignatureProps() {\n        return {\n            ...this.props.nameAndSignatureProps,\n            signature: this.signature,\n        };\n    }\n}\n", "import { Component } from \"@odoo/owl\";\n\nexport class TagsList extends Component {\n    static template = \"web.TagsList\";\n    static defaultProps = {\n        displayText: true,\n    };\n    static props = {\n        displayText: { type: Boolean, optional: true },\n        visibleItemsLimit: { type: Number, optional: true },\n        tags: { type: Object },\n    };\n    get visibleTagsCount() {\n        return this.props.visibleItemsLimit - 1;\n    }\n    get visibleTags() {\n        if (this.props.visibleItemsLimit && this.props.tags.length > this.props.visibleItemsLimit) {\n            return this.props.tags.slice(0, this.visibleTagsCount);\n        }\n        return this.props.tags;\n    }\n    get otherTags() {\n        if (\n            !this.props.visibleItemsLimit ||\n            this.props.tags.length <= this.props.visibleItemsLimit\n        ) {\n            return [];\n        }\n        return this.props.tags.slice(this.visibleTagsCount);\n    }\n    get tooltipInfo() {\n        return JSON.stringify({\n            tags: this.otherTags.map((tag) => ({\n                text: tag.text,\n                id: tag.id,\n            })),\n        });\n    }\n}\n", "const RSTRIP_REGEXP = /(?=\\n[ \\t]*$)/;\n/**\n * The child nodes of operation represent new content to create before target or\n * or other elements to move before target from the target tree (tree from which target is part of).\n * Some processing of text nodes has to be done in order to normalize the situation.\n * Note: we assume that target has a parent element.\n * @param {Element} target\n * @param {Element} operation\n */\nfunction addBefore(target, operation) {\n    const nodes = getNodes(target, operation);\n    if (nodes.length === 0) {\n        return;\n    }\n    const { previousSibling } = target;\n    target.before(...nodes);\n    if (previousSibling?.nodeType === Node.TEXT_NODE) {\n        const [text1, text2] = previousSibling.data.split(RSTRIP_REGEXP);\n        previousSibling.data = text1.trimEnd();\n        if (nodes[0].nodeType === Node.TEXT_NODE) {\n            mergeTextNodes(previousSibling, nodes[0]);\n        }\n        if (text2 && nodes.some((n) => n.nodeType !== Node.TEXT_NODE)) {\n            const textNode = document.createTextNode(text2);\n            target.before(textNode);\n            if (textNode.previousSibling.nodeType === Node.TEXT_NODE) {\n                mergeTextNodes(textNode.previousSibling, textNode);\n            }\n        }\n    }\n}\n\n/**\n * element is part of a tree. Here we return the root element of that tree.\n * Note: this root element is not necessarily the documentElement of the ownerDocument\n * of element (hence the following code).\n * @param {Element} element\n * @returns {Element}\n */\nfunction getRoot(element) {\n    while (element.parentElement) {\n        element = element.parentElement;\n    }\n    return element;\n}\n\nconst HASCLASS_REGEXP = /hasclass\\(([^)]*)\\)/g;\n/**\n * @param {Element} operation\n * @returns {string}\n */\nfunction getXpath(operation) {\n    const xpath = operation.getAttribute(\"expr\");\n    // hasclass does not exist in XPath 1.0 but is a custom function defined server side (see _hasclass) usable in lxml.\n    // Here we have to replace it by a complex condition (which is not nice).\n    // Note: we assume that classes do not contain the 2 chars , and )\n    return xpath.replaceAll(HASCLASS_REGEXP, (_, capturedGroup) => {\n        return capturedGroup\n            .split(\",\")\n            .map((c) => `contains(concat(' ', @class, ' '), ' ${c.trim().slice(1, -1)} ')`)\n            .join(\" and \");\n    });\n}\n\n/**\n * @param {Element} element\n * @param {Element} operation\n * @returns {Node|null}\n */\nfunction getNode(element, operation) {\n    const root = getRoot(element);\n    const doc = new Document();\n    doc.appendChild(root); // => root is the documentElement of its ownerDocument (we do that in case root is a clone)\n    if (operation.tagName === \"xpath\") {\n        const xpath = getXpath(operation);\n        const result = doc.evaluate(xpath, root, null, XPathResult.FIRST_ORDERED_NODE_TYPE);\n        return result.singleNodeValue;\n    }\n    for (const elem of root.querySelectorAll(operation.tagName)) {\n        if (\n            [...operation.attributes].every(\n                ({ name, value }) => name === \"position\" || elem.getAttribute(name) === value\n            )\n        ) {\n            return elem;\n        }\n    }\n    return null;\n}\n\n/**\n * @param {Element} element\n * @param {Element} operation\n * @returns {Element}\n */\nfunction getElement(element, operation) {\n    const node = getNode(element, operation);\n    if (!node) {\n        throw new Error(`Element '${operation.outerHTML}' cannot be located in element tree`);\n    }\n    if (!(node instanceof Element)) {\n        throw new Error(`Found node ${node} instead of an element`);\n    }\n    return node;\n}\n\n/**\n * @param {Element} element\n * @param {Element} operation\n * @returns {Node[]}\n */\nfunction getNodes(element, operation) {\n    const nodes = [];\n    for (const childNode of operation.childNodes) {\n        if (childNode.tagName === \"xpath\" && childNode.getAttribute?.(\"position\") === \"move\") {\n            const node = getElement(element, childNode);\n            removeNode(node);\n            nodes.push(node);\n        } else {\n            nodes.push(childNode);\n        }\n    }\n    return nodes;\n}\n\n/**\n * @param {Text} first\n * @param {Text} second\n * @param {boolean} [trimEnd=true]\n */\nfunction mergeTextNodes(first, second, trimEnd = true) {\n    first.data = (trimEnd ? first.data.trimEnd() : first.data) + second.data;\n    second.remove();\n}\n\nfunction splitAndTrim(str, separator) {\n    return str.split(separator).map((s) => s.trim());\n}\n\n/**\n * @param {Element} target\n * @param {Element} operation\n */\nfunction modifyAttributes(target, operation) {\n    for (const child of operation.children) {\n        if (child.tagName !== \"attribute\") {\n            continue;\n        }\n        const attributeName = child.getAttribute(\"name\");\n        const firstNode = child.childNodes[0];\n        let value = firstNode?.nodeType === Node.TEXT_NODE ? firstNode.data : \"\";\n\n        const add = child.getAttribute(\"add\") || \"\";\n        const remove = child.getAttribute(\"remove\") || \"\";\n        if (add || remove) {\n            if (firstNode?.nodeType === Node.TEXT_NODE) {\n                throw new Error(`Useless element content ${firstNode.outerHTML}`);\n            }\n            const separator = child.getAttribute(\"separator\") || \",\";\n            const toRemove = new Set(splitAndTrim(remove, separator));\n            const values = splitAndTrim(target.getAttribute(attributeName) || \"\", separator).filter(\n                (s) => !toRemove.has(s)\n            );\n            values.push(...splitAndTrim(add, separator).filter((s) => s));\n            value = values.join(separator);\n        }\n\n        if (value) {\n            target.setAttribute(attributeName, value);\n        } else {\n            target.removeAttribute(attributeName);\n        }\n    }\n}\n\n/**\n * Remove node and normalize surrounind text nodes (if any)\n * Note: we assume that node has a parent element\n * @param {Node} node\n */\nfunction removeNode(node) {\n    const { nextSibling, previousSibling } = node;\n    node.remove();\n    if (nextSibling?.nodeType === Node.TEXT_NODE && previousSibling?.nodeType === Node.TEXT_NODE) {\n        mergeTextNodes(\n            previousSibling,\n            nextSibling,\n            previousSibling.parentElement.firstChild === previousSibling\n        );\n    }\n}\n\n/**\n * @param {Element} root\n * @param {Element} target\n * @param {Element} operation\n */\nfunction replace(root, target, operation) {\n    const mode = operation.getAttribute(\"mode\") || \"outer\";\n    switch (mode) {\n        case \"outer\": {\n            const result = operation.ownerDocument.evaluate(\n                \".//*[text()='$0']\",\n                operation,\n                null,\n                XPathResult.ORDERED_NODE_SNAPSHOT_TYPE\n            );\n            for (let i = 0; i < result.snapshotLength; i++) {\n                const loc = result.snapshotItem(i);\n                loc.firstChild.replaceWith(target.cloneNode(true));\n            }\n            if (target.parentElement) {\n                const nodes = getNodes(target, operation);\n                target.replaceWith(...nodes);\n            } else {\n                let operationContent = null;\n                let comment = null;\n                for (const child of operation.childNodes) {\n                    if (child.nodeType === Node.ELEMENT_NODE) {\n                        operationContent = child;\n                        break;\n                    }\n                    if (child.nodeType === Node.COMMENT_NODE) {\n                        comment = child;\n                    }\n                }\n                root = operationContent.cloneNode(true);\n                if (target.hasAttribute(\"t-name\")) {\n                    root.setAttribute(\"t-name\", target.getAttribute(\"t-name\"));\n                }\n                if (comment) {\n                    root.prepend(comment);\n                }\n            }\n            break;\n        }\n        case \"inner\":\n            while (target.firstChild) {\n                target.removeChild(target.lastChild);\n            }\n            target.append(...operation.childNodes);\n            break;\n        default:\n            throw new Error(`Invalid mode attribute: '${mode}'`);\n    }\n    return root;\n}\n\n/**\n * @param {Element} root\n * @param {Element} operations is a single element whose children represent operations to perform on root\n * @param {string} [url=\"\"]\n * @returns {Element} root modified (in place) by the operations\n */\nexport function applyInheritance(root, operations, url = \"\") {\n    for (const operation of operations.children) {\n        const target = getElement(root, operation);\n        const position = operation.getAttribute(\"position\") || \"inside\";\n\n        if (odoo.debug && url) {\n            const attributes = [...operation.attributes].map(\n                ({ name, value }) =>\n                    `${name}=${JSON.stringify(name === \"position\" ? position : value)}`\n            );\n            const comment = document.createComment(\n                ` From file: ${url} ; ${attributes.join(\" ; \")} `\n            );\n            if (position === \"attributes\") {\n                target.before(comment); // comment won't be visible if target is root\n            } else {\n                operation.prepend(comment);\n            }\n        }\n\n        switch (position) {\n            case \"replace\": {\n                root = replace(root, target, operation); // root can be replaced (see outer mode)\n                break;\n            }\n            case \"attributes\": {\n                modifyAttributes(target, operation);\n                break;\n            }\n            case \"inside\": {\n                const sentinel = document.createElement(\"sentinel\");\n                target.append(sentinel);\n                addBefore(sentinel, operation);\n                removeNode(sentinel);\n                break;\n            }\n            case \"after\": {\n                const sentinel = document.createElement(\"sentinel\");\n                target.after(sentinel);\n                addBefore(sentinel, operation);\n                removeNode(sentinel);\n                break;\n            }\n            case \"before\": {\n                addBefore(target, operation);\n                break;\n            }\n            default:\n                throw new Error(`Invalid position attribute: '${position}'`);\n        }\n    }\n    return root;\n}\n", "import { applyInheritance } from \"@web/core/template_inheritance\";\n\nconst parser = new DOMParser();\n/** @type {((document: Document) => void)[]} */\nconst templateProcessors = [];\n/** @type {((url: string) => boolean)[]} */\nlet urlFilters = [];\nfunction getParsedTemplate(templateString) {\n    const doc = parser.parseFromString(templateString, \"text/xml\");\n    for (const processor of templateProcessors) {\n        processor(doc);\n    }\n    return doc.firstChild;\n}\n\nfunction getClone(template) {\n    const c = template.cloneNode(true);\n    new Document().append(c); // => c is the documentElement of its ownerDocument\n    return c;\n}\n\nconst registered = new Set();\nfunction isRegistered(...args) {\n    const key = JSON.stringify([...args]);\n    if (registered.has(key)) {\n        return true;\n    }\n    registered.add(key);\n    return false;\n}\n\nlet blockType = null;\nlet blockId = 0;\n\nconst templates = {};\nconst parsedTemplates = {};\nconst info = {};\nexport function registerTemplate(name, url, templateString) {\n    if (isRegistered(...arguments)) {\n        return;\n    }\n    if (blockType !== \"templates\") {\n        blockType = \"templates\";\n        blockId++;\n    }\n    if (name in templates && (info[name].url !== url || templates[name] !== templateString)) {\n        throw new Error(`Template ${name} already exists`);\n    }\n    templates[name] = templateString;\n    info[name] = { blockId, url };\n}\n\nconst templateExtensions = {};\nconst parsedTemplateExtensions = {};\nexport function registerTemplateExtension(inheritFrom, url, templateString) {\n    if (isRegistered(...arguments)) {\n        return;\n    }\n    if (blockType !== \"extensions\") {\n        blockType = \"extensions\";\n        blockId++;\n    }\n    if (!templateExtensions[inheritFrom]) {\n        templateExtensions[inheritFrom] = [];\n    }\n    if (!templateExtensions[inheritFrom][blockId]) {\n        templateExtensions[inheritFrom][blockId] = [];\n    }\n    templateExtensions[inheritFrom][blockId].push({\n        templateString,\n        url,\n    });\n}\n\n/**\n * @param {(document: Document) => void} processor\n */\nexport function registerTemplateProcessor(processor) {\n    templateProcessors.push(processor);\n}\n\n/**\n * @param {typeof urlFilters} filters\n */\nexport function setUrlFilters(filters) {\n    urlFilters = filters;\n}\n\nfunction _getTemplate(name, blockId = null) {\n    if (!(name in parsedTemplates)) {\n        if (!(name in templates)) {\n            return null;\n        }\n        const templateString = templates[name];\n        parsedTemplates[name] = getParsedTemplate(templateString);\n    }\n    let processedTemplate = parsedTemplates[name];\n\n    const inheritFrom = processedTemplate.getAttribute(\"t-inherit\");\n    if (inheritFrom) {\n        const parentTemplate = _getTemplate(inheritFrom, blockId || info[name].blockId);\n        if (!parentTemplate) {\n            throw new Error(\n                `Constructing template ${name}: template parent ${inheritFrom} not found`\n            );\n        }\n        const element = getClone(processedTemplate);\n        processedTemplate = applyInheritance(getClone(parentTemplate), element, info[name].url);\n        if (processedTemplate.tagName !== element.tagName) {\n            const temp = processedTemplate;\n            processedTemplate = new Document().createElement(element.tagName);\n            processedTemplate.append(...temp.childNodes);\n        }\n        for (const { name, value } of element.attributes) {\n            if (![\"t-inherit\", \"t-inherit-mode\"].includes(name)) {\n                processedTemplate.setAttribute(name, value);\n            }\n        }\n    }\n\n    for (const otherBlockId in templateExtensions[name] || {}) {\n        if (blockId && otherBlockId > blockId) {\n            break;\n        }\n        if (!(name in parsedTemplateExtensions)) {\n            parsedTemplateExtensions[name] = {};\n        }\n        if (!(otherBlockId in parsedTemplateExtensions[name])) {\n            parsedTemplateExtensions[name][otherBlockId] = [];\n            for (const { templateString, url } of templateExtensions[name][otherBlockId]) {\n                parsedTemplateExtensions[name][otherBlockId].push({\n                    template: getParsedTemplate(templateString),\n                    url,\n                });\n            }\n        }\n        for (const { template, url } of parsedTemplateExtensions[name][otherBlockId]) {\n            if (!urlFilters.every((filter) => filter(url))) {\n                continue;\n            }\n            processedTemplate = applyInheritance(\n                inheritFrom ? processedTemplate : getClone(processedTemplate),\n                getClone(template),\n                url\n            );\n        }\n    }\n\n    return processedTemplate;\n}\n\n/** @type {Record<string, Element>} */\nlet processedTemplates = {};\n\n/**\n * @param {string} name\n */\nexport function getTemplate(name) {\n    if (!processedTemplates[name]) {\n        processedTemplates[name] = _getTemplate(name);\n    }\n    return processedTemplates[name];\n}\n\nexport function clearProcessedTemplates() {\n    processedTemplates = {};\n}\n\nexport function checkPrimaryTemplateParents(namesToCheck) {\n    const missing = new Set(namesToCheck.filter((name) => !(name in templates)));\n    if (missing.size) {\n        console.error(`Missing (primary) parent templates: ${[...missing].join(\", \")}`);\n    }\n}\n", "import { Component } from \"@odoo/owl\";\n\nexport class Tooltip extends Component {\n    static template = \"web.Tooltip\";\n    static props = {\n        close: Function,\n        tooltip: { type: String, optional: true },\n        template: { type: String, optional: true },\n        info: { optional: true },\n    };\n}\n", "import { useService } from \"@web/core/utils/hooks\";\n\nimport { useEffect, useRef } from \"@odoo/owl\";\n\nexport function useTooltip(refName, params) {\n    const tooltip = useService(\"tooltip\");\n    const ref = useRef(refName);\n    useEffect(\n        (el) => tooltip.add(el, params),\n        () => [ref.el]\n    );\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\nimport { Tooltip } from \"./tooltip\";\nimport { hasTouch } from \"@web/core/browser/feature_detection\";\n\nimport { whenReady } from \"@odoo/owl\";\n\n/**\n * The tooltip service allows to display custom tooltips on every elements with\n * a \"data-tooltip\" attribute. This attribute can be set on elements for which\n * we prefer a custom tooltip instead of the native one displaying the value of\n * the \"title\" attribute.\n *\n * Usage:\n *   <button data-tooltip=\"This is a tooltip\">Do something</button>\n *\n * The ideal position of the tooltip can be specified thanks to the attribute\n * \"data-tooltip-position\":\n *   <button data-tooltip=\"This is a tooltip\" data-tooltip-position=\"left\">Do something</button>\n *\n * The opening delay can be modified with the \"data-tooltip-delay\" attribute (default: 400):\n *   <button data-tooltip=\"This is a tooltip\" data-tooltip-delay=\"0\">Do something</button>\n *\n * The default behaviour on touch devices to open the tooltip can be modified from \"hold-to-show\"\n * to \"tap-to-show\" \"with the data-tooltip-touch-tap-to-show\" attribute:\n *  <button data-tooltip=\"This is a tooltip\" data-tooltip-touch-tap-to-show=\"true\">Do something</button>\n *\n * For advanced tooltips containing dynamic and/or html content, the\n * \"data-tooltip-template\" and \"data-tooltip-info\" attributes can be used.\n * For example, let's suppose the following qweb template:\n *   <t t-name=\"some_template\">\n *     <ul>\n *       <li>info.x</li>\n *       <li>info.y</li>\n *     </ul>\n *   </t>\n * This template can then be used in a tooltip as follows:\n *   <button data-tooltip-template=\"some_template\" data-tooltip-info=\"info\">Do something</button>\n * with \"info\" being a stringified object with two keys \"x\" and \"y\".\n */\n\nconst OPEN_DELAY = 400;\nconst CLOSE_DELAY = 200;\n\nexport const tooltipService = {\n    dependencies: [\"popover\"],\n    start(env, { popover }) {\n        let openTooltipTimeout;\n        let closeTooltip;\n        let target = null;\n        let touchPressed;\n        let mouseEntered;\n        const elementsWithTooltips = new WeakMap();\n\n        /**\n         * Closes the currently opened tooltip if any, or prevent it from opening.\n         */\n        function cleanup() {\n            target = null;\n            browser.clearTimeout(openTooltipTimeout);\n            openTooltipTimeout = null;\n            if (closeTooltip) {\n                closeTooltip();\n                closeTooltip = null;\n            }\n        }\n\n        /**\n         * Checks that the target is in the DOM and we're hovering the target.\n         * @returns {boolean}\n         */\n        function shouldCleanup() {\n            if (!target) {\n                return false;\n            }\n            if (!document.body.contains(target)) {\n                return true; // target is no longer in the DOM\n            }\n            if (hasTouch() && !mouseEntered) {\n                return !touchPressed;\n            }\n            return false;\n        }\n\n        /**\n         * Checks whether there is a tooltip registered on the event target, and\n         * if there is, creates a timeout to open the corresponding tooltip\n         * after a delay.\n         *\n         * @param {HTMLElement} el the element on which to add the tooltip\n         * @param {object} param1\n         * @param {string} [param1.tooltip] the string to add as a tooltip, if\n         *  no tooltip template is specified\n         * @param {string} [param1.template] the name of the template to use for\n         *  tooltip, if any\n         * @param {object} [param1.info] info for the tooltip template\n         * @param {'top'|'bottom'|'left'|'right'} param1.position\n         * @param {number} [param1.delay] delay after which the popover should\n         *  open\n         */\n        function openTooltip(el, { tooltip = \"\", template, info, position, delay = OPEN_DELAY }) {\n            cleanup();\n            if (!tooltip && !template) {\n                return;\n            }\n\n            target = el;\n            openTooltipTimeout = browser.setTimeout(() => {\n                // verify that the element is still in the DOM\n                if (target.isConnected) {\n                    closeTooltip = popover.add(\n                        target,\n                        Tooltip,\n                        { tooltip, template, info },\n                        { position }\n                    );\n                    // Prevent title from showing on a parent at the same time\n                    target.title = \"\";\n                }\n            }, delay);\n        }\n\n        /**\n         * Checks whether there is a tooltip registered on the element, and\n         * if there is, creates a timeout to open the corresponding tooltip\n         * after a delay.\n         *\n         * @param {HTMLElement} el\n         */\n        function openElementsTooltip(el) {\n            // Fix weird behavior in Firefox where MouseEvent can be dispatched\n            // from TEXT_NODE, even if they shouldn't...\n            if (el.nodeType === Node.TEXT_NODE) {\n                return;\n            }\n            if (elementsWithTooltips.has(el)) {\n                openTooltip(el, elementsWithTooltips.get(el));\n            } else if (el.matches(\"[data-tooltip], [data-tooltip-template]\")) {\n                const dataset = el.dataset;\n                const params = {\n                    tooltip: dataset.tooltip,\n                    template: dataset.tooltipTemplate,\n                    position: dataset.tooltipPosition,\n                };\n                if (dataset.tooltipInfo) {\n                    params.info = JSON.parse(dataset.tooltipInfo);\n                }\n                if (dataset.tooltipDelay) {\n                    params.delay = parseInt(dataset.tooltipDelay, 10);\n                }\n                openTooltip(el, params);\n            }\n        }\n\n        /**\n         * Checks whether there is a tooltip registered on the event target, and\n         * if there is, creates a timeout to open the corresponding tooltip\n         * after a delay.\n         *\n         * @param {MouseEvent} ev a \"mouseenter\" event\n         */\n        function onMouseenter(ev) {\n            mouseEntered = true;\n            openElementsTooltip(ev.target);\n        }\n\n        function onMouseleave(ev) {\n            if (target === ev.target) {\n                mouseEntered = false;\n                cleanup();\n            }\n        }\n        /**\n         * Checks whether there is a tooltip registered on the event target, and\n         * if there is, creates a timeout to open the corresponding tooltip\n         * after a delay.\n         *\n         * @param {TouchEvent} ev a \"touchstart\" event\n         */\n        function onTouchStart(ev) {\n            touchPressed = true;\n            openElementsTooltip(ev.target);\n        }\n\n        whenReady(() => {\n            // Regularly check that the target is still in the DOM and if not, close the tooltip\n            browser.setInterval(() => {\n                if (shouldCleanup()) {\n                    cleanup();\n                }\n            }, CLOSE_DELAY);\n\n            if (hasTouch()) {\n                document.body.addEventListener(\"touchstart\", onTouchStart);\n\n                document.body.addEventListener(\"touchend\", (ev) => {\n                    if (ev.target.matches(\"[data-tooltip], [data-tooltip-template]\")) {\n                        if (!ev.target.dataset.tooltipTouchTapToShow) {\n                            touchPressed = false;\n                        }\n                    }\n                });\n\n                document.body.addEventListener(\"touchcancel\", (ev) => {\n                    if (ev.target.matches(\"[data-tooltip], [data-tooltip-template]\")) {\n                        if (!ev.target.dataset.tooltipTouchTapToShow) {\n                            touchPressed = false;\n                        }\n                    }\n                });\n            }\n\n            // Listen (using event delegation) to \"mouseenter\" events to open the tooltip if any\n            document.body.addEventListener(\"mouseenter\", onMouseenter, { capture: true });\n            // Listen (using event delegation) to \"mouseleave\" events to close the tooltip if any\n            document.body.addEventListener(\"mouseleave\", onMouseleave, { capture: true });\n        });\n\n        return {\n            add(el, params) {\n                elementsWithTooltips.set(el, params);\n                return () => {\n                    elementsWithTooltips.delete(el);\n                    if (target === el) {\n                        cleanup();\n                    }\n                };\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"tooltip\", tooltipService);\n", "import { browser } from \"./browser/browser\";\n\nimport {\n    Component,\n    onWillUpdateProps,\n    status,\n    useComponent,\n    useEffect,\n    useState,\n    xml,\n} from \"@odoo/owl\";\n\n// Allows to disable transitions globally, useful for testing (and maybe for\n// a reduced motion setting in the future?)\nexport const config = {\n    disabled: false,\n};\n/**\n * Creates a transition to be used within the current component. Usage:\n *  --- in JS:\n *  this.transition = useTransition({ name: \"myClass\" });\n *  --- in XML:\n *  <div t-if=\"transition.shouldMount\" t-att-class=\"transition.class\"/>\n *\n * @param {Object} options\n * @param {string} options.name the prefix to use for the transition classes\n * @param {boolean} [options.initialVisibility=true] whether to start the\n *  transition in the on or off state\n * @param {number} [options.immediate=false] (only relevant when initialVisibility\n *  is true) set to true to animate initially. By default, there's no animation\n *  if the element is initially visible.\n * @param {number} [options.leaveDuration] the leaveDuration of the transition\n * @param {Function} [options.onLeave] a function that will be called when the\n *  element will be removed in the next render cycle\n * @returns {{ shouldMount, class }} an object containing two fields that\n *  indicate whether an element on which the transition is applied should be\n *  mounted and the class string that should be put on it\n */\nexport function useTransition({\n    name,\n    initialVisibility = true,\n    immediate = false,\n    leaveDuration = 500,\n    onLeave = () => {},\n}) {\n    const component = useComponent();\n    const state = useState({\n        shouldMount: initialVisibility,\n        stage: initialVisibility ? \"enter\" : \"leave\",\n    });\n\n    if (config.disabled) {\n        return {\n            get shouldMount() {\n                return state.shouldMount;\n            },\n            set shouldMount(val) {\n                state.shouldMount = val;\n            },\n            get className() {\n                return `${name} ${name}-enter-active`;\n            },\n            get stage() {\n                return \"enter-active\";\n            },\n        };\n    }\n    // We need to allow the element to be mounted in the enter state so that it\n    // will get the transition when we activate the enter-active class. This\n    // onNextPatch allows us to activate the class that we want the next time\n    // the component is patched.\n    let onNextPatch = null;\n    useEffect(() => {\n        if (onNextPatch) {\n            onNextPatch();\n            onNextPatch = null;\n        }\n    });\n\n    let prevState, timer;\n    const transition = {\n        get shouldMount() {\n            return state.shouldMount;\n        },\n        set shouldMount(newState) {\n            if (newState === prevState) {\n                return;\n            }\n            browser.clearTimeout(timer);\n            prevState = newState;\n            // when true - transition from enter to enter-active\n            // when false - transition from enter-active to leave, unmount after leaveDuration\n            if (newState) {\n                if (status(component) === \"mounted\" || immediate) {\n                    state.stage = \"enter\";\n                    // force a render here so that we get a patch even if the state didn't change\n                    component.render();\n                    onNextPatch = () => {\n                        state.stage = \"enter-active\";\n                    };\n                } else {\n                    state.stage = \"enter-active\";\n                }\n                state.shouldMount = true;\n            } else {\n                state.stage = \"leave\";\n                timer = browser.setTimeout(() => {\n                    state.shouldMount = false;\n                    onLeave();\n                }, leaveDuration);\n            }\n        },\n        get className() {\n            return `${name} ${name}-${state.stage}`;\n        },\n        get stage() {\n            return state.stage;\n        },\n    };\n    transition.shouldMount = initialVisibility;\n    return transition;\n}\n\n/**\n * A higher order component that handles a transition to be used within its\n * default slot. Generally, the useTransition hook is simpler to use, but the\n * HOC has the advantage that it can be spawned as needed during the render (eg:\n * in a t-foreach loop) without knowing at setup-time how many transitions need\n * to be created. @see useTransition\n */\nexport class Transition extends Component {\n    static template = xml`<t t-slot=\"default\" t-if=\"transition.shouldMount\" className=\"transition.className\"/>`;\n    static props = {\n        name: String,\n        visible: { type: Boolean, optional: true },\n        immediate: { type: Boolean, optional: true },\n        leaveDuration: { type: Number, optional: true },\n        onLeave: { type: Function, optional: true },\n        slots: Object,\n    };\n\n    setup() {\n        const { immediate, visible, leaveDuration, name, onLeave } = this.props;\n        this.transition = useTransition({\n            initialVisibility: visible,\n            immediate,\n            leaveDuration,\n            name,\n            onLeave,\n        });\n        onWillUpdateProps(({ visible = true }) => {\n            this.transition.shouldMount = visible;\n        });\n    }\n}\n", "import { Domain } from \"@web/core/domain\";\nimport { formatAST, parseExpr } from \"@web/core/py_js/py\";\nimport { toPyValue } from \"@web/core/py_js/py_utils\";\nimport { deepCopy, deepEqual } from \"../utils/objects\";\n\n/** @typedef { import(\"@web/core/py_js/py_parser\").AST } AST */\n/** @typedef {import(\"@web/core/domain\").DomainRepr} DomainRepr */\n\n/**\n * @typedef {number|string|boolean|Expression} Atom\n */\n\n/**\n * @typedef {Atom|Atom[]} Value\n */\n\n/**\n * @typedef {Object} Condition\n * @property {\"condition\"} type\n * @property {Value} path\n * @property {Value} operator\n * @property {Value} value\n * @property {boolean} negate\n */\n\n/**\n * @typedef {Object} ComplexCondition\n * @property {\"complex_condition\"} type\n * @property {string} value expression\n */\n\n/**\n * @typedef {Object} Connector\n * @property {\"connector\"} type\n * @property {boolean} negate\n * @property {\"|\"|\"&\"} value\n * @property {Tree[]} children\n */\n\n/**\n * @typedef {Connector|Condition|ComplexCondition} Tree\n */\n\n/**\n * @typedef {Object} Options\n * @property {(value: Value) => (null|Object)} [getFieldDef]\n * @property {boolean} [distributeNot]\n */\n\nexport const TERM_OPERATORS_NEGATION = {\n    \"<\": \">=\",\n    \">\": \"<=\",\n    \"<=\": \">\",\n    \">=\": \"<\",\n    \"=\": \"!=\",\n    \"!=\": \"=\",\n    in: \"not in\",\n    like: \"not like\",\n    ilike: \"not ilike\",\n    \"not in\": \"in\",\n    \"not like\": \"like\",\n    \"not ilike\": \"ilike\",\n};\n\nconst TERM_OPERATORS_NEGATION_EXTENDED = {\n    ...TERM_OPERATORS_NEGATION,\n    is: \"is not\",\n    \"is not\": \"is\",\n    \"==\": \"!=\",\n    \"!=\": \"==\", // override here\n};\n\nconst EXCHANGE = {\n    \"<\": \">\",\n    \"<=\": \">=\",\n    \">\": \"<\",\n    \">=\": \"<=\",\n    \"=\": \"=\",\n    \"!=\": \"!=\",\n};\n\nconst COMPARATORS = [\"<\", \"<=\", \">\", \">=\", \"in\", \"not in\", \"==\", \"is\", \"!=\", \"is not\"];\n\nconst DATETIME_TODAY_STRING_EXPRESSION = `datetime.datetime.combine(context_today(), datetime.time(0, 0, 0)).to_utc().strftime(\"%Y-%m-%d %H:%M:%S\")`;\nconst DATE_TODAY_STRING_EXPRESSION = `context_today().strftime(\"%Y-%m-%d\")`;\nconst DELTA_DATE_AST = parseExpr(\n    `(context_today() + relativedelta(period=amount)).strftime('%Y-%m-%d')`\n);\nconst DELTA_DATETIME_AST = parseExpr(\n    `datetime.datetime.combine(context_today() + relativedelta(period=amount), datetime.time(0, 0, 0)).to_utc().strftime(\"%Y-%m-%d %H:%M:%S\")`\n);\n\nfunction replaceKwargs(ast, fieldType, kwargs = {}) {\n    const astCopy = deepCopy(ast);\n    if (fieldType === \"date\") {\n        astCopy.fn.obj.right.kwargs = kwargs;\n    } else {\n        astCopy.fn.obj.fn.obj.args[0].right.kwargs = kwargs;\n    }\n    return astCopy;\n}\n\nfunction getDelta(ast, fieldType) {\n    const kwargs =\n        (fieldType === \"date\"\n            ? ast.fn?.obj?.right?.kwargs\n            : ast.fn?.obj?.fn?.obj?.args?.[0]?.right?.kwargs) || {};\n    if (Object.keys(kwargs).length !== 1) {\n        return null;\n    }\n    if (\n        !deepEqual(\n            replaceKwargs(ast, fieldType),\n            replaceKwargs(fieldType === \"date\" ? DELTA_DATE_AST : DELTA_DATETIME_AST, fieldType)\n        )\n    ) {\n        return null;\n    }\n    const [option, amountAST] = Object.entries(kwargs)[0];\n    return [toValue(amountAST), option];\n}\n\nfunction getDeltaExpression(value, fieldType) {\n    const ast = replaceKwargs(\n        fieldType === \"date\" ? DELTA_DATE_AST : DELTA_DATETIME_AST,\n        fieldType,\n        { [value[1]]: toAST(value[0]) }\n    );\n    return expression(formatAST(ast));\n}\n\nfunction isTodayExpr(val, type) {\n    return (\n        val._expr ===\n        (type === \"date\" ? DATE_TODAY_STRING_EXPRESSION : DATETIME_TODAY_STRING_EXPRESSION)\n    );\n}\n\nexport class Expression {\n    constructor(ast) {\n        if (typeof ast === \"string\") {\n            ast = parseExpr(ast);\n        }\n        this._ast = ast;\n        this._expr = formatAST(ast);\n    }\n\n    toAST() {\n        return this._ast;\n    }\n\n    toString() {\n        return this._expr;\n    }\n}\n\n/**\n * @param {string} expr\n * @returns {Expression}\n */\nexport function expression(expr) {\n    return new Expression(expr);\n}\n\n/**\n * @param {\"|\"|\"&\"} value\n * @param {Tree[]} [children=[]]\n * @param {boolean} [negate=false]\n * @returns {Connector}\n */\nexport function connector(value, children = [], negate = false) {\n    return { type: \"connector\", value, children, negate };\n}\n\n/**\n * @param {Value} path\n * @param {Value} operator\n * @param {Value} value\n * @param {boolean} [negate=false]\n * @returns {Condition}\n */\nexport function condition(path, operator, value, negate = false) {\n    return { type: \"condition\", path, operator, value, negate };\n}\n\n/**\n * @param {string} value\n * @returns {ComplexCondition}\n */\nexport function complexCondition(value) {\n    parseExpr(value);\n    return { type: \"complex_condition\", value };\n}\n\n/**\n * @param {Value} value\n * @returns {Value}\n */\nfunction cloneValue(value) {\n    if (value instanceof Expression) {\n        return new Expression(value.toAST());\n    }\n    if (Array.isArray(value)) {\n        return value.map(cloneValue);\n    }\n    return value;\n}\n\n/**\n * @param {Tree} tree\n * @returns {Tree}\n */\nexport function cloneTree(tree) {\n    const clone = {};\n    for (const key in tree) {\n        clone[key] = cloneValue(tree[key]);\n    }\n    return clone;\n}\n\nexport function formatValue(value) {\n    return formatAST(toAST(value));\n}\n\nexport function normalizeValue(value) {\n    return toValue(toAST(value)); // no array in array (see isWithinArray)\n}\n\n/**\n * @param {import(\"@web/core/py_js/py_parser\").AST} ast\n * @returns {Value}\n */\nexport function toValue(ast, isWithinArray = false) {\n    if ([4, 10].includes(ast.type) && !isWithinArray) {\n        /** 4: list, 10: tuple */\n        return ast.value.map((v) => toValue(v, true));\n    } else if ([0, 1, 2].includes(ast.type)) {\n        /** 0: number, 1: string, 2: boolean */\n        return ast.value;\n    } else if (ast.type === 6 && ast.op === \"-\" && ast.right.type === 0) {\n        /** 6: unary operator */\n        return -ast.right.value;\n    } else if (ast.type === 5 && [\"false\", \"true\"].includes(ast.value)) {\n        /** 5: name */\n        return JSON.parse(ast.value);\n    } else {\n        return new Expression(ast);\n    }\n}\n\nexport function isTree(value) {\n    return (\n        typeof value === \"object\" &&\n        !(value instanceof Domain) &&\n        !(value instanceof Expression) &&\n        !Array.isArray(value) &&\n        value !== null\n    );\n}\n\n/**\n * @param {Value} value\n * @returns  {import(\"@web/core/py_js/py_parser\").AST}\n */\nfunction toAST(value) {\n    if (isTree(value)) {\n        const domain = new Domain(domainFromTree(value));\n        return domain.ast;\n    }\n    if (value instanceof Expression) {\n        return value.toAST();\n    }\n    if (Array.isArray(value)) {\n        return { type: 4, value: value.map(toAST) };\n    }\n    return toPyValue(value);\n}\n\n/**\n * @param {AND|OR} parent\n * @param {Tree} child\n */\nfunction addChild(parent, child) {\n    if (child.type === \"connector\" && !child.negate && child.value === parent.value) {\n        parent.children.push(...child.children);\n    } else {\n        parent.children.push(child);\n    }\n}\n\n/**\n * @param {Condition} condition\n * @returns {Condition}\n */\nfunction getNormalizedCondition(condition) {\n    let { operator, negate } = condition;\n    if (negate && typeof operator === \"string\" && TERM_OPERATORS_NEGATION[operator]) {\n        operator = TERM_OPERATORS_NEGATION[operator];\n        negate = false;\n    }\n    return { ...condition, operator, negate };\n}\n\nfunction normalizeCondition(condition) {\n    Object.assign(condition, getNormalizedCondition(condition));\n}\n\n/**\n * @param {AST[]} ASTs\n * @param {boolean} distributeNot\n * @param {boolean} [negate=false]\n * @returns {{ tree: Tree, remaimingASTs: AST[] }}\n */\nfunction _construcTree(ASTs, distributeNot, negate = false) {\n    const [firstAST, ...tailASTs] = ASTs;\n\n    if (firstAST.type === 1 && firstAST.value === \"!\") {\n        return _construcTree(tailASTs, distributeNot, !negate);\n    }\n\n    const tree = { type: firstAST.type === 1 ? \"connector\" : \"condition\" };\n    if (tree.type === \"connector\") {\n        tree.value = firstAST.value;\n        if (distributeNot && negate) {\n            tree.value = tree.value === \"&\" ? \"|\" : \"&\";\n            tree.negate = false;\n        } else {\n            tree.negate = negate;\n        }\n        tree.children = [];\n    } else {\n        const [pathAST, operatorAST, valueAST] = firstAST.value;\n        tree.path = toValue(pathAST);\n        tree.negate = negate;\n        tree.operator = toValue(operatorAST);\n        tree.value = toValue(valueAST);\n        if ([\"any\", \"not any\"].includes(tree.operator)) {\n            try {\n                tree.value = treeFromDomain(formatAST(valueAST));\n            } catch {\n                tree.value = Array.isArray(tree.value) ? tree.value : [tree.value];\n            }\n        }\n        normalizeCondition(tree);\n    }\n    let remaimingASTs = tailASTs;\n    if (tree.type === \"connector\") {\n        for (let i = 0; i < 2; i++) {\n            const { tree: child, remaimingASTs: otherASTs } = _construcTree(\n                remaimingASTs,\n                distributeNot,\n                distributeNot && negate\n            );\n            remaimingASTs = otherASTs;\n            addChild(tree, child);\n        }\n    }\n    return { tree, remaimingASTs };\n}\n\n/**\n * @param {AST[]} initialASTs\n * @param {Object} options\n * @param {boolean} [options.distributeNot=false]\n * @returns {Tree}\n */\nfunction construcTree(initialASTs, options) {\n    if (!initialASTs.length) {\n        return connector(\"&\");\n    }\n    const { tree } = _construcTree(initialASTs, options.distributeNot);\n    return tree;\n}\n\n/**\n * @param {Tree} tree\n * @returns {AST[]}\n */\nfunction getASTs(tree) {\n    const ASTs = [];\n    if (tree.type === \"condition\") {\n        if (tree.negate) {\n            ASTs.push(toAST(\"!\"));\n        }\n        ASTs.push({\n            type: 10,\n            value: [tree.path, tree.operator, tree.value].map(toAST),\n        });\n        return ASTs;\n    }\n\n    const length = tree.children.length;\n    if (length && tree.negate) {\n        ASTs.push(toAST(\"!\"));\n    }\n    for (let i = 0; i < length - 1; i++) {\n        ASTs.push(toAST(tree.value));\n    }\n    for (const child of tree.children) {\n        ASTs.push(...getASTs(child));\n    }\n    return ASTs;\n}\n\nfunction not(ast) {\n    if (isNot(ast)) {\n        return ast.right;\n    }\n    if (ast.type === 2) {\n        return { ...ast, value: !ast.value };\n    }\n    if (ast.type === 7 && COMPARATORS.includes(ast.op)) {\n        return { ...ast, op: TERM_OPERATORS_NEGATION_EXTENDED[ast.op] }; // do not use this if ast is within a domain context!\n    }\n    return { type: 6, op: \"not\", right: isBool(ast) ? ast.args[0] : ast };\n}\n\nfunction bool(ast) {\n    if (isBool(ast) || isNot(ast) || ast.type === 2) {\n        return ast;\n    }\n    return { type: 8, fn: { type: 5, value: \"bool\" }, args: [ast], kwargs: {} };\n}\n\nfunction name(value) {\n    return { type: 5, value };\n}\n\nfunction or(left, right) {\n    return { type: 14, op: \"or\", left, right };\n}\n\nfunction and(left, right) {\n    return { type: 14, op: \"and\", left, right };\n}\n\nfunction isNot(ast) {\n    return ast.type === 6 && ast.op === \"not\";\n}\n\nfunction is(oneParamFunc, ast) {\n    return (\n        ast.type === 8 &&\n        ast.fn.type === 5 &&\n        ast.fn.value === oneParamFunc &&\n        ast.args.length === 1\n    ); // improve condition?\n}\n\nfunction isSet(ast) {\n    return ast.type === 8 && ast.fn.type === 5 && ast.fn.value === \"set\" && ast.args.length <= 1;\n}\n\nfunction isBool(ast) {\n    return is(\"bool\", ast);\n}\n\nfunction isValidPath(ast, options) {\n    const getFieldDef = options.getFieldDef || (() => null);\n    if (ast.type === 5) {\n        return getFieldDef(ast.value) !== null;\n    }\n    return false;\n}\n\nfunction isX2Many(ast, options) {\n    if (isValidPath(ast, options)) {\n        const fieldDef = options.getFieldDef(ast.value); // safe: isValidPath has not returned null;\n        return [\"many2many\", \"one2many\"].includes(fieldDef.type);\n    }\n    return false;\n}\n\nfunction _getConditionFromComparator(ast, options) {\n    if ([\"is\", \"is not\"].includes(ast.op)) {\n        // we could do something smarter here\n        // e.g. if left is a boolean field and right is a boolean\n        // we can create a condition based on \"=\"\n        return null;\n    }\n\n    let operator = ast.op;\n    if (operator === \"==\") {\n        operator = \"=\";\n    }\n\n    let left = ast.left;\n    let right = ast.right;\n    if (isValidPath(left, options) == isValidPath(right, options)) {\n        return null;\n    }\n\n    if (!isValidPath(left, options)) {\n        if (operator in EXCHANGE) {\n            const temp = left;\n            left = right;\n            right = temp;\n            operator = EXCHANGE[operator];\n        } else {\n            return null;\n        }\n    }\n\n    return condition(left.value, operator, toValue(right));\n}\n\nfunction isValidPath2(ast, options) {\n    if (!ast) {\n        return null;\n    }\n    if ([4, 10].includes(ast.type) && ast.value.length === 1) {\n        return isValidPath(ast.value[0], options);\n    }\n    return isValidPath(ast, options);\n}\n\nfunction _getConditionFromIntersection(ast, options, negate = false) {\n    let left = ast.fn.obj.args[0];\n    let right = ast.args[0];\n\n    if (!left) {\n        return condition(negate ? 1 : 0, \"=\", 1);\n    }\n\n    // left/right exchange\n    if (isValidPath2(left, options) == isValidPath2(right, options)) {\n        return null;\n    }\n    if (!isValidPath2(left, options)) {\n        const temp = left;\n        left = right;\n        right = temp;\n    }\n\n    if ([4, 10].includes(left.type) && left.value.length === 1) {\n        left = left.value[0];\n    }\n\n    if (!right) {\n        return condition(left.value, negate ? \"=\" : \"!=\", false);\n    }\n\n    // try to extract the ast of an iterable\n    // we only make simple conversions here\n    if (isSet(right)) {\n        if (!right.args[0]) {\n            right = { type: 4, value: [] };\n        }\n        if ([4, 10].includes(right.args[0].type)) {\n            right = right.args[0];\n        }\n    }\n\n    if (![4, 10].includes(right.type)) {\n        return null;\n    }\n\n    return condition(left.value, negate ? \"not in\" : \"in\", toValue(right));\n}\n\n/**\n * @param {AST} ast\n * @param {Options} options\n * @param {boolean} [negate=false]\n * @returns {Condition|ComplexCondition}\n */\nfunction _leafFromAST(ast, options, negate = false) {\n    if (isNot(ast)) {\n        return _treeFromAST(ast.right, options, !negate);\n    }\n\n    if (ast.type === 5 /** name */ && isValidPath(ast, options)) {\n        return condition(ast.value, negate ? \"=\" : \"!=\", false);\n    }\n\n    const astValue = toValue(ast);\n    if ([\"boolean\", \"number\", \"string\"].includes(typeof astValue)) {\n        return condition(astValue ? 1 : 0, \"=\", 1);\n    }\n\n    if (\n        ast.type === 8 &&\n        ast.fn.type === 15 /** object lookup */ &&\n        isSet(ast.fn.obj) &&\n        ast.fn.key === \"intersection\"\n    ) {\n        const tree = _getConditionFromIntersection(ast, options, negate);\n        if (tree) {\n            return tree;\n        }\n    }\n\n    if (ast.type === 7 && COMPARATORS.includes(ast.op)) {\n        if (negate) {\n            return _leafFromAST(not(ast), options);\n        }\n        const tree = _getConditionFromComparator(ast, options);\n        if (tree) {\n            return tree;\n        }\n    }\n\n    // no conclusive/simple way to transform ast in a condition\n    return complexCondition(formatAST(negate ? not(ast) : ast));\n}\n\n/**\n * @param {AST} ast\n * @param {Options} options\n * @param {boolean} [negate=false]\n * @returns {Tree}\n */\nfunction _treeFromAST(ast, options, negate = false) {\n    if (isNot(ast)) {\n        return _treeFromAST(ast.right, options, !negate);\n    }\n\n    if (ast.type === 14) {\n        const tree = connector(\n            ast.op === \"and\" ? \"&\" : \"|\" // and/or are the only ops that are given type 14 (for now)\n        );\n        if (options.distributeNot && negate) {\n            tree.value = tree.value === \"&\" ? \"|\" : \"&\";\n        } else {\n            tree.negate = negate;\n        }\n        const subASTs = [ast.left, ast.right];\n        for (const subAST of subASTs) {\n            const child = _treeFromAST(subAST, options, options.distributeNot && negate);\n            addChild(tree, child);\n        }\n        return tree;\n    }\n\n    if (ast.type === 13) {\n        const newAST = or(and(ast.condition, ast.ifTrue), and(not(ast.condition), ast.ifFalse));\n        return _treeFromAST(newAST, options, negate);\n    }\n\n    return _leafFromAST(ast, options, negate);\n}\n\nfunction _expressionFromTree(tree, options, isRoot = false) {\n    if (tree.type === \"connector\" && tree.value === \"|\" && tree.children.length === 2) {\n        // check if we have an \"if else\"\n        const isSimpleAnd = (tree) =>\n            tree.type === \"connector\" && tree.value === \"&\" && tree.children.length === 2;\n        if (tree.children.every((c) => isSimpleAnd(c))) {\n            const [c1, c2] = tree.children;\n            for (let i = 0; i < 2; i++) {\n                const c1Child = c1.children[i];\n                const str1 = _expressionFromTree({ ...c1Child }, options);\n                for (let j = 0; j < 2; j++) {\n                    const c2Child = c2.children[j];\n                    const str2 = _expressionFromTree(c2Child, options);\n                    if (str1 === `not ${str2}` || `not ${str1}` === str2) {\n                        /** @todo smth smarter. this is very fragile */\n                        const others = [c1.children[1 - i], c2.children[1 - j]];\n                        const str = _expressionFromTree(c1Child, options);\n                        const strs = others.map((c) => _expressionFromTree(c, options));\n                        return `${strs[0]} if ${str} else ${strs[1]}`;\n                    }\n                }\n            }\n        }\n    }\n\n    if (tree.type === \"connector\") {\n        const connector = tree.value === \"&\" ? \"and\" : \"or\";\n        const subExpressions = tree.children.map((c) => _expressionFromTree(c, options));\n        if (!subExpressions.length) {\n            return connector === \"and\" ? \"1\" : \"0\";\n        }\n        let expression = subExpressions.join(` ${connector} `);\n        if (!isRoot || tree.negate) {\n            expression = `( ${expression} )`;\n        }\n        if (tree.negate) {\n            expression = `not ${expression}`;\n        }\n        return expression;\n    }\n\n    if (tree.type === \"complex_condition\") {\n        return tree.value;\n    }\n\n    tree = getNormalizedCondition(tree);\n    const { path, operator, value } = tree;\n\n    const op = operator === \"=\" ? \"==\" : operator; // do something about is ?\n    if (typeof op !== \"string\" || !COMPARATORS.includes(op)) {\n        throw new Error(\"Invalid operator\");\n    }\n\n    // we can assume that negate = false here: comparators have negation defined\n    // and the tree has been normalized\n\n    if ([0, 1].includes(path)) {\n        if (operator !== \"=\" || value !== 1) {\n            // check if this is too restricive for us\n            return new Error(\"Invalid condition\");\n        }\n        return formatAST({ type: 2, value: Boolean(path) });\n    }\n\n    const pathAST = toAST(path);\n    if (typeof path == \"string\" && isValidPath(name(path), options)) {\n        pathAST.type = 5;\n    }\n\n    if (value === false && [\"=\", \"!=\"].includes(operator)) {\n        // true makes sense for non boolean fields?\n        return formatAST(operator === \"=\" ? not(pathAST) : pathAST);\n    }\n\n    let valueAST = toAST(value);\n    if (\n        [\"in\", \"not in\"].includes(operator) &&\n        !(value instanceof Expression) &&\n        ![4, 10].includes(valueAST.type)\n    ) {\n        valueAST = { type: 4, value: [valueAST] };\n    }\n\n    if (pathAST.type === 5 && isX2Many(pathAST, options) && [\"in\", \"not in\"].includes(operator)) {\n        const ast = {\n            type: 8,\n            fn: {\n                type: 15,\n                obj: {\n                    args: [pathAST],\n                    type: 8,\n                    fn: {\n                        type: 5,\n                        value: \"set\",\n                    },\n                },\n                key: \"intersection\",\n            },\n            args: [valueAST],\n        };\n        return formatAST(operator === \"not in\" ? not(ast) : ast);\n    }\n\n    // add case true for boolean fields\n\n    return formatAST({\n        type: 7,\n        op,\n        left: pathAST,\n        right: valueAST,\n    });\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//  PUBLIC: CREATE/REMOVE\n//    between operator\n//    is, is_not, set, not_set operators\n//    complex conditions\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n * @param {Tree} tree\n * @returns {Tree}\n */\nfunction createBetweenOperators(tree) {\n    if ([\"condition\", \"complex_condition\"].includes(tree.type)) {\n        return tree;\n    }\n    const processedChildren = tree.children.map(createBetweenOperators);\n    if (tree.value === \"|\") {\n        return { ...tree, children: processedChildren };\n    }\n    const children = [];\n    for (let i = 0; i < processedChildren.length; i++) {\n        const child1 = processedChildren[i];\n        const child2 = processedChildren[i + 1];\n        if (\n            child1.type === \"condition\" &&\n            child2 &&\n            child2.type === \"condition\" &&\n            formatValue(child1.path) === formatValue(child2.path) &&\n            child1.operator === \">=\" &&\n            child2.operator === \"<=\"\n        ) {\n            children.push(\n                condition(child1.path, \"between\", normalizeValue([child1.value, child2.value]))\n            );\n            i += 1;\n        } else {\n            children.push(child1);\n        }\n    }\n    if (children.length === 1) {\n        return { ...children[0] };\n    }\n    return { ...tree, children };\n}\n\n/**\n * @param {Tree} tree\n * @param {Options} [options={}]\n * @returns {Tree}\n */\nfunction createWithinOperators(tree, options = {}) {\n    if (tree.children) {\n        return {\n            ...tree,\n            children: tree.children.map((child) => createWithinOperators(child, options)),\n        };\n    }\n    const fieldType = options.getFieldDef?.(tree.path)?.type;\n    if (tree.operator !== \"between\" || ![\"date\", \"datetime\"].includes(fieldType)) {\n        return tree;\n    }\n\n    function getProcessedDelta(val, periodShouldBePositive = true) {\n        const delta = getDelta(toAST(val), fieldType);\n        if (delta) {\n            const [amount] = delta;\n            if (\n                Number.isInteger(amount) &&\n                // @ts-ignore\n                ((amount < 0 && periodShouldBePositive) || (amount > 0 && !periodShouldBePositive))\n            ) {\n                return null;\n            }\n        }\n        return delta;\n    }\n\n    const newTree = { ...tree };\n\n    if (isTodayExpr(newTree.value[0], fieldType)) {\n        const delta = getProcessedDelta(newTree.value[1]);\n        if (delta) {\n            newTree.operator = \"within\";\n            newTree.value = [...delta, fieldType];\n        }\n    } else if (isTodayExpr(newTree.value[1], fieldType)) {\n        const delta = getProcessedDelta(newTree.value[0], false);\n        if (delta) {\n            newTree.operator = \"within\";\n            newTree.value = [...delta, fieldType];\n        }\n    }\n\n    return newTree;\n}\n\n/**\n * @param {Tree} tree\n * @returns {Tree}\n */\nexport function removeBetweenOperators(tree) {\n    if (tree.type === \"complex_condition\") {\n        return tree;\n    }\n    if (tree.type === \"condition\") {\n        if (tree.operator !== \"between\") {\n            return tree;\n        }\n        const { negate, path, value } = tree;\n        return connector(\n            \"&\",\n            [condition(path, \">=\", value[0]), condition(path, \"<=\", value[1])],\n            negate\n        );\n    }\n    const processedChildren = tree.children.map(removeBetweenOperators);\n    if (tree.value === \"|\") {\n        return { ...tree, children: processedChildren };\n    }\n    const newTree = { ...tree, children: [] };\n    // after processing a child might have become a connector \"&\" --> normalize\n    for (let i = 0; i < processedChildren.length; i++) {\n        addChild(newTree, processedChildren[i]);\n    }\n    return newTree;\n}\n\nexport function removeWithinOperators(tree) {\n    if (tree.type === \"complex_condition\") {\n        return tree;\n    }\n    if (tree.type === \"condition\") {\n        if (tree.operator !== \"within\") {\n            return tree;\n        }\n        const { negate, path, value } = tree;\n        const fieldType = value[2];\n        const expressions = [\n            expression(\n                fieldType === \"date\"\n                    ? DATE_TODAY_STRING_EXPRESSION\n                    : DATETIME_TODAY_STRING_EXPRESSION\n            ),\n            getDeltaExpression(value, fieldType),\n        ];\n        const reverse = Number.isInteger(value[0]) && value[0] > 0;\n        return condition(\n            path,\n            \"between\",\n            reverse ? Object.values(expressions) : Object.values(expressions).reverse(),\n            negate\n        );\n    }\n    const processedChildren = tree.children.map(removeWithinOperators);\n    return { ...tree, children: processedChildren };\n}\n\n/**\n * @param {Tree} tree\n * @param {options} [options={}]\n * @param {Function} [options.getFieldDef]\n * @returns {Tree}\n */\nexport function createVirtualOperators(tree, options = {}) {\n    if (tree.type === \"condition\") {\n        const { path, operator, value } = tree;\n        if ([\"=\", \"!=\"].includes(operator)) {\n            const fieldDef = options.getFieldDef?.(path) || null;\n            if (fieldDef) {\n                if (fieldDef.type === \"boolean\") {\n                    return { ...tree, operator: operator === \"=\" ? \"is\" : \"is_not\" };\n                } else if (\n                    ![\"many2one\", \"date\", \"datetime\"].includes(fieldDef?.type) &&\n                    value === false\n                ) {\n                    return { ...tree, operator: operator === \"=\" ? \"not_set\" : \"set\" };\n                }\n            }\n        }\n        if (operator === \"=ilike\") {\n            if (value.endsWith?.(\"%\")) {\n                return { ...tree, operator: \"starts_with\", value: value.slice(0, -1) };\n            }\n            if (value.startsWith?.(\"%\")) {\n                return { ...tree, operator: \"ends_with\", value: value.slice(1) };\n            }\n        }\n        return tree;\n    }\n    if (tree.type === \"complex_condition\") {\n        return tree;\n    }\n    const processedChildren = tree.children.map((c) => createVirtualOperators(c, options));\n    return { ...tree, children: processedChildren };\n}\n\n/**\n * @param {Tree} tree\n * @returns {Tree}\n */\nexport function removeVirtualOperators(tree) {\n    if (tree.type === \"condition\") {\n        const { operator, value } = tree;\n        if ([\"is\", \"is_not\"].includes(operator)) {\n            return { ...tree, operator: operator === \"is\" ? \"=\" : \"!=\" };\n        }\n        if ([\"set\", \"not_set\"].includes(operator)) {\n            return { ...tree, operator: operator === \"set\" ? \"!=\" : \"=\" };\n        }\n        if ([\"starts_with\", \"ends_with\"].includes(operator)) {\n            return {\n                ...tree,\n                value: operator === \"starts_with\" ? `${value}%` : `%${value}`,\n                operator: \"=ilike\",\n            };\n        }\n        return tree;\n    }\n    if (tree.type === \"complex_condition\") {\n        return tree;\n    }\n    const processedChildren = tree.children.map((c) => removeVirtualOperators(c));\n    return { ...tree, children: processedChildren };\n}\n\n/**\n * @param {Tree} tree\n * @returns {Tree} the conditions better expressed as complex conditions become complex conditions\n */\nfunction createComplexConditions(tree) {\n    if (tree.type === \"condition\") {\n        if (tree.path instanceof Expression && tree.operator === \"=\" && tree.value === 1) {\n            // not sure about this one -> we should maybe evaluate the condition and check\n            // if it does not become something e.g. the name of a integer field?\n            return complexCondition(String(tree.path));\n        }\n        return cloneTree(tree);\n    }\n    if (tree.type === \"complex_condition\") {\n        return cloneTree(tree);\n    }\n    return {\n        ...tree,\n        children: tree.children.map((child) => createComplexConditions(child)),\n    };\n}\n\n/**\n * @param {Tree} tree\n * @returns {Tree} a simple tree (without complex conditions)\n */\nfunction removeComplexConditions(tree) {\n    if (tree.type === \"condition\") {\n        return cloneTree(tree);\n    }\n    if (tree.type === \"complex_condition\") {\n        const ast = parseExpr(tree.value);\n        return condition(new Expression(bool(ast)), \"=\", 1);\n    }\n    return {\n        ...tree,\n        children: tree.children.map((child) => removeComplexConditions(child)),\n    };\n}\n\n////////////////////////////////////////////////////////////////////////////////\n//  PUBLIC: MAPPINGS\n//    tree <-> expression\n//    domain <-> expression\n//    expression <-> tree\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n * @param {string} expression\n * @param {Options} [options={}]\n * @returns {Tree} a tree representation of an expression\n */\nexport function treeFromExpression(expression, options = {}) {\n    const ast = parseExpr(expression);\n    const tree = _treeFromAST(ast, options);\n    return createVirtualOperators(\n        createWithinOperators(createBetweenOperators(tree), options),\n        options\n    );\n}\n\n/**\n * @param {Tree} tree\n * @param {Options} [options={}]\n * @returns {string} an expression\n */\nexport function expressionFromTree(tree, options = {}) {\n    const simplifiedTree = createComplexConditions(\n        removeBetweenOperators(removeWithinOperators(removeVirtualOperators(tree)))\n    );\n    return _expressionFromTree(simplifiedTree, options, true);\n}\n\n/**\n * @param {Tree} tree\n * @returns {string} a string representation of a domain\n */\nexport function domainFromTree(tree) {\n    const simplifiedTree = removeBetweenOperators(\n        removeWithinOperators(removeVirtualOperators(removeComplexConditions(tree)))\n    );\n    const domainAST = {\n        type: 4,\n        value: getASTs(simplifiedTree),\n    };\n    return formatAST(domainAST);\n}\n\n/**\n * @param {DomainRepr} domain\n * @param {Object} [options={}] see construcTree API\n * @returns {Tree} a (simple) tree representation of a domain\n */\nexport function treeFromDomain(domain, options = {}) {\n    domain = new Domain(domain);\n    const domainAST = domain.ast;\n    const tree = construcTree(domainAST.value, options); // a simple tree\n    return createVirtualOperators(\n        createWithinOperators(createBetweenOperators(tree), options),\n        options\n    );\n}\n\n/**\n * @param {DomainRepr} domain a string representation of a domain\n * @param {Options} [options={}]\n * @returns {string} an expression\n */\nexport function expressionFromDomain(domain, options = {}) {\n    const tree = treeFromDomain(domain, options);\n    return expressionFromTree(tree, options);\n}\n\n/**\n * @param {string} expression an expression\n * @param {Options} [options={}]\n * @returns {string} a string representation of a domain\n */\nexport function domainFromExpression(expression, options = {}) {\n    const tree = treeFromExpression(expression, options);\n    return domainFromTree(tree);\n}\n", "import {\n    getResModel,\n    useMakeGetFieldDef,\n    useMakeGetConditionDescription,\n} from \"@web/core/tree_editor/utils\";\nimport { Component, onWillStart, onWillUpdateProps } from \"@odoo/owl\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\nimport {\n    condition,\n    cloneTree,\n    formatValue,\n    removeVirtualOperators,\n    connector,\n    isTree,\n} from \"@web/core/tree_editor/condition_tree\";\nimport {\n    getDefaultValue,\n    getValueEditorInfo,\n} from \"@web/core/tree_editor/tree_editor_value_editors\";\nimport { ModelFieldSelector } from \"@web/core/model_field_selector/model_field_selector\";\nimport { useLoadFieldInfo } from \"@web/core/model_field_selector/utils\";\nimport { deepEqual, shallowEqual } from \"@web/core/utils/objects\";\n\nconst TRUE_TREE = condition(1, \"=\", 1);\n\nfunction collectDifferences(tree, otherTree) {\n    // some differences shadow the other differences \"below\":\n    if (tree.type !== otherTree.type) {\n        return [{ type: \"other\" }];\n    }\n    if (tree.negate !== otherTree.negate) {\n        return [{ type: \"other\" }];\n    }\n    if (tree.type === \"condition\") {\n        if (formatValue(tree.path) !== formatValue(otherTree.path)) {\n            return [{ type: \"other\" }];\n        }\n        if (formatValue(tree.value) !== formatValue(otherTree.value)) {\n            return [{ type: \"other\" }];\n        }\n        if (formatValue(tree.operator) !== formatValue(otherTree.operator)) {\n            if (tree.operator === \"!=\" && otherTree.operator === \"set\") {\n                return [{ type: \"replacement\", tree, operator: \"set\" }];\n            } else if (tree.operator === \"=\" && otherTree.operator === \"not_set\") {\n                return [{ type: \"replacement\", tree, operator: \"not_set\" }];\n            } else {\n                return [{ type: \"other\" }];\n            }\n        }\n        return [];\n    }\n    if (tree.value !== otherTree.value) {\n        return [{ type: \"other\" }];\n    }\n    if (tree.type === \"complex_condition\") {\n        return [];\n    }\n    if (tree.children.length !== otherTree.children.length) {\n        return [{ type: \"other\" }];\n    }\n    const diffs = [];\n    for (let i = 0; i < tree.children.length; i++) {\n        const child = tree.children[i];\n        const otherChild = otherTree.children[i];\n        const childDiffs = collectDifferences(child, otherChild);\n        if (childDiffs.some((d) => d.type !== \"replacement\")) {\n            return [{ type: \"other\" }];\n        }\n        diffs.push(...childDiffs);\n    }\n    return diffs;\n}\n\nfunction restoreVirtualOperators(tree, otherTree) {\n    const diffs = collectDifferences(tree, otherTree);\n    // note that the array diffs is homogeneous:\n    // we have diffs of the form [], [other], [repl, ..., repl]\n    if (diffs.some((d) => d.type !== \"replacement\")) {\n        return;\n    }\n    for (const { tree, operator } of diffs) {\n        tree.operator = operator;\n    }\n}\n\nexport class TreeEditor extends Component {\n    static template = \"web.TreeEditor\";\n    static components = {\n        Dropdown,\n        DropdownItem,\n        ModelFieldSelector,\n        TreeEditor,\n    };\n    static props = {\n        tree: Object,\n        resModel: String,\n        update: Function,\n        getDefaultCondition: Function,\n        getPathEditorInfo: Function,\n        getOperatorEditorInfo: Function,\n        getDefaultOperator: Function,\n        readonly: { type: Boolean, optional: true },\n        slots: { type: Object, optional: true },\n        isDebugMode: { type: Boolean, optional: true },\n        defaultConnector: { type: [{ value: \"&\" }, { value: \"|\" }], optional: true },\n        isSubTree: { type: Boolean, optional: true },\n    };\n    static defaultProps = {\n        defaultConnector: \"&\",\n        readonly: false,\n        isSubTree: false,\n    };\n\n    setup() {\n        this.isTree = isTree;\n        this.fieldService = useService(\"field\");\n        this.nameService = useService(\"name\");\n        this.loadFieldInfo = useLoadFieldInfo(this.fieldService);\n        this.makeGetFieldDef = useMakeGetFieldDef(this.fieldService);\n        this.makeGetConditionDescription = useMakeGetConditionDescription(\n            this.fieldService,\n            this.nameService\n        );\n        onWillStart(() => this.onPropsUpdated(this.props));\n        onWillUpdateProps((nextProps) => this.onPropsUpdated(nextProps));\n    }\n\n    async onPropsUpdated(props) {\n        this.tree = cloneTree(props.tree);\n        if (shallowEqual(this.tree, TRUE_TREE)) {\n            this.tree = connector(props.defaultConnector);\n        } else if (this.tree.type !== \"connector\") {\n            this.tree = connector(props.defaultConnector, [this.tree]);\n        }\n\n        if (this.previousTree) {\n            // find \"first\" difference\n            restoreVirtualOperators(this.tree, this.previousTree);\n            this.previousTree = null;\n        }\n\n        const [fieldDefs, getFieldDef] = await Promise.all([\n            this.fieldService.loadFields(props.resModel),\n            this.makeGetFieldDef(props.resModel, this.tree),\n        ]);\n        this.getFieldDef = getFieldDef;\n        this.defaultCondition = props.getDefaultCondition(fieldDefs);\n\n        if (props.readonly) {\n            this.getConditionDescription = await this.makeGetConditionDescription(\n                props.resModel,\n                this.tree,\n                this.getFieldDef\n            );\n        }\n    }\n\n    get className() {\n        return `${this.props.readonly ? \"o_read_mode\" : \"o_edit_mode\"}`;\n    }\n\n    get isDebugMode() {\n        return this.props.isDebugMode !== undefined ? this.props.isDebugMode : !!this.env.debug;\n    }\n\n    notifyChanges() {\n        this.previousTree = cloneTree(this.tree);\n        this.props.update(this.tree);\n    }\n\n    updateConnector(node, value) {\n        node.value = value;\n        node.negate = false;\n        this.notifyChanges();\n    }\n\n    updateComplexCondition(node, value) {\n        node.value = value;\n        this.notifyChanges();\n    }\n\n    createNewLeaf() {\n        return cloneTree(this.defaultCondition);\n    }\n\n    createNewBranch(value) {\n        return connector(value, [this.createNewLeaf(), this.createNewLeaf()]);\n    }\n\n    insertRootLeaf(parent) {\n        parent.children.push(this.createNewLeaf());\n        this.notifyChanges();\n    }\n\n    insertLeaf(parent, node) {\n        const newNode = node.type !== \"connector\" ? cloneTree(node) : this.createNewLeaf();\n        const index = parent.children.indexOf(node);\n        parent.children.splice(index + 1, 0, newNode);\n        this.notifyChanges();\n    }\n\n    insertBranch(parent, node) {\n        const nextConnector = parent.value === \"&\" ? \"|\" : \"&\";\n        const newNode = this.createNewBranch(nextConnector);\n        const index = parent.children.indexOf(node);\n        parent.children.splice(index + 1, 0, newNode);\n        this.notifyChanges();\n    }\n\n    delete(parent, node) {\n        const index = parent.children.indexOf(node);\n        parent.children.splice(index, 1);\n        this.notifyChanges();\n    }\n\n    getResModel(node) {\n        const fieldDef = this.getFieldDef(node.path);\n        const resModel = getResModel(fieldDef);\n        return resModel;\n    }\n\n    getPathEditorInfo() {\n        return this.props.getPathEditorInfo(this.props.resModel, this.defaultCondition);\n    }\n\n    getOperatorEditorInfo(node) {\n        const fieldDef = this.getFieldDef(node.path);\n        return this.props.getOperatorEditorInfo(fieldDef);\n    }\n\n    getValueEditorInfo(node) {\n        const fieldDef = this.getFieldDef(node.path);\n        return getValueEditorInfo(fieldDef, node.operator);\n    }\n\n    async updatePath(node, path) {\n        const { fieldDef } = await this.loadFieldInfo(this.props.resModel, path);\n        node.path = path;\n        node.negate = false;\n        node.operator = this.props.getDefaultOperator(fieldDef);\n        node.value = getDefaultValue(fieldDef, node.operator);\n        this.notifyChanges();\n    }\n\n    updateLeafOperator(node, operator, negate) {\n        const previousNode = cloneTree(node);\n        const fieldDef = this.getFieldDef(node.path);\n        node.negate = negate;\n        node.operator = operator;\n        node.value = getDefaultValue(fieldDef, operator, node.value);\n        if (deepEqual(removeVirtualOperators(node), removeVirtualOperators(previousNode))) {\n            // no interesting changes for parent\n            // this means that parent might not render the domain selector\n            // but we need to udpate editors\n            this.render();\n        }\n        this.notifyChanges();\n    }\n\n    updateLeafValue(node, value) {\n        node.value = value;\n        this.notifyChanges();\n    }\n\n    highlightNode(target) {\n        const nodeEl = target.closest(\".o_tree_editor_node\");\n        nodeEl.classList.toggle(\"o_hovered_button\");\n    }\n}\n", "import { MultiRecordSelector } from \"@web/core/record_selectors/multi_record_selector\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { formatAST, toPyValue } from \"@web/core/py_js/py_utils\";\nimport { Expression } from \"@web/core/tree_editor/condition_tree\";\nimport { RecordSelector } from \"@web/core/record_selectors/record_selector\";\n\nexport const isId = (val) => Number.isInteger(val) && val >= 1;\n\nexport const getFormat = (val, displayNames) => {\n    let text;\n    let colorIndex;\n    if (isId(val)) {\n        text =\n            typeof displayNames[val] === \"string\"\n                ? displayNames[val]\n                : _t(\"Inaccessible/missing record ID: %s\", val);\n        colorIndex = typeof displayNames[val] === \"string\" ? 0 : 2; // 0 = grey, 2 = orange\n    } else {\n        text =\n            val instanceof Expression\n                ? String(val)\n                : _t(\"Invalid record ID: %s\", formatAST(toPyValue(val)));\n        colorIndex = val instanceof Expression ? 2 : 1; // 1 = red\n    }\n    return { text, colorIndex };\n};\n\nexport class DomainSelectorAutocomplete extends MultiRecordSelector {\n    static props = {\n        ...MultiRecordSelector.props,\n        resIds: true, //resIds could be an array of ids or an array of expressions\n    };\n\n    getIds(props = this.props) {\n        return props.resIds.filter((val) => isId(val));\n    }\n\n    getTags(props, displayNames) {\n        return props.resIds.map((val, index) => {\n            const { text, colorIndex } = getFormat(val, displayNames);\n            return {\n                text,\n                colorIndex,\n                onDelete: () => {\n                    this.props.update([\n                        ...this.props.resIds.slice(0, index),\n                        ...this.props.resIds.slice(index + 1),\n                    ]);\n                },\n            };\n        });\n    }\n}\n\nexport class DomainSelectorSingleAutocomplete extends RecordSelector {\n    static props = {\n        ...RecordSelector.props,\n        resId: true,\n    };\n\n    getDisplayName(props = this.props, displayNames) {\n        const { resId } = props;\n        if (resId === false) {\n            return \"\";\n        }\n        const { text } = getFormat(resId, displayNames);\n        return text;\n    }\n\n    getIds(props = this.props) {\n        if (isId(props.resId)) {\n            return [props.resId];\n        }\n        return [];\n    }\n}\n", "import { Component } from \"@odoo/owl\";\nimport { TagsList } from \"@web/core/tags_list/tags_list\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport class Input extends Component {\n    static props = [\"value\", \"update\", \"startEmpty?\"];\n    static template = \"web.TreeEditor.Input\";\n}\n\nexport class Select extends Component {\n    static props = [\"value\", \"update\", \"options\", \"addBlankOption?\"];\n    static template = \"web.TreeEditor.Select\";\n\n    deserialize(value) {\n        return JSON.parse(value);\n    }\n\n    serialize(value) {\n        return JSON.stringify(value);\n    }\n}\n\nexport class Range extends Component {\n    static props = [\"value\", \"update\", \"editorInfo\"];\n    static template = \"web.TreeEditor.Range\";\n\n    update(index, newValue) {\n        const result = [...this.props.value];\n        result[index] = newValue;\n        return this.props.update(result);\n    }\n}\n\nexport class Within extends Component {\n    static props = [\"value\", \"update\", \"amountEditorInfo\", \"optionEditorInfo\"];\n    static template = \"web.TreeEditor.Within\";\n    static components = { Input, Select };\n    static options = [\n        [\"days\", _t(\"days\")],\n        [\"weeks\", _t(\"weeks\")],\n        [\"months\", _t(\"months\")],\n        [\"years\", _t(\"years\")],\n    ];\n    update(index, newValue) {\n        const result = [...this.props.value];\n        result[index] = newValue;\n        return this.props.update(result);\n    }\n}\n\nexport class List extends Component {\n    static components = { TagsList };\n    static props = [\"value\", \"update\", \"editorInfo\"];\n    static template = \"web.TreeEditor.List\";\n\n    get tags() {\n        const { isSupported, stringify } = this.props.editorInfo;\n        return this.props.value.map((val, index) => ({\n            text: stringify(val),\n            colorIndex: isSupported(val) ? 0 : 2,\n            onDelete: () => {\n                this.props.update([\n                    ...this.props.value.slice(0, index),\n                    ...this.props.value.slice(index + 1),\n                ]);\n            },\n        }));\n    }\n\n    update(newValue) {\n        return this.props.update([...this.props.value, newValue]);\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport {\n    formatValue,\n    TERM_OPERATORS_NEGATION,\n    toValue,\n} from \"@web/core/tree_editor/condition_tree\";\nimport { sprintf } from \"@web/core/utils/strings\";\nimport { parseExpr } from \"@web/core/py_js/py\";\nimport { Select } from \"@web/core/tree_editor/tree_editor_components\";\n\nconst OPERATOR_DESCRIPTIONS = {\n    // valid operators (see TERM_OPERATORS in expression.py)\n    \"=\": \"=\",\n    \"!=\": \"!=\",\n    \"<=\": \"<=\",\n    \"<\": \"<\",\n    \">\": \">\",\n    \">=\": \">=\",\n    \"=?\": \"=?\",\n    \"=like\": _t(\"=like\"),\n    \"=ilike\": _t(\"=ilike\"),\n    like: _t(\"like\"),\n    \"not like\": _t(\"not like\"),\n    ilike: _t(\"contains\"),\n    \"not ilike\": _t(\"does not contain\"),\n    in: _t(\"is in\"),\n    \"not in\": _t(\"is not in\"),\n    child_of: _t(\"child of\"),\n    parent_of: _t(\"parent of\"),\n\n    // virtual operators (replace = and != in some cases)\n    is: _t(\"is\"),\n    is_not: _t(\"is not\"),\n    set: _t(\"is set\"),\n    not_set: _t(\"is not set\"),\n\n    starts_with: _t(\"starts with\"),\n    ends_with: _t(\"ends with\"),\n\n    // virtual operator (equivalent to a couple (>=,<=))\n    between: _t(\"is between\"),\n    within: _t(\"is within\"),\n\n    any: (fieldDefType) => {\n        switch (fieldDefType) {\n            case \"many2one\":\n                return _t(\"matches\");\n            default:\n                return _t(\"match\");\n        }\n    },\n    \"not any\": (fieldDefType) => {\n        switch (fieldDefType) {\n            case \"many2one\":\n                return _t(\"matches none of\");\n            default:\n                return _t(\"match none of\");\n        }\n    },\n};\n\nfunction toKey(operator, negate = false) {\n    if (!negate && typeof operator === \"string\" && operator in OPERATOR_DESCRIPTIONS) {\n        // this case is the main one. We keep it simple\n        return operator;\n    }\n    return JSON.stringify([formatValue(operator), negate]);\n}\n\nfunction toOperator(key) {\n    if (!key.includes(\"[\")) {\n        return [key, false];\n    }\n    const [expr, negate] = JSON.parse(key);\n    return [toValue(parseExpr(expr)), negate];\n}\n\nfunction getOperatorDescription(operator, fieldDefType) {\n    const description = OPERATOR_DESCRIPTIONS[operator];\n    if (\n        typeof description === \"function\" &&\n        description.constructor?.name !== \"LazyTranslatedString\"\n    ) {\n        return description(fieldDefType);\n    }\n    return description;\n}\n\nexport function getOperatorLabel(operator, fieldDefType, negate = false) {\n    let label;\n    if (typeof operator === \"string\" && operator in OPERATOR_DESCRIPTIONS) {\n        if (negate && operator in TERM_OPERATORS_NEGATION) {\n            return getOperatorDescription(TERM_OPERATORS_NEGATION[operator], fieldDefType);\n        }\n        label = getOperatorDescription(operator, fieldDefType);\n    } else {\n        label = formatValue(operator);\n    }\n    if (negate) {\n        return sprintf(`not %s`, label);\n    }\n    return label;\n}\n\nfunction getOperatorInfo(operator, fieldDefType, negate = false) {\n    const key = toKey(operator, negate);\n    const label = getOperatorLabel(operator, fieldDefType, negate);\n    return [key, label];\n}\n\nexport function getOperatorEditorInfo(operators, fieldDef) {\n    const defaultOperator = operators[0];\n    const operatorsInfo = operators.map((operator) => getOperatorInfo(operator, fieldDef?.type));\n    return {\n        component: Select,\n        extractProps: ({ update, value: [operator, negate] }) => {\n            const [operatorKey, operatorLabel] = getOperatorInfo(operator, fieldDef?.type, negate);\n            const options = [...operatorsInfo];\n            if (!options.some(([key]) => key === operatorKey)) {\n                options.push([operatorKey, operatorLabel]);\n            }\n            return {\n                value: operatorKey,\n                update: (operatorKey) => update(...toOperator(operatorKey)),\n                options,\n            };\n        },\n        defaultValue: () => defaultOperator,\n        isSupported: ([operator]) =>\n            typeof operator === \"string\" && operator in OPERATOR_DESCRIPTIONS, // should depend on fieldDef too... (e.g. parent_id does not always make sense)\n        message: _t(\"Operator not supported\"),\n        stringify: ([operator, negate]) => getOperatorLabel(operator, negate),\n    };\n}\n", "import {\n    deserializeDate,\n    deserializeDateTime,\n    serializeDate,\n    serializeDateTime,\n} from \"@web/core/l10n/dates\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"@web/core/registry\";\nimport { DateTimeInput } from \"@web/core/datetime/datetime_input\";\nimport {\n    DomainSelectorAutocomplete,\n    DomainSelectorSingleAutocomplete,\n} from \"@web/core/tree_editor/tree_editor_autocomplete\";\nimport { unique } from \"@web/core/utils/arrays\";\nimport { Input, Select, List, Range, Within } from \"@web/core/tree_editor/tree_editor_components\";\nimport { connector, formatValue, isTree } from \"@web/core/tree_editor/condition_tree\";\nimport { getResModel, disambiguate, isId } from \"@web/core/tree_editor/utils\";\nimport { Domain } from \"@web/core/domain\";\n\nconst { DateTime } = luxon;\n\n// ============================================================================\n\nconst formatters = registry.category(\"formatters\");\nconst parsers = registry.category(\"parsers\");\n\nfunction parseValue(fieldType, value) {\n    const parser = parsers.get(fieldType, (value) => value);\n    try {\n        return parser(value);\n    } catch {\n        return value;\n    }\n}\n\nfunction isParsable(fieldType, value) {\n    const parser = parsers.get(fieldType, (value) => value);\n    try {\n        parser(value);\n    } catch {\n        return false;\n    }\n    return true;\n}\n\nfunction genericSerializeDate(type, value) {\n    return type === \"date\" ? serializeDate(value) : serializeDateTime(value);\n}\n\nfunction genericDeserializeDate(type, value) {\n    return type === \"date\" ? deserializeDate(value) : deserializeDateTime(value);\n}\n\nconst STRING_EDITOR = {\n    component: Input,\n    extractProps: ({ value, update }) => ({ value, update }),\n    isSupported: (value) => typeof value === \"string\",\n    defaultValue: () => \"\",\n};\n\nfunction makeSelectEditor(options, params = {}) {\n    const getOption = (value) => options.find(([v]) => v === value) || null;\n    return {\n        component: Select,\n        extractProps: ({ value, update }) => ({\n            value,\n            update,\n            options,\n            addBlankOption: params.addBlankOption,\n        }),\n        isSupported: (value) => Boolean(getOption(value)),\n        defaultValue: () => options[0]?.[0] ?? false,\n        stringify: (value, disambiguate) => {\n            const option = getOption(value);\n            return option ? option[1] : disambiguate ? formatValue(value) : String(value);\n        },\n        message: _t(\"Value not in selection\"),\n    };\n}\n\nfunction getDomain(fieldDef) {\n    if (fieldDef.type === \"many2one\") {\n        return [];\n    }\n    try {\n        return new Domain(fieldDef.domain || []).toList();\n    } catch {\n        return [];\n    }\n}\n\nfunction makeAutoCompleteEditor(fieldDef) {\n    return {\n        component: DomainSelectorAutocomplete,\n        extractProps: ({ value, update }) => {\n            return {\n                resModel: getResModel(fieldDef),\n                fieldString: fieldDef.string,\n                domain: getDomain(fieldDef),\n                update: (value) => update(unique(value)),\n                resIds: unique(value),\n            };\n        },\n        isSupported: (value) => Array.isArray(value),\n        defaultValue: () => [],\n    };\n}\n\n// ============================================================================\n\nfunction getPartialValueEditorInfo(fieldDef, operator, params = {}) {\n    switch (operator) {\n        case \"set\":\n        case \"not_set\":\n            return {\n                component: null,\n                extractProps: null,\n                isSupported: (value) => value === false,\n                defaultValue: () => false,\n            };\n        case \"=like\":\n        case \"=ilike\":\n        case \"like\":\n        case \"not like\":\n        case \"ilike\":\n        case \"not ilike\":\n            return STRING_EDITOR;\n        case \"between\": {\n            const editorInfo = getValueEditorInfo(fieldDef, \"=\");\n            return {\n                component: Range,\n                extractProps: ({ value, update }) => ({\n                    value,\n                    update,\n                    editorInfo,\n                }),\n                isSupported: (value) => Array.isArray(value) && value.length === 2,\n                defaultValue: () => {\n                    const { defaultValue } = editorInfo;\n                    return [defaultValue(), defaultValue()];\n                },\n            };\n        }\n        case \"within\": {\n            return {\n                component: Within,\n                extractProps: ({ value, update }) => ({\n                    value,\n                    update,\n                    amountEditorInfo: getValueEditorInfo({ type: \"integer\" }, \"=\"),\n                    optionEditorInfo: makeSelectEditor(Within.options),\n                }),\n                isSupported: (value) =>\n                    Array.isArray(value) &&\n                    value.length === 3 &&\n                    typeof value[1] === \"string\" &&\n                    value[2] === fieldDef.type,\n                defaultValue: () => {\n                    return [-1, \"months\", fieldDef.type];\n                },\n            };\n        }\n        case \"in\":\n        case \"not in\": {\n            switch (fieldDef.type) {\n                case \"tags\":\n                    return STRING_EDITOR;\n                case \"many2one\":\n                case \"many2many\":\n                case \"one2many\":\n                    return makeAutoCompleteEditor(fieldDef);\n                default: {\n                    const editorInfo = getValueEditorInfo(fieldDef, \"=\", {\n                        addBlankOption: true,\n                        startEmpty: true,\n                    });\n                    return {\n                        component: List,\n                        extractProps: ({ value, update }) => {\n                            if (!disambiguate(value)) {\n                                const { stringify } = editorInfo;\n                                editorInfo.stringify = (val) => stringify(val, false);\n                            }\n                            return {\n                                value,\n                                update,\n                                editorInfo,\n                            };\n                        },\n                        isSupported: (value) => Array.isArray(value),\n                        defaultValue: () => [],\n                    };\n                }\n            }\n        }\n        case \"any\":\n        case \"not any\": {\n            switch (fieldDef.type) {\n                case \"many2one\":\n                case \"many2many\":\n                case \"one2many\": {\n                    return {\n                        component: null,\n                        extractProps: null,\n                        isSupported: isTree,\n                        defaultValue: () => connector(\"&\"),\n                    };\n                }\n            }\n        }\n    }\n\n    const { type } = fieldDef;\n    switch (type) {\n        case \"integer\":\n        case \"float\":\n        case \"monetary\": {\n            const formatType = type === \"integer\" ? \"integer\" : \"float\";\n            return {\n                component: Input,\n                extractProps: ({ value, update }) => ({\n                    value: String(value),\n                    update: (value) => update(parseValue(formatType, value)),\n                    startEmpty: params.startEmpty,\n                }),\n                isSupported: () => true,\n                defaultValue: () => 1,\n                shouldResetValue: (value) => parseValue(formatType, value) === value,\n            };\n        }\n        case \"date\":\n        case \"datetime\":\n            return {\n                component: DateTimeInput,\n                extractProps: ({ value, update }) => ({\n                    value:\n                        params.startEmpty || value === false\n                            ? false\n                            : genericDeserializeDate(type, value),\n                    type,\n                    onApply: (value) => {\n                        if (!params.startEmpty || value) {\n                            update(genericSerializeDate(type, value || DateTime.local()));\n                        }\n                    },\n                }),\n                isSupported: (value) =>\n                    value === false || (typeof value === \"string\" && isParsable(type, value)),\n                defaultValue: () => genericSerializeDate(type, DateTime.local()),\n                stringify: (value) => {\n                    if (value === false) {\n                        return _t(\"False\");\n                    }\n                    if (typeof value === \"string\" && isParsable(type, value)) {\n                        const formatter = formatters.get(type, formatValue);\n                        return formatter(genericDeserializeDate(type, value));\n                    }\n                    return formatValue(value);\n                },\n                message: _t(\"Not a valid %s\", type),\n            };\n        case \"char\":\n        case \"html\":\n        case \"text\":\n            return STRING_EDITOR;\n        case \"boolean\": {\n            if ([\"is\", \"is_not\"].includes(operator)) {\n                const options = [\n                    [true, _t(\"set\")],\n                    [false, _t(\"not set\")],\n                ];\n                return makeSelectEditor(options, params);\n            }\n            const options = [\n                [true, _t(\"True\")],\n                [false, _t(\"False\")],\n            ];\n            return makeSelectEditor(options, params);\n        }\n        case \"many2one\": {\n            if ([\"=\", \"!=\", \"parent_of\", \"child_of\"].includes(operator)) {\n                return {\n                    component: DomainSelectorSingleAutocomplete,\n                    extractProps: ({ value, update }) => {\n                        return {\n                            resModel: getResModel(fieldDef),\n                            fieldString: fieldDef.string,\n                            update,\n                            resId: value,\n                        };\n                    },\n                    isSupported: () => true,\n                    defaultValue: () => false,\n                    shouldResetValue: (value) => value !== false && !isId(value),\n                };\n            }\n            break;\n        }\n        case \"many2many\":\n        case \"one2many\":\n            if ([\"=\", \"!=\"].includes(operator)) {\n                return makeAutoCompleteEditor(fieldDef);\n            }\n            break;\n        case \"selection\": {\n            const options = fieldDef.selection || [];\n            return makeSelectEditor(options, params);\n        }\n        case undefined: {\n            const options = [[1, \"1\"]];\n            return makeSelectEditor(options, params);\n        }\n    }\n\n    // Global default for visualization mainly. It is there to visualize what\n    // has been produced in the debug textarea (in o_domain_selector_debug_container)\n    // It is hardly useful to produce a string in general.\n    return {\n        component: Input,\n        extractProps: ({ value, update }) => ({\n            value: String(value),\n            update,\n        }),\n        isSupported: () => true,\n        defaultValue: () => \"\",\n    };\n}\n\nexport function getValueEditorInfo(fieldDef, operator, options = {}) {\n    const info = getPartialValueEditorInfo(fieldDef || {}, operator, options);\n    return {\n        extractProps: ({ value, update }) => ({ value, update }),\n        message: _t(\"Value not supported\"),\n        stringify: (val, disambiguate = true) => {\n            if (disambiguate) {\n                return formatValue(val);\n            }\n            return String(val);\n        },\n        ...info,\n    };\n}\n\nexport function getDefaultValue(fieldDef, operator, value = null) {\n    const { isSupported, shouldResetValue, defaultValue } = getValueEditorInfo(fieldDef, operator);\n    if (value === null || !isSupported(value) || shouldResetValue?.(value)) {\n        return defaultValue();\n    }\n    return value;\n}\n", "import { unique, zip } from \"@web/core/utils/arrays\";\nimport { getOperatorLabel } from \"@web/core/tree_editor/tree_editor_operator_editor\";\nimport {\n    Expression,\n    condition,\n    createVirtualOperators,\n    normalizeValue,\n    isTree,\n} from \"@web/core/tree_editor/condition_tree\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport {\n    deserializeDate,\n    deserializeDateTime,\n    formatDate,\n    formatDateTime,\n} from \"@web/core/l10n/dates\";\nimport { useLoadFieldInfo, useLoadPathDescription } from \"@web/core/model_field_selector/utils\";\nimport { Within } from \"./tree_editor_components\";\n\n/**\n * @param {import(\"@web/core/tree_editor/condition_tree\").Value} val\n * @param {boolean} disambiguate\n * @param {Object|null} fieldDef\n * @param {Object} displayNames\n * @returns\n */\nfunction formatValue(val, disambiguate, fieldDef, displayNames) {\n    if (val instanceof Expression) {\n        return val.toString();\n    }\n    if (displayNames && isId(val)) {\n        if (typeof displayNames[val] === \"string\") {\n            val = displayNames[val];\n        } else {\n            return _t(\"Inaccessible/missing record ID: %s\", val);\n        }\n    }\n    if (fieldDef?.type === \"selection\") {\n        const [, label] = (fieldDef.selection || []).find(([v]) => v === val) || [];\n        if (label !== undefined) {\n            val = label;\n        }\n    }\n    if (typeof val === \"string\") {\n        if (fieldDef?.type === \"datetime\") {\n            return formatDateTime(deserializeDateTime(val));\n        }\n        if (fieldDef?.type === \"date\") {\n            return formatDate(deserializeDate(val));\n        }\n    }\n    if (disambiguate && typeof val === \"string\") {\n        return JSON.stringify(val);\n    }\n    return val;\n}\n\nexport function isId(value) {\n    return Number.isInteger(value) && value >= 1;\n}\n\nexport function disambiguate(value, displayNames) {\n    if (!Array.isArray(value)) {\n        return value === \"\";\n    }\n    let hasSomeString = false;\n    let hasSomethingElse = false;\n    for (const val of value) {\n        if (val === \"\") {\n            return true;\n        }\n        if (typeof val === \"string\" || (displayNames && isId(val))) {\n            hasSomeString = true;\n        } else {\n            hasSomethingElse = true;\n        }\n    }\n    return hasSomeString && hasSomethingElse;\n}\n\nexport function useMakeGetFieldDef(fieldService) {\n    fieldService ||= useService(\"field\");\n    const loadFieldInfo = useLoadFieldInfo(fieldService);\n    return async (resModel, tree, additionalsPath = []) => {\n        const pathsInTree = getPathsInTree(tree);\n        const paths = new Set([...pathsInTree, ...additionalsPath]);\n        const promises = [];\n        const fieldDefs = {};\n        for (const path of paths) {\n            if (typeof path === \"string\") {\n                promises.push(\n                    loadFieldInfo(resModel, path).then(({ fieldDef }) => {\n                        fieldDefs[path] = fieldDef;\n                    })\n                );\n            }\n        }\n        await Promise.all(promises);\n        return (path) => {\n            if (typeof path === \"string\") {\n                return fieldDefs[path];\n            }\n            return null;\n        };\n    };\n}\n\nfunction useGetTreePathDescription(fieldService) {\n    fieldService ||= useService(\"field\");\n    const loadPathDescription = useLoadPathDescription(fieldService);\n    return async (resModel, tree) => {\n        const paths = getPathsInTree(tree);\n        const promises = [];\n        const pathDescriptions = new Map();\n        for (const path of paths) {\n            promises.push(\n                loadPathDescription(resModel, path).then(({ displayNames }) => {\n                    pathDescriptions.set(path, displayNames.join(\" \\u2794 \"));\n                })\n            );\n        }\n        await Promise.all(promises);\n        return (path) => pathDescriptions.get(path);\n    };\n}\n\nasync function getDisplayNames(tree, getFieldDef, nameService) {\n    const resIdsByModel = extractIdsFromTree(tree, getFieldDef);\n    const proms = [];\n    const resModels = [];\n    for (const [resModel, resIds] of Object.entries(resIdsByModel)) {\n        resModels.push(resModel);\n        proms.push(nameService.loadDisplayNames(resModel, resIds));\n    }\n    return Object.fromEntries(zip(resModels, await Promise.all(proms)));\n}\n\nexport function useMakeGetConditionDescription(fieldService, nameService) {\n    const makeGetPathDescriptions = useGetTreePathDescription(fieldService);\n    return async (resModel, tree, getFieldDef) => {\n        tree = simplifyTree(tree);\n        const [displayNames, getPathDescription] = await Promise.all([\n            getDisplayNames(tree, getFieldDef, nameService),\n            makeGetPathDescriptions(resModel, tree),\n        ]);\n        return (node) =>\n            _getConditionDescription(node, getFieldDef, getPathDescription, displayNames);\n    };\n}\n\nfunction _getConditionDescription(node, getFieldDef, getPathDescription, displayNames) {\n    const nodeWithVirtualOperators = createVirtualOperators(node, { getFieldDef });\n    const { operator, negate, value, path } = nodeWithVirtualOperators;\n    const fieldDef = getFieldDef(path);\n    const operatorLabel = getOperatorLabel(operator, fieldDef?.type, negate);\n    const pathDescription = getPathDescription(path);\n    const description = {\n        pathDescription,\n        operatorDescription: operatorLabel,\n        valueDescription: null,\n    };\n\n    if (isTree(node.value)) {\n        return description;\n    }\n    if ([\"set\", \"not_set\"].includes(operator)) {\n        return description;\n    }\n    if ([\"is\", \"is_not\"].includes(operator)) {\n        description.valueDescription = {\n            values: [value ? _t(\"set\") : _t(\"not set\")],\n            join: \"\",\n            addParenthesis: false,\n        };\n        return description;\n    }\n\n    const coModeldisplayNames = displayNames[getResModel(fieldDef)];\n    const dis = disambiguate(value, coModeldisplayNames);\n    const values =\n        operator == \"within\"\n            ? [value[0], Within.options.find((option) => option[0] === value[1])[1]]\n            : (Array.isArray(value) ? value : [value])\n                  .slice(0, 21)\n                  .map((val, index) =>\n                      index < 20 ? formatValue(val, dis, fieldDef, coModeldisplayNames) : \"...\"\n                  );\n    let join;\n    let addParenthesis = Array.isArray(value);\n    switch (operator) {\n        case \"between\":\n            join = _t(\"and\");\n            addParenthesis = false;\n            break;\n        case \"within\":\n            join = \" \";\n            addParenthesis = false;\n            break;\n        case \"in\":\n        case \"not in\":\n            join = \",\";\n            break;\n        default:\n            join = _t(\"or\");\n    }\n    description.valueDescription = { values, join, addParenthesis };\n    return description;\n}\n\nexport function useGetTreeDescription(fieldService, nameService) {\n    fieldService ||= useService(\"field\");\n    nameService ||= useService(\"name\");\n    const makeGetFieldDef = useMakeGetFieldDef(fieldService);\n    const makeGetConditionDescription = useMakeGetConditionDescription(fieldService, nameService);\n    return async (resModel, tree) => {\n        async function getTreeDescription(resModel, tree, isSubExpression = false) {\n            tree = simplifyTree(tree);\n            if (tree.type === \"connector\") {\n                // we assume that the domain tree is normalized (--> there is at least two children)\n                const childDescriptions = tree.children.map((node) =>\n                    getTreeDescription(resModel, node, true)\n                );\n                const separator = tree.value === \"&\" ? _t(\"and\") : _t(\"or\");\n                let description = await Promise.all(childDescriptions);\n                description = description.join(` ${separator} `);\n                if (isSubExpression || tree.negate) {\n                    description = `( ${description} )`;\n                }\n                if (tree.negate) {\n                    description = `! ${description}`;\n                }\n                return description;\n            }\n            const getFieldDef = await makeGetFieldDef(resModel, tree);\n            const getConditionDescription = await makeGetConditionDescription(\n                resModel,\n                tree,\n                getFieldDef\n            );\n            const { pathDescription, operatorDescription, valueDescription } =\n                getConditionDescription(tree);\n            const stringDescription = [pathDescription, operatorDescription];\n            if (valueDescription) {\n                const { values, join, addParenthesis } = valueDescription;\n                const jointedValues = values.join(` ${join} `);\n                stringDescription.push(addParenthesis ? `( ${jointedValues} )` : jointedValues);\n            } else if (isTree(tree.value)) {\n                const _fieldDef = getFieldDef(tree.path);\n                const _resModel = getResModel(_fieldDef);\n                const _tree = tree.value;\n                const description = await getTreeDescription(_resModel, _tree);\n                stringDescription.push(`( ${description} )`);\n            }\n            return stringDescription.join(\" \");\n        }\n        return getTreeDescription(resModel, tree);\n    };\n}\n\nexport function getResModel(fieldDef) {\n    if (fieldDef) {\n        return fieldDef.is_property ? fieldDef.comodel : fieldDef.relation;\n    }\n    return null;\n}\n\nfunction extractIdsFromTree(tree, getFieldDef) {\n    const idsByModel = _extractIdsRecursive(tree, getFieldDef, {});\n\n    for (const resModel in idsByModel) {\n        idsByModel[resModel] = unique(idsByModel[resModel]);\n    }\n\n    return idsByModel;\n}\n\nfunction _extractIdsRecursive(tree, getFieldDef, idsByModel) {\n    if (tree.type === \"condition\") {\n        const fieldDef = getFieldDef(tree.path);\n        if ([\"many2one\", \"many2many\", \"one2many\"].includes(fieldDef?.type)) {\n            const value = tree.value;\n            const values = Array.isArray(value) ? value : [value];\n            const ids = values.filter((val) => isId(val));\n            const resModel = getResModel(fieldDef);\n            if (ids.length) {\n                if (!idsByModel[resModel]) {\n                    idsByModel[resModel] = [];\n                }\n                idsByModel[resModel].push(...ids);\n            }\n        }\n    }\n    if (tree.type === \"connector\") {\n        for (const child of tree.children) {\n            _extractIdsRecursive(child, getFieldDef, idsByModel);\n        }\n    }\n    return idsByModel;\n}\n\nexport function getPathsInTree(tree) {\n    const paths = [];\n    if (tree.type === \"condition\") {\n        paths.push(tree.path);\n    }\n    if (tree.type === \"connector\" && tree.children) {\n        for (const child of tree.children) {\n            paths.push(...getPathsInTree(child));\n        }\n    }\n    return unique(paths);\n}\n\nconst SPECIAL_FIELDS = [\"country_id\", \"user_id\", \"partner_id\", \"stage_id\", \"id\"];\n\nexport function getDefaultPath(fieldDefs) {\n    for (const name of SPECIAL_FIELDS) {\n        const fieldDef = fieldDefs[name];\n        if (fieldDef) {\n            return fieldDef.name;\n        }\n    }\n    const name = Object.keys(fieldDefs)[0];\n    if (name) {\n        return name;\n    }\n    throw new Error(`No field found`);\n}\n\n/**\n * @param {Tree} tree\n * @returns {tree}\n */\nfunction simplifyTree(tree) {\n    if (tree.type === \"condition\") {\n        return tree;\n    }\n    const processedChildren = tree.children.map(simplifyTree);\n    if (tree.value === \"&\") {\n        return { ...tree, children: processedChildren };\n    }\n    const children = [];\n    const childrenByPath = {};\n    for (const child of processedChildren) {\n        if (\n            child.type === \"connector\" ||\n            typeof child.path !== \"string\" ||\n            ![\"=\", \"in\"].includes(child.operator)\n        ) {\n            children.push(child);\n        } else {\n            if (!childrenByPath[child.path]) {\n                childrenByPath[child.path] = [];\n            }\n            childrenByPath[child.path].push(child);\n        }\n    }\n    for (const path in childrenByPath) {\n        if (childrenByPath[path].length === 1) {\n            children.push(childrenByPath[path][0]);\n            continue;\n        }\n        const value = [];\n        for (const child of childrenByPath[path]) {\n            if (child.operator === \"=\") {\n                value.push(child.value);\n            } else {\n                value.push(...child.value);\n            }\n        }\n        children.push(condition(path, \"in\", normalizeValue(value)));\n    }\n    if (children.length === 1) {\n        return { ...children[0] };\n    }\n    return { ...tree, children };\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { browser } from \"@web/core/browser/browser\";\n\nimport { EventBus, Component, useState, xml } from \"@odoo/owl\";\n\nexport class BlockUI extends Component {\n    static props = {\n        bus: EventBus,\n    };\n\n    static template = xml`\n        <t t-if=\"state.blockState === BLOCK_STATES.UNBLOCKED\">\n            <div/>\n        </t>\n        <t t-else=\"\">\n            <t t-set=\"visiblyBlocked\" t-value=\"state.blockState === BLOCK_STATES.VISIBLY_BLOCKED\"/>\n            <div class=\"o_blockUI fixed-top d-flex justify-content-center align-items-center flex-column vh-100\"\n                 t-att-class=\"visiblyBlocked ? '' : 'o_blockUI_invisible'\">\n                <t t-if=\"visiblyBlocked\">\n                    <div class=\"o_spinner mb-4\">\n                        <img src=\"/web/static/img/spin.svg\" alt=\"Loading...\"/>\n                    </div>\n                    <div class=\"o_message text-center px-4\">\n                        <t t-esc=\"state.line1\"/><br/>\n                        <t t-esc=\"state.line2\"/>\n                    </div>\n                </t>\n            </div>\n        </t>\n    `;\n\n    setup() {\n        this.messagesByDuration = [\n            { time: 20, l1: _t(\"Loading...\") },\n            { time: 40, l1: _t(\"Still loading...\") },\n            {\n                time: 60,\n                l1: _t(\"Still loading...\"),\n                l2: _t(\"Please be patient.\"),\n            },\n            {\n                time: 180,\n                l1: _t(\"Don't leave yet,\"),\n                l2: _t(\"it's still loading...\"),\n            },\n            {\n                time: 120,\n                l1: _t(\"You may not believe it,\"),\n                l2: _t(\"but the application is actually loading...\"),\n            },\n            {\n                time: 3180,\n                l1: _t(\"Take a minute to get a coffee,\"),\n                l2: _t(\"because it's loading...\"),\n            },\n            {\n                time: null,\n                l1: _t(\"Maybe you should consider reloading the application by pressing F5...\"),\n            },\n        ];\n        this.BLOCK_STATES = { UNBLOCKED: 0, BLOCKED: 1, VISIBLY_BLOCKED: 2 };\n        this.state = useState({\n            blockState: this.BLOCK_STATES.UNBLOCKED,\n            line1: \"\",\n            line2: \"\",\n        });\n\n        this.props.bus.addEventListener(\"BLOCK\", this.block.bind(this));\n        this.props.bus.addEventListener(\"UNBLOCK\", this.unblock.bind(this));\n    }\n\n    replaceMessage(index) {\n        const message = this.messagesByDuration[index];\n        this.state.line1 = message.l1;\n        this.state.line2 = message.l2 || \"\";\n        if (message.time !== null) {\n            this.msgTimer = browser.setTimeout(() => {\n                this.replaceMessage(index + 1);\n            }, message.time * 1000);\n        }\n    }\n\n    block(ev) {\n        const showBlockedUI = () => (this.state.blockState = this.BLOCK_STATES.VISIBLY_BLOCKED);\n        const delay = ev.detail?.delay;\n        if (delay) {\n            this.state.blockState = this.BLOCK_STATES.BLOCKED;\n            this.showBlockedUITimer = setTimeout(showBlockedUI, delay);\n        } else {\n            showBlockedUI();\n        }\n\n        if (ev.detail?.message) {\n            this.state.line1 = ev.detail.message;\n        } else {\n            this.replaceMessage(0);\n        }\n    }\n\n    unblock() {\n        this.state.blockState = this.BLOCK_STATES.UNBLOCKED;\n        clearTimeout(this.showBlockedUITimer);\n        clearTimeout(this.msgTimer);\n        this.state.line1 = \"\";\n        this.state.line2 = \"\";\n    }\n}\n", "import { useService } from \"@web/core/utils/hooks\";\nimport { registry } from \"@web/core/registry\";\nimport { throttleForAnimation } from \"@web/core/utils/timing\";\nimport { BlockUI } from \"./block_ui\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { getTabableElements } from \"@web/core/utils/ui\";\nimport { getActiveHotkey } from \"../hotkeys/hotkey_service\";\n\nimport { EventBus, reactive, useEffect, useRef } from \"@odoo/owl\";\n\nexport const SIZES = { XS: 0, VSM: 1, SM: 2, MD: 3, LG: 4, XL: 5, XXL: 6 };\n\nfunction getFirstAndLastTabableElements(el) {\n    const tabableEls = getTabableElements(el);\n    return [tabableEls[0], tabableEls[tabableEls.length - 1]];\n}\n\n/**\n * This hook will set the UI active element\n * when the caller component will mount/patch and\n * only if the t-reffed element has some tabable elements.\n *\n * The caller component could pass a `t-ref` value of its template\n * to delegate the UI active element to another element than itself.\n *\n * @param {string} refName\n */\nexport function useActiveElement(refName) {\n    if (!refName) {\n        throw new Error(\"refName not given to useActiveElement\");\n    }\n    const uiService = useService(\"ui\");\n    const ref = useRef(refName);\n\n    function trapFocus(e) {\n        const hotkey = getActiveHotkey(e);\n        if (![\"tab\", \"shift+tab\"].includes(hotkey)) {\n            return;\n        }\n        const el = e.currentTarget;\n        const [firstTabableEl, lastTabableEl] = getFirstAndLastTabableElements(el);\n        switch (hotkey) {\n            case \"tab\":\n                if (document.activeElement === lastTabableEl) {\n                    firstTabableEl.focus();\n                    e.preventDefault();\n                    e.stopPropagation();\n                }\n                break;\n            case \"shift+tab\":\n                if (document.activeElement === firstTabableEl) {\n                    lastTabableEl.focus();\n                    e.preventDefault();\n                    e.stopPropagation();\n                }\n                break;\n        }\n    }\n\n    useEffect(\n        (el) => {\n            if (el) {\n                const [firstTabableEl] = getFirstAndLastTabableElements(el);\n                if (!firstTabableEl) {\n                    // no tabable elements: no need to trap focus nor become the UI active element\n                    return;\n                }\n                const oldActiveElement = document.activeElement;\n                uiService.activateElement(el);\n\n                el.addEventListener(\"keydown\", trapFocus);\n\n                if (!el.contains(document.activeElement)) {\n                    firstTabableEl.focus();\n                }\n                return () => {\n                    uiService.deactivateElement(el);\n                    el.removeEventListener(\"keydown\", trapFocus);\n\n                    /**\n                     * In some cases, the current active element is not\n                     * anymore in el (e.g. with ConfirmationDialog, the\n                     * confirm button is disabled when clicked, so the\n                     * focus is lost). In that case, we also want to restore\n                     * the focus to the previous active element so we\n                     * check if the current active element is the body\n                     */\n                    if (\n                        el.contains(document.activeElement) ||\n                        document.activeElement === document.body\n                    ) {\n                        oldActiveElement.focus();\n                    }\n                };\n            }\n        },\n        () => [ref.el]\n    );\n}\n\n// window size handling\nexport const MEDIAS_BREAKPOINTS = [\n    { maxWidth: 474 },\n    { minWidth: 475, maxWidth: 575 },\n    { minWidth: 576, maxWidth: 767 },\n    { minWidth: 768, maxWidth: 991 },\n    { minWidth: 992, maxWidth: 1199 },\n    { minWidth: 1200, maxWidth: 1533 },\n    { minWidth: 1534 },\n];\n\n/**\n * Create the MediaQueryList used both by the uiService and config from\n * `MEDIA_BREAKPOINTS`.\n *\n * @returns {MediaQueryList[]}\n */\nexport function getMediaQueryLists() {\n    return MEDIAS_BREAKPOINTS.map(({ minWidth, maxWidth }) => {\n        if (!maxWidth) {\n            return window.matchMedia(`(min-width: ${minWidth}px)`);\n        }\n        if (!minWidth) {\n            return window.matchMedia(`(max-width: ${maxWidth}px)`);\n        }\n        return window.matchMedia(`(min-width: ${minWidth}px) and (max-width: ${maxWidth}px)`);\n    });\n}\n\n// window size handling.\nconst MEDIAS = getMediaQueryLists();\n\nexport const utils = {\n    getSize() {\n        return MEDIAS.findIndex((media) => media.matches);\n    },\n    isSmall(ui = {}) {\n        return (ui.size || utils.getSize()) <= SIZES.SM;\n    },\n};\n\nconst bus = new EventBus();\n\nexport function listenSizeChange(callback) {\n    bus.addEventListener(\"resize\", callback);\n    return () => bus.removeEventListener(\"resize\", callback);\n}\n\nexport const uiService = {\n    start(env) {\n        // block/unblock code\n        registry.category(\"main_components\").add(\"BlockUI\", { Component: BlockUI, props: { bus } });\n\n        let blockCount = 0;\n        function block(data) {\n            blockCount++;\n            // TODO could probably be improved to handle multiple block demands\n            // but that have different messages and delays\n            if (blockCount === 1) {\n                bus.trigger(\"BLOCK\", {\n                    message: data?.message,\n                    delay: data?.delay,\n                });\n            }\n        }\n        function unblock() {\n            blockCount--;\n            if (blockCount < 0) {\n                console.warn(\n                    \"Unblock ui was called more times than block, you should only unblock the UI if you have previously blocked it.\"\n                );\n                blockCount = 0;\n            }\n            if (blockCount === 0) {\n                bus.trigger(\"UNBLOCK\");\n            }\n        }\n\n        // UI active element code\n        let activeElems = [document];\n\n        function activateElement(el) {\n            activeElems.push(el);\n            bus.trigger(\"active-element-changed\", el);\n        }\n        function deactivateElement(el) {\n            activeElems = activeElems.filter((x) => x !== el);\n            bus.trigger(\"active-element-changed\", ui.activeElement);\n        }\n        function getActiveElementOf(el) {\n            for (const activeElement of [...activeElems].reverse()) {\n                if (activeElement.contains(el)) {\n                    return activeElement;\n                }\n            }\n        }\n\n        const ui = reactive({\n            bus,\n            size: utils.getSize(),\n            get activeElement() {\n                return activeElems[activeElems.length - 1];\n            },\n            get isBlocked() {\n                return blockCount > 0;\n            },\n            isSmall: utils.isSmall(),\n            block,\n            unblock,\n            activateElement,\n            deactivateElement,\n            getActiveElementOf,\n        });\n\n        // listen to media query status changes\n        const updateSize = () => {\n            const prevSize = ui.size;\n            ui.size = utils.getSize();\n            if (ui.size !== prevSize) {\n                ui.isSmall = utils.isSmall(ui);\n                bus.trigger(\"resize\");\n            }\n        };\n        browser.addEventListener(\"resize\", throttleForAnimation(updateSize));\n\n        Object.defineProperty(env, \"isSmall\", {\n            get() {\n                return ui.isSmall;\n            },\n        });\n\n        return ui;\n    },\n};\n\nregistry.category(\"services\").add(\"ui\", uiService);\n", "import { browser } from \"@web/core/browser/browser\";\nimport { pyToJsLocale } from \"@web/core/l10n/utils/locales\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { Cache } from \"@web/core/utils/cache\";\nimport { session } from \"@web/session\";\nimport { ensureArray } from \"./utils/arrays\";\n\n// This file exports an object containing user-related information and functions\n// allowing to obtain/alter user-related information from the server.\n\n/**\n * This function exists for testing purposes. We don't want tests to share the\n * same cache. It allows to generate new caches at the beginning of tests.\n *\n * Note: with hoot, this will no longer be necessary.\n *\n * @returns Object\n */\nexport function _makeUser(session) {\n    // Retrieve user-related information from the session\n    const {\n        home_action_id: homeActionId,\n        is_admin: isAdmin,\n        is_internal_user: isInternalUser,\n        is_system: isSystem,\n        name,\n        partner_id: partnerId,\n        show_effect: showEffect,\n        uid: userId,\n        username: login,\n        user_context: context,\n        user_settings,\n        partner_write_date: writeDate,\n    } = session;\n    const settings = user_settings || {};\n\n    // Delete user-related information from the session, s.t. there's a single source of truth\n    delete session.home_action_id;\n    delete session.is_admin;\n    delete session.is_internal_user;\n    delete session.is_system;\n    delete session.name;\n    delete session.partner_id;\n    delete session.show_effect;\n    delete session.uid;\n    delete session.username;\n    delete session.user_context;\n    delete session.user_settings;\n    delete session.partner_write_date;\n\n    // Generate caches for has_group and has_access calls\n    const getGroupCacheValue = (group, context) => {\n        if (!userId) {\n            return Promise.resolve(false);\n        }\n        return rpc(\"/web/dataset/call_kw/res.users/has_group\", {\n            model: \"res.users\",\n            method: \"has_group\",\n            args: [userId, group],\n            kwargs: { context },\n        });\n    };\n    const getGroupCacheKey = (group) => group;\n    const groupCache = new Cache(getGroupCacheValue, getGroupCacheKey);\n    if (isInternalUser !== undefined) {\n        groupCache.cache[\"base.group_user\"] = Promise.resolve(isInternalUser);\n    }\n    if (isSystem !== undefined) {\n        groupCache.cache[\"base.group_system\"] = Promise.resolve(isSystem);\n    }\n    const getAccessRightCacheValue = (model, operation, ids, context) => {\n        const url = `/web/dataset/call_kw/${model}/has_access`;\n        return rpc(url, {\n            model,\n            method: \"has_access\",\n            args: [ids, operation],\n            kwargs: { context },\n        });\n    };\n    const getAccessRightCacheKey = (model, operation, ids) =>\n        JSON.stringify([model, operation, ids]);\n    const accessRightCache = new Cache(getAccessRightCacheValue, getAccessRightCacheKey);\n    const lang = pyToJsLocale(context?.lang);\n\n    const user = {\n        name,\n        login,\n        isAdmin,\n        isSystem,\n        partnerId,\n        homeActionId,\n        showEffect,\n        userId, // TODO: rename into id?\n        writeDate,\n        get context() {\n            return Object.assign({}, context, { uid: this.userId });\n        },\n        get lang() {\n            return lang;\n        },\n        get tz() {\n            return this.context.tz;\n        },\n        get settings() {\n            return Object.assign({}, settings);\n        },\n        updateContext(update) {\n            Object.assign(context, update);\n        },\n        hasGroup(group) {\n            return groupCache.read(group, this.context);\n        },\n        checkAccessRight(model, operation, ids = []) {\n            return accessRightCache.read(model, operation, ensureArray(ids), this.context);\n        },\n        async setUserSettings(key, value) {\n            const model = \"res.users.settings\";\n            const method = \"set_res_users_settings\";\n            const changedSettings = await rpc(`/web/dataset/call_kw/${model}/${method}`, {\n                model,\n                method,\n                args: [[this.settings.id]],\n                kwargs: {\n                    new_settings: {\n                        [key]: value,\n                    },\n                    context: this.context,\n                },\n            });\n            Object.assign(settings, changedSettings);\n        },\n    };\n\n    return user;\n}\n\nexport const user = _makeUser(session);\n\nconst LAST_CONNECTED_USER_KEY = \"web.lastConnectedUser\";\n\nexport const getLastConnectedUsers = () => {\n    const lastConnectedUsers = browser.localStorage.getItem(LAST_CONNECTED_USER_KEY);\n    return lastConnectedUsers ? JSON.parse(lastConnectedUsers) : [];\n};\n\nexport const setLastConnectedUsers = (users) => {\n    browser.localStorage.setItem(LAST_CONNECTED_USER_KEY, JSON.stringify(users.slice(0, 5)));\n};\n\nif (user.login && user.login !== \"__system__\") {\n    const users = getLastConnectedUsers();\n    const lastConnectedUsers = [\n        {\n            login: user.login,\n            name: user.name,\n            partnerId: user.partnerId,\n            partnerWriteDate: user.writeDate,\n            userId: user.userId,\n        },\n        ...users.filter((u) => u.userId !== user.userId),\n    ];\n    setLastConnectedUsers(lastConnectedUsers);\n}\n", "import { Component, useRef, useState, useEffect } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\nimport { getLastConnectedUsers, setLastConnectedUsers } from \"@web/core/user\";\nimport { imageUrl } from \"@web/core/utils/urls\";\n\nexport class UserSwitch extends Component {\n    static template = \"web.login_user_switch\";\n    static props = {};\n\n    setup() {\n        const users = getLastConnectedUsers();\n        this.root = useRef(\"root\");\n        this.state = useState({\n            users,\n            displayUserChoice: users.length > 1,\n        });\n        this.form = document.querySelector(\"form.oe_login_form\");\n        this.form.classList.toggle(\"d-none\", users.length > 1);\n        this.form.querySelector(\":placeholder-shown\")?.focus();\n        useEffect(\n            (el) => el?.querySelector(\"button.list-group-item-action\")?.focus(),\n            () => [this.root.el]\n        );\n    }\n\n    toggleFormDisplay() {\n        this.state.displayUserChoice = !this.state.displayUserChoice && this.state.users.length;\n        this.form.classList.toggle(\"d-none\", this.state.displayUserChoice);\n        this.form.querySelector(\":placeholder-shown\")?.focus();\n    }\n\n    getAvatarUrl({ partnerId, partnerWriteDate: unique }) {\n        return imageUrl(\"res.partner\", partnerId, \"avatar_128\", { unique });\n    }\n\n    remove(deletedUser) {\n        this.state.users = this.state.users.filter((user) => user !== deletedUser);\n        setLastConnectedUsers(this.state.users);\n        if (!this.state.users.length) {\n            this.fillForm();\n        }\n    }\n\n    fillForm(login = \"\") {\n        this.form.querySelector(\"input#login\").value = login;\n        this.form.querySelector(\"input#password\").value = \"\";\n        this.toggleFormDisplay();\n    }\n}\n\nregistry.category(\"public_components\").add(\"web.user_switch\", UserSwitch);\n", "import { shallowEqual as _shallowEqual } from \"./objects\";\n\n/**\n * @template T\n * @template {string | number | symbol} K\n * @typedef {keyof T | ((item: T) => K)} Criterion\n */\n\n/**\n * Same values returned as those returned by cartesian function for case n = 0\n * and n > 1. For n = 1, brackets are put around the unique parameter elements.\n *\n * @template T\n * @param {...T[]} args\n * @returns {T[][]}\n */\nfunction _cartesian(...args) {\n    if (args.length === 0) {\n        return [undefined];\n    }\n    const firstArray = args.shift().map((elem) => [elem]);\n    if (args.length === 0) {\n        return firstArray;\n    }\n    const result = [];\n    const productOfOtherArrays = _cartesian(...args);\n    for (const array of firstArray) {\n        for (const tuple of productOfOtherArrays) {\n            result.push([...array, ...tuple]);\n        }\n    }\n    return result;\n}\n\n/**\n * Helper function returning an extraction handler to use on array elements to\n * return a certain attribute or mutated form of the element.\n *\n * @private\n * @template T\n * @template {string | number | symbol} K\n * @param {Criterion<T, K>} [criterion]\n * @returns {(element: T) => any}\n */\nfunction _getExtractorFrom(criterion) {\n    if (criterion) {\n        switch (typeof criterion) {\n            case \"string\":\n                return (element) => element[criterion];\n            case \"function\":\n                return criterion;\n            default:\n                throw new Error(\n                    `Expected criterion of type 'string' or 'function' and got '${typeof criterion}'`\n                );\n        }\n    } else {\n        return (element) => element;\n    }\n}\n\n/**\n * Returns an array containing either:\n * - the elements contained in the given iterable OR\n * - the given element if it is not an iterable\n *\n * @template T\n * @param {T | Iterable<T>} [value]\n * @returns {T[]}\n */\nexport function ensureArray(value) {\n    return isIterable(value) ? [...value] : [value];\n}\n\n/**\n * Returns the array of elements contained in both arrays.\n *\n * @template T\n * @param {Iterable<T>} iter1\n * @param {Iterable<T>} iter2\n * @returns {T[]}\n */\nexport function intersection(iter1, iter2) {\n    const set2 = new Set(iter2);\n    return unique(iter1).filter((v) => set2.has(v));\n}\n\n/**\n * Returns whether the given value is an iterable object (excluding strings).\n *\n * @param {unknown} value\n */\nexport function isIterable(value) {\n    return Boolean(value && typeof value === \"object\" && value[Symbol.iterator]);\n}\n\n/**\n * Returns an object holding different groups defined by a given criterion\n * or a default one. Each group is a subset of the original given list.\n * The given criterion can either be:\n * - a string: a property name on the list elements which value will be the\n * group name,\n * - a function: a handler that will return the group name from a given\n * element.\n *\n * @template T\n * @template {string | number | symbol} K\n * @param {Iterable<T>} iterable\n * @param {Criterion<T, K>} [criterion]\n * @returns {Record<K, T[]>}\n */\nexport function groupBy(iterable, criterion) {\n    const extract = _getExtractorFrom(criterion);\n    /** @type {Partial<Record<K, T[]>>} */\n    const groups = {};\n    for (const element of iterable) {\n        const group = String(extract(element));\n        if (!(group in groups)) {\n            groups[group] = [];\n        }\n        groups[group].push(element);\n    }\n    return groups;\n}\n\n/**\n * Return a shallow copy of a given array sorted by a given criterion or a default one.\n * The given criterion can either be:\n * - a string: a property name on the array elements returning the sortable primitive\n * - a function: a handler that will return the sortable primitive from a given element.\n * The default order is ascending ('asc'). It can be modified by setting the extra param 'order' to 'desc'.\n *\n * @template T\n * @template {string | number | symbol} K\n * @param {Iterable<T>} iterable\n * @param {Criterion<T, K>} [criterion]\n * @param {\"asc\" | \"desc\"} [order=\"asc\"]\n * @returns {T[]}\n */\nexport function sortBy(iterable, criterion, order = \"asc\") {\n    const extract = _getExtractorFrom(criterion);\n    return [...iterable].sort((elA, elB) => {\n        const a = extract(elA);\n        const b = extract(elB);\n        let result;\n        if (isNaN(a) && isNaN(b)) {\n            result = a > b ? 1 : a < b ? -1 : 0;\n        } else {\n            result = a - b;\n        }\n        return order === \"asc\" ? result : -result;\n    });\n}\n\n/**\n * Returns an array containing all the elements of arrayA\n * that are not in arrayB and vice-versa.\n *\n * @template T\n * @param {Iterable<T>} iter1\n * @param {Iterable<T>} iter2\n * @returns {T[]} an array containing all the elements of iter1\n * that are not in iter2 and vice-versa.\n */\nexport function symmetricalDifference(iter1, iter2) {\n    const array1 = [...iter1];\n    const array2 = [...iter2];\n    return [\n        ...array1.filter((value) => !array2.includes(value)),\n        ...array2.filter((value) => !array1.includes(value)),\n    ];\n}\n\n/**\n * Returns the product of any number n of arrays.\n * The internal structures of their elements is preserved.\n * For n = 1, no brackets are put around the unique parameter elements\n * For n = 0, [undefined] is returned since it is the unit\n * of the cartesian product (up to isomorphism).\n *\n * @template T\n * @param {...T[]} args\n * @returns {T[] | T[][]}\n */\nexport function cartesian(...args) {\n    if (args.length === 0) {\n        return [undefined];\n    } else if (args.length === 1) {\n        return args[0];\n    } else {\n        return _cartesian(...args);\n    }\n}\n\nexport const shallowEqual = _shallowEqual;\n\n/**\n * Returns all initial sections of a given array, e.g. for [1, 2] the array\n * [[], [1], [1, 2]] is returned.\n *\n * @template T\n * @param {Iterable<T>} iterable\n * @returns {T[][]}\n */\nexport function sections(iterable) {\n    const array = [...iterable];\n    const sections = [];\n    for (let i = 0; i < array.length + 1; i++) {\n        sections.push(array.slice(0, i));\n    }\n    return sections;\n}\n\n/**\n * Returns an array containing all elements of the given\n * array but without duplicates.\n *\n * @template T\n * @param {Iterable<T>} iterable\n * @returns {T[]}\n */\nexport function unique(iterable) {\n    return [...new Set(iterable)];\n}\n\n/**\n * @template T1, T2\n * @param {Iterable<T1>} iter1\n * @param {Iterable<T2>} iter2\n * @param {boolean} [fill=false]\n * @returns {[T1, T2][]}\n */\nexport function zip(iter1, iter2, fill = false) {\n    const array1 = [...iter1];\n    const array2 = [...iter2];\n    /** @type {[T1, T2][]} */\n    const result = [];\n    const getLength = fill ? Math.max : Math.min;\n    for (let i = 0; i < getLength(array1.length, array2.length); i++) {\n        result.push([array1[i], array2[i]]);\n    }\n    return result;\n}\n\n/**\n * @template T1, T2, T\n * @param {Iterable<T1>} iter1\n * @param {Iterable<T2>} iter2\n * @param {(e1: T1, e2: T2) => T} mapFn\n * @returns {T[]}\n */\nexport function zipWith(iter1, iter2, mapFn) {\n    return zip(iter1, iter2).map(([e1, e2]) => mapFn(e1, e2));\n}\n/**\n * Creates an sliding window over an array of a given width. Eg:\n * slidingWindow([1, 2, 3, 4], 2) => [[1, 2], [2, 3], [3, 4]]\n *\n * @template T\n * @param {T[]} arr the array over which to create a sliding window\n * @param {number} width the width of the window\n * @returns {T[][]} an array of tuples of size width\n */\nexport function slidingWindow(arr, width) {\n    const res = [];\n    for (let i = 0; i <= arr.length - width; i++) {\n        res.push(arr.slice(i, i + width));\n    }\n    return res;\n}\n\nexport function rotate(i, arr, inc = 1) {\n    return (arr.length + i + inc) % arr.length;\n}\n", "import { useEffect } from \"@odoo/owl\";\nimport { browser } from \"../browser/browser\";\n\n/**\n * This is used on text inputs or textareas to automatically resize it based on its\n * content each time it is updated. It takes the reference of the element as\n * parameter and some options. Do note that it may introduce mild performance issues\n * since it will force a reflow of the layout each time the element is updated.\n * Do also note that it only works with textareas that are nested as only child\n * of some parent div (like in the text_field component).\n *\n * @param {Ref} ref\n */\nexport function useAutoresize(ref, options = {}) {\n    let wasProgrammaticallyResized = false;\n    let resize = null;\n    useEffect(\n        (el) => {\n            if (el) {\n                resize = (programmaticResize = false) => {\n                    wasProgrammaticallyResized = programmaticResize;\n                    if (el instanceof HTMLInputElement) {\n                        resizeInput(el, options);\n                    } else {\n                        resizeTextArea(el, options);\n                    }\n                    options.onResize?.(el, options);\n                };\n                el.addEventListener(\"input\", () => resize(true));\n                const resizeObserver = new ResizeObserver(() => {\n                    // This ensures that the resize function is not called twice on input or page load\n                    if (wasProgrammaticallyResized) {\n                        wasProgrammaticallyResized = false;\n                        return;\n                    }\n                    resize();\n                });\n                resizeObserver.observe(el);\n                return () => {\n                    el.removeEventListener(\"input\", resize);\n                    resizeObserver.unobserve(el);\n                    resizeObserver.disconnect();\n                    resize = null;\n                };\n            }\n        },\n        () => [ref.el]\n    );\n    useEffect(() => {\n        if (resize) {\n            resize(true);\n        }\n    });\n}\n\nfunction resizeInput(input) {\n    // This mesures the maximum width of the input which can get from the flex layout.\n    input.style.width = \"100%\";\n    const maxWidth = input.clientWidth;\n    // Somehow Safari 16 computes input sizes incorrectly. This is fixed in Safari 17\n    const isSafari16 = /Version\\/16.+Safari/i.test(browser.navigator.userAgent);\n    // Minimum width of the input\n    input.style.width = \"10px\";\n    if (input.value === \"\" && input.placeholder !== \"\") {\n        input.style.width = \"auto\";\n        return;\n    }\n    if (input.scrollWidth + 5 + (isSafari16 ? 8 : 0) > maxWidth) {\n        input.style.width = \"100%\";\n        return;\n    }\n    input.style.width = input.scrollWidth + 5 + (isSafari16 ? 8 : 0) + \"px\";\n}\n\nexport function resizeTextArea(textarea, options = {}) {\n    const minimumHeight = options.minimumHeight || 0;\n    let heightOffset = 0;\n    const style = window.getComputedStyle(textarea);\n    if (style.boxSizing === \"border-box\") {\n        const paddingHeight = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);\n        const borderHeight = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);\n        heightOffset = borderHeight + paddingHeight;\n    }\n    const previousStyle = {\n        borderTopWidth: style.borderTopWidth,\n        borderBottomWidth: style.borderBottomWidth,\n        padding: style.padding,\n    };\n    Object.assign(textarea.style, {\n        height: \"auto\",\n        borderTopWidth: 0,\n        borderBottomWidth: 0,\n        paddingTop: 0,\n        paddingRight: style.paddingRight,\n        paddingBottom: 0,\n        paddingLeft: style.paddingLeft,\n    });\n    textarea.style.height = \"auto\";\n    const height = Math.max(minimumHeight, textarea.scrollHeight + heightOffset);\n    Object.assign(textarea.style, previousStyle, { height: `${height}px` });\n    textarea.parentElement.style.height = `${height}px`;\n}\n", "import { _t } from \"@web/core/l10n/translation\";\n\n/**\n * @param {string} value\n * @returns {boolean}\n */\nexport function isBinarySize(value) {\n    return /^\\d+(\\.\\d*)? [^0-9]+$/.test(value);\n}\n\n/**\n * Get the length necessary for a base64 str to encode maxBytes\n * @param {number} maxBytes number of bytes we want to encode in base64\n * @returns {number} number of char\n */\nexport function toBase64Length(maxBytes) {\n    return Math.ceil(maxBytes * 4 / 3);\n}\n\n/**\n * @param {number} size number of bytes\n * @param {string}\n */\nexport function humanSize(size) {\n    const units = _t(\"Bytes|Kb|Mb|Gb|Tb|Pb|Eb|Zb|Yb\").split(\"|\");\n    let i = 0;\n    while (size >= 1024) {\n        size /= 1024;\n        ++i;\n    }\n    return `${size.toFixed(2)} ${units[i].trim()}`;\n}\n", "export class Cache {\n    constructor(getValue, getKey) {\n        this.cache = {};\n        this.getKey = getKey;\n        this.getValue = getValue;\n    }\n    _getCacheAndKey(...path) {\n        let cache = this.cache;\n        let key;\n        if (this.getKey) {\n            key = this.getKey(...path);\n        } else {\n            for (let i = 0; i < path.length - 1; i++) {\n                cache = cache[path[i]] = cache[path[i]] || {};\n            }\n            key = path[path.length - 1];\n        }\n        return { cache, key };\n    }\n    clear(...path) {\n        const { cache, key } = this._getCacheAndKey(...path);\n        delete cache[key];\n    }\n    invalidate() {\n        this.cache = {};\n    }\n    read(...path) {\n        const { cache, key } = this._getCacheAndKey(...path);\n        if (!(key in cache)) {\n            cache[key] = this.getValue(...path);\n        }\n        return cache[key];\n    }\n}\n", "/**\n * Adds the given classes to an element, whether the classes\n * are strings or objects.\n *\n * @param {HTMLElement} el\n * @param {String|Object|undefined} classes\n *\n * @example\n * addClassesToElement(el, \"hello\", { \"world\": 0 == 1, }...)\n */\nexport function addClassesToElement(el, ...classes) {\n    for (const classDefinition of classes) {\n        const classObj = toClassObj(classDefinition);\n        for (const className in classObj) {\n            if (classObj[className]) {\n                el.classList.add(className.trim());\n            }\n        }\n    }\n}\n\n/**\n * Merges two classes to a single class object, whether the\n * classes are strings or objects.\n *\n * @param {String|Object|undefined} classes\n * @returns {Object}\n *\n * @example\n * mergeClasses(\"hello\", { \"world\": 0 == 1, }...)\n */\nexport function mergeClasses(...classes) {\n    const classObj = {};\n    for (const classDefinition of classes) {\n        Object.assign(classObj, toClassObj(classDefinition));\n    }\n    return classObj;\n}\n\n/**\n * Returns an object from a class definition, whether it\n * is a string or an object.\n *\n * The returned object keys are css class names and the\n * values are expressions which represent if the class\n * should be added or not.\n *\n * @param {String|Object|undefined} classDefinition\n * @returns {Object}\n */\nfunction toClassObj(classDefinition) {\n    if (!classDefinition) {\n        return {};\n    } else if (typeof classDefinition === \"object\") {\n        return classDefinition;\n    } else if (typeof classDefinition === \"string\") {\n        const classObj = {};\n        classDefinition\n            .trim()\n            .split(/\\s+/)\n            .forEach((s) => {\n                classObj[s] = true;\n            });\n        return classObj;\n    } else {\n        console.warn(\n            `toClassObj only supports strings, objects and undefined className (got ${typeof classProp})`\n        );\n        return {};\n    }\n}\n", "/**\n * Converts RGB color components to HSL components.\n *\n * @static\n * @param {integer} r - [0, 255]\n * @param {integer} g - [0, 255]\n * @param {integer} b - [0, 255]\n * @returns {Object|false}\n *          - hue [0, 360[ (float)\n *          - saturation [0, 100] (float)\n *          - lightness [0, 100] (float)\n */\nexport function convertRgbToHsl(r, g, b) {\n    if (typeof (r) !== 'number' || isNaN(r) || r < 0 || r > 255\n            || typeof (g) !== 'number' || isNaN(g) || g < 0 || g > 255\n            || typeof (b) !== 'number' || isNaN(b) || b < 0 || b > 255) {\n        return false;\n    }\n\n    var red = r / 255;\n    var green = g / 255;\n    var blue = b / 255;\n    var maxColor = Math.max(red, green, blue);\n    var minColor = Math.min(red, green, blue);\n    var delta = maxColor - minColor;\n    var hue = 0;\n    var saturation = 0;\n    var lightness = (maxColor + minColor) / 2;\n    if (delta) {\n        if (maxColor === red) {\n            hue = (green - blue) / delta;\n        }\n        if (maxColor === green) {\n            hue = 2 + (blue - red) / delta;\n        }\n        if (maxColor === blue) {\n            hue = 4 + (red - green) / delta;\n        }\n        if (maxColor) {\n            saturation = delta / (1 - Math.abs(2 * lightness - 1));\n        }\n    }\n    hue = 60 * hue;\n    return {\n        hue: hue < 0 ? hue + 360 : hue,\n        saturation: saturation * 100,\n        lightness: lightness * 100,\n    };\n};\n/**\n * Converts HSL color components to RGB components.\n *\n * @static\n * @param {number} h - [0, 360[ (float)\n * @param {number} s - [0, 100] (float)\n * @param {number} l - [0, 100] (float)\n * @returns {Object|false}\n *          - red [0, 255] (integer)\n *          - green [0, 255] (integer)\n *          - blue [0, 255] (integer)\n */\nexport function convertHslToRgb(h, s, l) {\n    if (typeof (h) !== 'number' || isNaN(h) || h < 0 || h > 360\n            || typeof (s) !== 'number' || isNaN(s) || s < 0 || s > 100\n            || typeof (l) !== 'number' || isNaN(l) || l < 0 || l > 100) {\n        return false;\n    }\n\n    var huePrime = h / 60;\n    var saturation = s / 100;\n    var lightness = l / 100;\n    var chroma = saturation * (1 - Math.abs(2 * lightness - 1));\n    var secondComponent = chroma * (1 - Math.abs(huePrime % 2 - 1));\n    var lightnessAdjustment = lightness - chroma / 2;\n    var precision = 255;\n    chroma = Math.round((chroma + lightnessAdjustment) * precision);\n    secondComponent = Math.round((secondComponent + lightnessAdjustment) * precision);\n    lightnessAdjustment = Math.round((lightnessAdjustment) * precision);\n    if (huePrime >= 0 && huePrime < 1) {\n        return {\n            red: chroma,\n            green: secondComponent,\n            blue: lightnessAdjustment,\n        };\n    }\n    if (huePrime >= 1 && huePrime < 2) {\n        return {\n            red: secondComponent,\n            green: chroma,\n            blue: lightnessAdjustment,\n        };\n    }\n    if (huePrime >= 2 && huePrime < 3) {\n        return {\n            red: lightnessAdjustment,\n            green: chroma,\n            blue: secondComponent,\n        };\n    }\n    if (huePrime >= 3 && huePrime < 4) {\n        return {\n            red: lightnessAdjustment,\n            green: secondComponent,\n            blue: chroma,\n        };\n    }\n    if (huePrime >= 4 && huePrime < 5) {\n        return {\n            red: secondComponent,\n            green: lightnessAdjustment,\n            blue: chroma,\n        };\n    }\n    if (huePrime >= 5 && huePrime <= 6) {\n        return {\n            red: chroma,\n            green: lightnessAdjustment,\n            blue: secondComponent,\n        };\n    }\n    return false;\n};\n/**\n * Converts RGBA color components to a normalized CSS color: if the opacity\n * is invalid or equal to 100, a hex is returned; otherwise a rgba() css color\n * is returned.\n *\n * Those choice have multiple reason:\n * - A hex color is more common to c/c from other utilities on the web and is\n *   also shorter than rgb() css colors\n * - Opacity in hexadecimal notations is not supported on all browsers and is\n *   also less common to use.\n *\n * @static\n * @param {integer} r - [0, 255]\n * @param {integer} g - [0, 255]\n * @param {integer} b - [0, 255]\n * @param {float} a - [0, 100]\n * @returns {string}\n */\nexport function convertRgbaToCSSColor(r, g, b, a) {\n    if (typeof (r) !== 'number' || isNaN(r) || r < 0 || r > 255\n            || typeof (g) !== 'number' || isNaN(g) || g < 0 || g > 255\n            || typeof (b) !== 'number' || isNaN(b) || b < 0 || b > 255) {\n        return false;\n    }\n    if (typeof (a) !== 'number' || isNaN(a) || a < 0 || Math.abs(a - 100) < Number.EPSILON) {\n        const rr = r < 16 ? '0' + r.toString(16) : r.toString(16);\n        const gg = g < 16 ? '0' + g.toString(16) : g.toString(16);\n        const bb = b < 16 ? '0' + b.toString(16) : b.toString(16);\n        return (`#${rr}${gg}${bb}`).toUpperCase();\n    }\n    return `rgba(${r}, ${g}, ${b}, ${parseFloat((a / 100.0).toFixed(3))})`;\n};\n/**\n * Converts a CSS color (rgb(), rgba(), hexadecimal) to RGBA color components.\n *\n * Note: we don't support using and displaying hexadecimal color with opacity\n * but this method allows to receive one and returns the correct opacity value.\n *\n * @static\n * @param {string} cssColor - hexadecimal code or rgb() or rgba() or color()\n * @returns {Object|false}\n *          - red [0, 255] (integer)\n *          - green [0, 255] (integer)\n *          - blue [0, 255] (integer)\n *          - opacity [0, 100.0] (float)\n */\nexport function convertCSSColorToRgba(cssColor) {\n    // Check if cssColor is a rgba() or rgb() color\n    const rgba = cssColor.match(/^rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+(?:\\.\\d+)?))?\\)$/);\n    if (rgba) {\n        if (rgba[4] === undefined) {\n            rgba[4] = 1;\n        }\n        return {\n            red: parseInt(rgba[1]),\n            green: parseInt(rgba[2]),\n            blue: parseInt(rgba[3]),\n            opacity: Math.round(parseFloat(rgba[4]) * 100),\n        };\n    }\n\n    // Otherwise, check if cssColor is an hexadecimal code color\n    if (/^#([0-9A-F]{6}|[0-9A-F]{8})$/i.test(cssColor)) {\n        return {\n            red: parseInt(cssColor.substr(1, 2), 16),\n            green: parseInt(cssColor.substr(3, 2), 16),\n            blue: parseInt(cssColor.substr(5, 2), 16),\n            opacity: (cssColor.length === 9 ? (parseInt(cssColor.substr(7, 2), 16) / 255) : 1) * 100,\n        };\n    }\n\n    // TODO maybe implement a support for receiving css color like 'red' or\n    // 'transparent' (which are now considered non-css color by isCSSColor...)\n    // Note: however, if ever implemented be careful of 'white'/'black' which\n    // actually are color names for our color system...\n\n    // Check if cssColor is a color() functional notation allowing colorspace\n    // with implicit sRGB.\n    // \"<color()>\" allows to define a color specification in a formalized\n    // manner. It starts with the \"color(\" keyword, specifies color space\n    // parameters, and optionally includes an alpha value for transparency.\n    if (/color\\(.+\\)/.test(cssColor)) {\n        const canvasEl = document.createElement(\"canvas\");\n        canvasEl.height = 1;\n        canvasEl.width = 1;\n        const ctx = canvasEl.getContext(\"2d\");\n        ctx.fillStyle = cssColor;\n        ctx.fillRect(0, 0, 1, 1);\n        const data = ctx.getImageData(0, 0, 1, 1).data;\n        return {\n            red: data[0],\n            green: data[1],\n            blue: data[2],\n            opacity: data[3] / 2.55, // Convert 0-255 to percentage\n        };\n    }\n    return false;\n};\n/**\n * Converts a CSS color (rgb(), rgba(), hexadecimal) to a normalized version\n * of the same color (@see convertRgbaToCSSColor).\n *\n * Normalized color can be safely compared using string comparison.\n *\n * @static\n * @param {string} cssColor - hexadecimal code or rgb() or rgba()\n * @returns {string} - the normalized css color or the given css color if it\n *                     failed to be normalized\n */\nexport function normalizeCSSColor(cssColor) {\n    const rgba = convertCSSColorToRgba(cssColor);\n    if (!rgba) {\n        return cssColor;\n    }\n    return convertRgbaToCSSColor(rgba.red, rgba.green, rgba.blue, rgba.opacity);\n};\n/**\n * Checks if a given string is a css color.\n *\n * @static\n * @param {string} cssColor\n * @returns {boolean}\n */\nexport function isCSSColor(cssColor) {\n    return convertCSSColorToRgba(cssColor) !== false;\n};\n/**\n * Mixes two colors by applying a weighted average of their red, green and blue\n * components.\n *\n * @static\n * @param {string} cssColor1 - hexadecimal code or rgb() or rgba()\n * @param {string} cssColor2 - hexadecimal code or rgb() or rgba()\n * @param {number} weight - a number between 0 and 1\n * @returns {string} - mixed color in hexadecimal format\n */\nexport function mixCssColors(cssColor1, cssColor2, weight) {\n    const rgba1 = convertCSSColorToRgba(cssColor1);\n    const rgba2 = convertCSSColorToRgba(cssColor2);\n    const rgb1 = [rgba1.red, rgba1.green, rgba1.blue];\n    const rgb2 = [rgba2.red, rgba2.green, rgba2.blue];\n    const [r, g, b] = rgb1.map((_, idx) => Math.round(rgb2[idx] + (rgb1[idx] - rgb2[idx]) * weight));\n    return convertRgbaToCSSColor(r, g, b);\n};\n", "import { Component, onError, xml } from \"@odoo/owl\";\n\nexport class ErrorHandler extends Component {\n    static template = xml`<t t-slot=\"default\" />`;\n    static props = [\"onError\", \"slots\"];\n    setup() {\n        onError((error) => {\n            this.props.onError(error);\n        });\n    }\n}\n", "/**\n * Returns a promise resolved after 'wait' milliseconds\n *\n * @param {int} [wait=0] the delay in ms\n * @return {Promise}\n */\nexport function delay(wait) {\n    return new Promise(function (resolve) {\n        setTimeout(resolve, wait);\n    });\n}\n\n/**\n * KeepLast is a concurrency primitive that manages a list of tasks, and only\n * keeps the last task active.\n *\n * @template T\n */\nexport class KeepLast {\n    constructor() {\n        this._id = 0;\n    }\n    /**\n     * Register a new task\n     *\n     * @param {Promise<T>} promise\n     * @returns {Promise<T>}\n     */\n    add(promise) {\n        this._id++;\n        const currentId = this._id;\n        return new Promise((resolve, reject) => {\n            promise\n                .then((value) => {\n                    if (this._id === currentId) {\n                        resolve(value);\n                    }\n                })\n                .catch((reason) => {\n                    // not sure about this part\n                    if (this._id === currentId) {\n                        reject(reason);\n                    }\n                });\n        });\n    }\n}\n\n/**\n * A (Odoo) mutex is a primitive for serializing computations.  This is\n * useful to avoid a situation where two computations modify some shared\n * state and cause some corrupted state.\n *\n * Imagine that we have a function to fetch some data _load(), which returns\n * a promise which resolves to something useful. Now, we have some code\n * looking like this::\n *\n *      return this._load().then(function (result) {\n *          this.state = result;\n *      });\n *\n * If this code is run twice, but the second execution ends before the\n * first, then the final state will be the result of the first call to\n * _load.  However, if we have a mutex::\n *\n *      this.mutex = new Mutex();\n *\n * and if we wrap the calls to _load in a mutex::\n *\n *      return this.mutex.exec(function() {\n *          return this._load().then(function (result) {\n *              this.state = result;\n *          });\n *      });\n *\n * Then, it is guaranteed that the final state will be the result of the\n * second execution.\n *\n * A Mutex has to be a class, and not a function, because we have to keep\n * track of some internal state.\n */\nexport class Mutex {\n    constructor() {\n        this._lock = Promise.resolve();\n        this._queueSize = 0;\n        this._unlockedProm = undefined;\n        this._unlock = undefined;\n    }\n    /**\n     * Add a computation to the queue, it will be executed as soon as the\n     * previous computations are completed.\n     *\n     * @param {() => (void | Promise<void>)} action a function which may return a Promise\n     * @returns {Promise<void>}\n     */\n    async exec(action) {\n        this._queueSize++;\n        if (!this._unlockedProm) {\n            this._unlockedProm = new Promise((resolve) => {\n                this._unlock = () => {\n                    resolve();\n                    this._unlockedProm = undefined;\n                };\n            });\n        }\n        const always = () => {\n            return Promise.resolve(action()).finally(() => {\n                if (--this._queueSize === 0) {\n                    this._unlock();\n                }\n            });\n        };\n        this._lock = this._lock.then(always, always);\n        return this._lock;\n    }\n    /**\n     * @returns {Promise<void>} resolved as soon as the Mutex is unlocked\n     *   (directly if it is currently idle)\n     */\n    getUnlockedDef() {\n        return this._unlockedProm || Promise.resolve();\n    }\n}\n\n/**\n * Race is a class designed to manage concurrency problems inspired by\n * Promise.race(), except that it is dynamic in the sense that promises can be\n * added anytime to a Race instance. When a promise is added, it returns another\n * promise which resolves as soon as a promise, among all added promises, is\n * resolved. The race is thus over. From that point, a new race will begin the\n * next time a promise will be added.\n *\n * @template T\n */\nexport class Race {\n    constructor() {\n        this.currentProm = null;\n        this.currentPromResolver = null;\n        this.currentPromRejecter = null;\n    }\n    /**\n     * Register a new promise. If there is an ongoing race, the promise is added\n     * to that race. Otherwise, it starts a new race. The returned promise\n     * resolves as soon as the race is over, with the value of the first resolved\n     * promise added to the race.\n     *\n     * @param {Promise<T>} promise\n     * @returns {Promise<T>}\n     */\n    add(promise) {\n        if (!this.currentProm) {\n            this.currentProm = new Promise((resolve, reject) => {\n                this.currentPromResolver = (value) => {\n                    this.currentProm = null;\n                    this.currentPromResolver = null;\n                    this.currentPromRejecter = null;\n                    resolve(value);\n                };\n                this.currentPromRejecter = (error) => {\n                    this.currentProm = null;\n                    this.currentPromResolver = null;\n                    this.currentPromRejecter = null;\n                    reject(error);\n                };\n            });\n        }\n        promise.then(this.currentPromResolver).catch(this.currentPromRejecter);\n        return this.currentProm;\n    }\n    /**\n     * @returns {Promise<T>|null} promise resolved as soon as the race is over, or\n     *   null if there is no race ongoing)\n     */\n    getCurrentProm() {\n        return this.currentProm;\n    }\n}\n\n/**\n * Deferred is basically a resolvable/rejectable extension of Promise.\n */\nexport class Deferred extends Promise {\n    constructor() {\n        let resolve;\n        let reject;\n        const prom = new Promise((res, rej) => {\n            resolve = res;\n            reject = rej;\n        });\n        return Object.assign(prom, { resolve, reject });\n    }\n}\n", "import { makeDraggableHook } from \"@web/core/utils/draggable_hook_builder_owl\";\nimport { pick } from \"@web/core/utils/objects\";\n\n/** @typedef {import(\"@web/core/utils/draggable_hook_builder\").DraggableHandlerParams} DraggableHandlerParams */\n\n/**\n * @typedef DraggableParams\n *\n * MANDATORY\n *\n * @property {{ el: HTMLElement | null }} ref\n * @property {string} elements defines draggable elements\n *\n * OPTIONAL\n *\n * @property {boolean | () => boolean} [enable] whether the draggable system should\n *  be enabled.\n * @property {string | () => string} [handle] additional selector for when the dragging\n *  sequence must be initiated when dragging on a certain part of the element.\n * @property {string | () => string} [ignore] selector targetting elements that must\n *  initiate a drag.\n * @property {string | () => string} [cursor] cursor style during the dragging sequence.\n *\n * HANDLERS (also optional)\n *\n * @property {(params: DraggableHandlerParams) => any} [onDragStart]\n *  called when a dragging sequence is initiated.\n * @property {(params: DraggableHandlerParams) => any} [onDrag]\n *  called on each \"mousemove\" during the drag sequence.\n * @property {(params: DraggableHandlerParams) => any} [onDragEnd]\n *  called when the dragging sequence ends, regardless of the reason.\n * @property {(params: DraggableHandlerParams) => any} [onDrop] called when the dragging sequence\n *  ends on a mouseup action.\n */\n\n/**\n * @typedef DraggableState\n * @property {boolean} dragging\n */\n\n/** @type {(params: DraggableParams) => DraggableState} */\nexport const useDraggable = makeDraggableHook({\n    name: \"useDraggable\",\n    onWillStartDrag: ({ ctx }) => pick(ctx.current, \"element\"),\n    onDragStart: ({ ctx }) => pick(ctx.current, \"element\"),\n    onDrag: ({ ctx }) => pick(ctx.current, \"element\"),\n    onDragEnd: ({ ctx }) => pick(ctx.current, \"element\"),\n    onDrop: ({ ctx }) => pick(ctx.current, \"element\"),\n});\n", "import { clamp } from \"@web/core/utils/numbers\";\nimport { omit } from \"@web/core/utils/objects\";\nimport { closestScrollableX, closestScrollableY } from \"@web/core/utils/scrolling\";\nimport { setRecurringAnimationFrame } from \"@web/core/utils/timing\";\nimport { browser } from \"../browser/browser\";\nimport { hasTouch, isBrowserFirefox, isIOS } from \"../browser/feature_detection\";\n\n/**\n * @typedef {ReturnType<typeof makeCleanupManager>} CleanupManager\n *\n * @typedef {ReturnType<typeof makeDOMHelpers>} DOMHelpers\n *\n * @typedef DraggableBuilderParams\n * Hook params\n * @property {string} [name=\"useAnonymousDraggable\"]\n * @property {EdgeScrollingOptions} [edgeScrolling]\n * @property {Record<string, string[]>} [acceptedParams]\n * @property {Record<string, any>} [defaultParams]\n * Setup hooks\n * @property {{\n *  addListener: typeof import(\"@odoo/owl\")[\"useExternalListener\"];\n *  setup: typeof import(\"@odoo/owl\")[\"useEffect\"];\n *  teardown: typeof import(\"@odoo/owl\")[\"onWillUnmount\"];\n *  throttle: typeof import(\"./timing\")[\"useThrottleForAnimation\"];\n *  wrapState: typeof import(\"@odoo/owl\")[\"reactive\"];\n * }} setupHooks\n * Build hooks\n * @property {(params: DraggableBuildHandlerParams) => any} onComputeParams\n * Runtime hooks\n * @property {(params: DraggableBuildHandlerParams) => any} onDragStart\n * @property {(params: DraggableBuildHandlerParams) => any} onDrag\n * @property {(params: DraggableBuildHandlerParams) => any} onDragEnd\n * @property {(params: DraggableBuildHandlerParams) => any} onDrop\n * @property {(params: DraggableBuildHandlerParams) => any} onWillStartDrag\n *\n * @typedef DraggableHookContext\n * @property {{ el: HTMLElement | null }} ref\n * @property {string | null} [elementSelector=null]\n * @property {string | null} [ignoreSelector=null]\n * @property {string | null} [fullSelector=null]\n * @property {boolean} [followCursor=true]\n * @property {string | null} [cursor=null]\n * @property {() => boolean} [enable=() => false]\n * @property {(HTMLElement) => boolean} [preventDrag=(el) => false]\n * @property {Position} [pointer={ x: 0, y: 0 }]\n * @property {EdgeScrollingOptions} [edgeScrolling]\n * @property {number} [delay]\n * @property {number} [tolerance]\n * @property {DraggableHookCurrentContext} current\n *\n * @typedef DraggableHookCurrentContext\n * @property {HTMLElement} [current.container]\n * @property {DOMRect} [current.containerRect]\n * @property {HTMLElement} [current.element]\n * @property {DOMRect} [current.elementRect]\n * @property {HTMLElement | null} [current.scrollParentX]\n * @property {DOMRect | null} [current.scrollParentXRect]\n * @property {HTMLElement | null} [current.scrollParentY]\n * @property {DOMRect | null} [current.scrollParentYRect]\n * @property {\"left\"|\"right\"|\"top\"|\"bottom\"|null} [scrollingEdge]\n * @property {number} [timeout]\n * @property {Position} [initialPosition]\n * @property {Position} [offset={ x: 0, y: 0 }]\n *\n * @typedef EdgeScrollingOptions\n * @property {boolean} [enabled=true]\n * @property {number} [speed=10]\n * @property {number} [threshold=20]\n * @property {\"horizontal\"|\"vertical\"} [direction]\n *\n * @typedef Position\n * @property {number} x\n * @property {number} y\n *\n * @typedef {DOMHelpers & {\n *  ctx: DraggableHookContext,\n *  addCleanup(cleanupFn: () => any): void,\n *  addEffectCleanup(cleanupFn: () => any): void,\n *  callHandler(handlerName: string, arg: Record<any, any>): void,\n * }} DraggableBuildHandlerParams\n *\n * @typedef {DOMHelpers & Position & { element: HTMLElement }} DraggableHandlerParams\n */\n\nconst DRAGGABLE_CLASS = \"o_draggable\";\nexport const DRAGGED_CLASS = \"o_dragged\";\n\nconst DEFAULT_ACCEPTED_PARAMS = {\n    enable: [Boolean, Function],\n    preventDrag: [Function],\n    ref: [Object],\n    elements: [String],\n    handle: [String, Function],\n    ignore: [String, Function],\n    cursor: [String],\n    edgeScrolling: [Object, Function],\n    delay: [Number],\n    tolerance: [Number],\n    touchDelay: [Number],\n    iframeWindow: [Object, Function],\n};\nconst DEFAULT_DEFAULT_PARAMS = {\n    elements: `.${DRAGGABLE_CLASS}`,\n    enable: true,\n    preventDrag: () => false,\n    edgeScrolling: {\n        speed: 10,\n        threshold: 30,\n    },\n    delay: 0,\n    tolerance: 10,\n    touchDelay: 300,\n};\nconst LEFT_CLICK = 0;\nconst MANDATORY_PARAMS = [\"ref\"];\nconst WHITE_LISTED_KEYS = [\"Alt\", \"Control\", \"Meta\", \"Shift\"];\n\n/**\n * Cache containing the elements in which an attribute has been modified by a hook.\n * It is global since multiple draggable hooks can interact with the same elements.\n * @type {Record<string, Set<HTMLElement>>}\n */\nconst elCache = {};\n\n/**\n * Transforms a camelCased string to return its kebab-cased version.\n * Typically used to generate CSS properties from JS objects.\n *\n * @param {string} str\n * @returns {string}\n */\nfunction camelToKebab(str) {\n    return str.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * @template T\n * @param {T | () => T} valueOrFn\n * @returns {T}\n */\nfunction getReturnValue(valueOrFn) {\n    if (typeof valueOrFn === \"function\") {\n        return valueOrFn();\n    }\n    return valueOrFn;\n}\n\n/**\n * Returns the first scrollable parent of the given element (recursively), or null\n * if none is found. A 'scrollable' element is defined by 2 things:\n *\n * - for either in width or in height: the 'scroll' value is larger than the 'client'\n * value;\n *\n * - its computed 'overflow' property is set to either \"auto\" or \"scroll\"\n *\n * If both of these assertions are true, it means that the element can effectively\n * be scrolled on at least one axis.\n * @param {HTMLElement} el\n * @returns {(HTMLElement | null)[]}\n */\nfunction getScrollParents(el) {\n    return [closestScrollableX(el), closestScrollableY(el)];\n}\n\n/**\n * @param {() => any} [defaultCleanupFn]\n */\nfunction makeCleanupManager(defaultCleanupFn) {\n    /**\n     * Registers the given cleanup function to be called when cleaning up hooks.\n     * @param {() => any} [cleanupFn]\n     */\n    const add = (cleanupFn) => typeof cleanupFn === \"function\" && cleanups.push(cleanupFn);\n\n    /**\n     * Runs all cleanup functions while clearing the cleanups list.\n     */\n    const cleanup = () => {\n        while (cleanups.length) {\n            cleanups.pop()();\n        }\n        add(defaultCleanupFn);\n    };\n\n    const cleanups = [];\n\n    add(defaultCleanupFn);\n\n    return { add, cleanup };\n}\n\n/**\n * @param {CleanupManager} cleanup\n */\nfunction makeDOMHelpers(cleanup) {\n    /**\n     * @param {HTMLElement} el\n     * @param  {...string} classNames\n     */\n    const addClass = (el, ...classNames) => {\n        if (!el || !classNames.length) {\n            return;\n        }\n        cleanup.add(() => el.classList.remove(...classNames));\n        el.classList.add(...classNames);\n    };\n\n    /**\n     * Adds an event listener to be cleaned up after the next drag sequence\n     * has stopped.\n     * @param {EventTarget} el\n     * @param {string} event\n     * @param {(...args: any[]) => any} callback\n     * @param {AddEventListenerOptions & { noAddedStyle?: boolean }} [options]\n     */\n    const addListener = (el, event, callback, options = {}) => {\n        if (!el || !event || !callback) {\n            return;\n        }\n        const { noAddedStyle } = options;\n        delete options.noAddedStyle;\n        el.addEventListener(event, callback, options);\n        if (!noAddedStyle && /mouse|pointer|touch/.test(event)) {\n            // Restore pointer events on elements listening on mouse/pointer/touch events.\n            addStyle(el, { pointerEvents: \"auto\" });\n        }\n        cleanup.add(() => el.removeEventListener(event, callback, options));\n    };\n\n    /**\n     * Adds style to an element to be cleaned up after the next drag sequence has\n     * stopped.\n     * @param {HTMLElement} el\n     * @param {Record<string, string | number>} style\n     */\n    const addStyle = (el, style) => {\n        if (!el || !style || !Object.keys(style).length) {\n            return;\n        }\n        cleanup.add(saveAttribute(el, \"style\"));\n        for (const key in style) {\n            const [value, priority] = String(style[key]).split(/\\s*!\\s*/);\n            el.style.setProperty(camelToKebab(key), value, priority);\n        }\n    };\n\n    /**\n     * Returns the bounding rect of the given element. If the `adjust` option is set\n     * to true, the rect will be reduced by the padding of the element.\n     * @param {HTMLElement} el\n     * @param {Object} [options={}]\n     * @param {boolean} [options.adjust=false]\n     * @returns {DOMRect}\n     */\n    const getRect = (el, options = {}) => {\n        if (!el) {\n            return {};\n        }\n        const rect = el.getBoundingClientRect();\n        if (options.adjust) {\n            const style = getComputedStyle(el);\n            const [pl, pr, pt, pb] = [\n                \"padding-left\",\n                \"padding-right\",\n                \"padding-top\",\n                \"padding-bottom\",\n            ].map((prop) => pixelValueToNumber(style.getPropertyValue(prop)));\n\n            rect.x += pl;\n            rect.y += pt;\n            rect.width -= pl + pr;\n            rect.height -= pt + pb;\n        }\n        return rect;\n    };\n\n    /**\n     * @param {HTMLElement} el\n     * @param {string} attribute\n     */\n    const removeAttribute = (el, attribute) => {\n        if (!el || !attribute) {\n            return;\n        }\n        cleanup.add(saveAttribute(el, attribute));\n        el.removeAttribute(attribute);\n    };\n\n    /**\n     * @param {HTMLElement} el\n     * @param {...string} classNames\n     */\n    const removeClass = (el, ...classNames) => {\n        if (!el || !classNames.length) {\n            return;\n        }\n        cleanup.add(saveAttribute(el, \"class\"));\n        el.classList.remove(...classNames);\n    };\n\n    /**\n     * Adds style to an element to be cleaned up after the next drag sequence has\n     * stopped.\n     * @param {HTMLElement} el\n     * @param {...string} properties\n     */\n    const removeStyle = (el, ...properties) => {\n        if (!el || !properties.length) {\n            return;\n        }\n        cleanup.add(saveAttribute(el, \"style\"));\n        for (const key of properties) {\n            el.style.removeProperty(camelToKebab(key));\n        }\n    };\n\n    /**\n     * @param {HTMLElement} el\n     * @param {string} attribute\n     * @param {any} value\n     */\n    const setAttribute = (el, attribute, value) => {\n        if (!el || !attribute) {\n            return;\n        }\n        cleanup.add(saveAttribute(el, attribute));\n        el.setAttribute(attribute, String(value));\n    };\n\n    return {\n        addClass,\n        addListener,\n        addStyle,\n        getRect,\n        removeAttribute,\n        removeClass,\n        removeStyle,\n        setAttribute,\n    };\n}\n\n/**\n * Converts a CSS pixel value to a number, removing the 'px' part.\n * @param {string} val\n * @returns {number}\n */\nfunction pixelValueToNumber(val) {\n    return Number(val.endsWith(\"px\") ? val.slice(0, -2) : val);\n}\n\n/**\n * @param {Event} ev\n * @param {{ stop?: boolean }} params\n */\nfunction safePrevent(ev, { stop } = {}) {\n    if (ev.cancelable) {\n        ev.preventDefault();\n        if (stop) {\n            ev.stopPropagation();\n        }\n    }\n}\n\nfunction saveAttribute(el, attribute) {\n    const restoreAttribute = () => {\n        cache.delete(el);\n        if (hasAttribute) {\n            el.setAttribute(attribute, originalValue);\n        } else {\n            el.removeAttribute(attribute);\n        }\n    };\n\n    if (!(attribute in elCache)) {\n        elCache[attribute] = new Set();\n    }\n    const cache = elCache[attribute];\n\n    if (cache.has(el)) {\n        return;\n    }\n\n    cache.add(el);\n    const hasAttribute = el.hasAttribute(attribute);\n    const originalValue = el.getAttribute(attribute);\n\n    return restoreAttribute;\n}\n\n/**\n * @template T\n * @param {T | () => T} value\n * @returns {() => T}\n */\nfunction toFunction(value) {\n    return typeof value === \"function\" ? value : () => value;\n}\n\n/**\n * @param {DraggableBuilderParams} hookParams\n * @returns {(params: Record<keyof typeof DEFAULT_ACCEPTED_PARAMS, any>) => { dragging: boolean }}\n */\nexport function makeDraggableHook(hookParams) {\n    hookParams = getReturnValue(hookParams);\n\n    const hookName = hookParams.name || \"useAnonymousDraggable\";\n    const { setupHooks } = hookParams;\n    const allAcceptedParams = { ...DEFAULT_ACCEPTED_PARAMS, ...hookParams.acceptedParams };\n    const defaultParams = { ...DEFAULT_DEFAULT_PARAMS, ...hookParams.defaultParams };\n\n    /**\n     * Computes the current params and converts the params definition\n     * @param {SortableParams} params\n     * @returns {[string, string | boolean][]}\n     */\n    const computeParams = (params) => {\n        const computedParams = { enable: () => true };\n        for (const prop in allAcceptedParams) {\n            if (prop in params) {\n                if (prop === \"enable\") {\n                    computedParams[prop] = toFunction(params[prop]);\n                } else if (\n                    allAcceptedParams[prop].length === 1 &&\n                    allAcceptedParams[prop][0] === Function\n                ) {\n                    computedParams[prop] = params[prop];\n                } else {\n                    computedParams[prop] = getReturnValue(params[prop]);\n                }\n            }\n        }\n        return Object.entries(computedParams);\n    };\n\n    /**\n     * Basic error builder for the hook.\n     * @param {string} reason\n     * @returns {Error}\n     */\n    const makeError = (reason) => new Error(`Error in hook ${hookName}: ${reason}.`);\n    let preventClick = false;\n\n    return {\n        [hookName](params) {\n            /**\n             * Executes a handler from the `hookParams`.\n             * @param {string} hookHandlerName\n             * @param {Record<any, any>} arg\n             */\n            const callBuildHandler = (hookHandlerName, arg) => {\n                if (typeof hookParams[hookHandlerName] !== \"function\") {\n                    return;\n                }\n                const returnValue = hookParams[hookHandlerName]({ ctx, ...helpers, ...arg });\n                if (returnValue) {\n                    callHandler(hookHandlerName, returnValue);\n                }\n            };\n\n            /**\n             * Safely executes a handler from the `params`, so that the drag sequence can\n             * be interrupted if an error occurs.\n             * @param {string} handlerName\n             * @param {Record<any, any>} arg\n             */\n            const callHandler = (handlerName, arg) => {\n                if (typeof params[handlerName] !== \"function\") {\n                    return;\n                }\n                try {\n                    params[handlerName]({ ...dom, ...ctx.pointer, ...arg });\n                } catch (err) {\n                    dragEnd(null, true);\n                    throw err;\n                }\n            };\n\n            /**\n             * Returns whether the user has moved from at least the number of pixels\n             * that are tolerated from the initial pointer position.\n             */\n            const canStartDrag = () => {\n                const {\n                    pointer,\n                    current: { initialPosition },\n                } = ctx;\n                return (\n                    !ctx.tolerance ||\n                    Math.hypot(pointer.x - initialPosition.x, pointer.y - initialPosition.y) >=\n                        ctx.tolerance\n                );\n            };\n\n            /**\n             * Main entry function to start a drag sequence.\n             */\n            const dragStart = () => {\n                state.dragging = true;\n                state.willDrag = false;\n\n                // Compute scrollable parent\n                const isDocumentScrollingElement = ctx.current.container\n                    === ctx.current.container.ownerDocument.scrollingElement;\n                // If the container is the \"ownerDocument.scrollingElement\",\n                // there is no need to get the scroll parent as it is the\n                // scrollable element itself.\n                // TODO: investigate if \"getScrollParents\" should not consider\n                // the \"ownerDocument.scrollingElement\" directly.\n                [ctx.current.scrollParentX, ctx.current.scrollParentY] =\n                    isDocumentScrollingElement\n                    ? [ctx.current.container, ctx.current.container]\n                    : getScrollParents(ctx.current.container);\n\n                updateRects();\n                const { x, y, width, height } = ctx.current.elementRect;\n\n                // Adjusts the offset\n                ctx.current.offset = {\n                    x: ctx.current.initialPosition.x - x,\n                    y: ctx.current.initialPosition.y - y,\n                };\n\n                if (ctx.followCursor) {\n                    dom.addStyle(ctx.current.element, {\n                        width: `${width}px`,\n                        height: `${height}px`,\n                        position: \"fixed !important\",\n                    });\n\n                    // First adjustment\n                    updateElementPosition();\n                }\n\n                dom.addClass(document.body, \"pe-none\", \"user-select-none\");\n                if (params.iframeWindow) {\n                    for (const iframe of document.getElementsByTagName(\"iframe\")) {\n                        if (iframe.contentWindow === params.iframeWindow) {\n                            dom.addClass(iframe, \"pe-none\", \"user-select-none\");\n                        }\n                    }\n                }\n                // FIXME: adding pe-none and cursor on the same element makes\n                // no sense as pe-none prevents the cursor to be displayed.\n                if (ctx.cursor) {\n                    dom.addStyle(document.body, { cursor: ctx.cursor });\n                }\n\n                if (\n                    (ctx.current.scrollParentX || ctx.current.scrollParentY) &&\n                    ctx.edgeScrolling.enabled\n                ) {\n                    const cleanupFn = setRecurringAnimationFrame(handleEdgeScrolling);\n                    cleanup.add(cleanupFn);\n                }\n\n                dom.addClass(ctx.current.element, DRAGGED_CLASS);\n\n                callBuildHandler(\"onDragStart\");\n            };\n\n            /**\n             * Main exit function to stop a drag sequence. Note that it can be called\n             * even if a drag sequence did not start yet to perform a cleanup of all\n             * current context variables.\n             * @param {HTMLElement | null} target\n             * @param {boolean} [inErrorState] can be set to true when an error\n             *  occurred to avoid falling into an infinite loop if the error\n             *  originated from one of the handlers.\n             */\n            const dragEnd = (target, inErrorState) => {\n                if (state.dragging) {\n                    preventClick = true;\n                    if (!inErrorState) {\n                        if (target) {\n                            callBuildHandler(\"onDrop\", { target });\n                        }\n                        callBuildHandler(\"onDragEnd\");\n                    }\n                }\n\n                cleanup.cleanup();\n            };\n\n            /**\n             * Applies scroll to the container if the current element is near\n             * the edge of the container.\n             */\n            const handleEdgeScrolling = (deltaTime) => {\n                updateRects();\n                const { x: pointerX, y: pointerY } = ctx.pointer;\n                const xRect = ctx.current.scrollParentXRect;\n                const yRect = ctx.current.scrollParentYRect;\n\n                // \"getBoundingClientRect()\"\" (used in \"getRect()\") gives the\n                // distance from the element's top to the viewport, excluding\n                // scroll position. Only the \"document.scrollingElement\" element\n                // (\"<html>\") accounts for scrollTop.\n                const scrollParentYEl = ctx.current.scrollParentY;\n                if (scrollParentYEl === ctx.current.container.ownerDocument.scrollingElement) {\n                    yRect.y += scrollParentYEl.scrollTop;\n                }\n\n                const { direction, speed, threshold } = ctx.edgeScrolling;\n                const correctedSpeed = (speed / 16) * deltaTime;\n\n                const diff = {};\n                ctx.current.scrollingEdge = null;\n                if (xRect) {\n                    const maxWidth = xRect.x + xRect.width;\n                    if (pointerX - xRect.x < threshold) {\n                        diff.x = [pointerX - xRect.x, -1];\n                        ctx.current.scrollingEdge = \"left\";\n                    } else if (maxWidth - pointerX < threshold) {\n                        diff.x = [maxWidth - pointerX, 1];\n                        ctx.current.scrollingEdge = \"right\";\n                    }\n                }\n                if (yRect) {\n                    const maxHeight = yRect.y + yRect.height;\n                    if (pointerY - yRect.y < threshold) {\n                        diff.y = [pointerY - yRect.y, -1];\n                        ctx.current.scrollingEdge = \"top\";\n                    } else if (maxHeight - pointerY < threshold) {\n                        diff.y = [maxHeight - pointerY, 1];\n                        ctx.current.scrollingEdge = \"bottom\";\n                    }\n                }\n\n                const diffToScroll = ([delta, sign]) =>\n                    (1 - Math.max(delta, 0) / threshold) * correctedSpeed * sign;\n                if ((!direction || direction === \"vertical\") && diff.y) {\n                    ctx.current.scrollParentY.scrollBy({ top: diffToScroll(diff.y) });\n                }\n                if ((!direction || direction === \"horizontal\") && diff.x) {\n                    ctx.current.scrollParentX.scrollBy({ left: diffToScroll(diff.x) });\n                }\n                callBuildHandler(\"onDrag\");\n            };\n\n            /**\n             * Global (= ref) \"click\" event handler.\n             * Used to prevent click events after dragEnd\n             * @param {PointerEvent} ev\n             */\n            const onClick = (ev) => {\n                if (preventClick) {\n                    safePrevent(ev, { stop: true });\n                }\n            };\n\n            /**\n             * Window \"keydown\" event handler.\n             * @param {KeyboardEvent} ev\n             */\n            const onKeyDown = (ev) => {\n                if (!state.dragging || !ctx.enable()) {\n                    return;\n                }\n                if (!WHITE_LISTED_KEYS.includes(ev.key)) {\n                    safePrevent(ev, { stop: true });\n\n                    // Cancels drag sequences on every non-whitelisted key down event.\n                    dragEnd(null);\n                }\n            };\n\n            /**\n             * Global (= ref) \"pointercancel\" event handler.\n             */\n            const onPointerCancel = () => {\n                dragEnd(null);\n            };\n\n            /**\n             * Global (= ref) \"pointerdown\" event handler.\n             * @param {PointerEvent} ev\n             */\n            const onPointerDown = (ev) => {\n                preventClick = false;\n                updatePointerPosition(ev);\n\n                const initiationDelay = ev.pointerType === \"touch\" ? ctx.touchDelay : ctx.delay;\n\n                // A drag sequence can still be in progress if the pointerup occurred\n                // outside of the window.\n                dragEnd(null);\n\n                const fullSelectorEl = ev.target.closest(ctx.fullSelector);\n                if (\n                    ev.button !== LEFT_CLICK ||\n                    !ctx.enable() ||\n                    !fullSelectorEl ||\n                    (ctx.ignoreSelector && ev.target.closest(ctx.ignoreSelector)) ||\n                    ctx.preventDrag(fullSelectorEl)\n                ) {\n                    return;\n                }\n\n                // In FireFox: elements with `overflow: hidden` will prevent mouseenter and mouseleave\n                // events from firing on elements underneath them. This is the case when dragging a card\n                // by the heading. In such cases, we can prevent the default\n                // action on the pointerdown event to allow pointer events to fire properly.\n                // https://bugzilla.mozilla.org/show_bug.cgi?id=1352061\n                // https://bugzilla.mozilla.org/show_bug.cgi?id=339293\n                safePrevent(ev);\n                let activeElement = document.activeElement;\n                while (activeElement?.nodeName === \"IFRAME\") {\n                    activeElement = activeElement.contentDocument?.activeElement;\n                }\n                if (activeElement && !activeElement.contains(ev.target)) {\n                    activeElement.blur();\n                }\n\n                const { currentTarget, pointerId, target } = ev;\n                ctx.current.initialPosition = { ...ctx.pointer };\n\n                if (target.hasPointerCapture(pointerId)) {\n                    target.releasePointerCapture(pointerId);\n                }\n\n                if (initiationDelay) {\n                    if (hasTouch()) {\n                        if (ev.pointerType === \"touch\") {\n                            dom.addClass(target.closest(ctx.elementSelector), \"o_touch_bounce\");\n                        }\n                        if (isBrowserFirefox()) {\n                            // On Firefox mobile, long-touch events trigger an unpreventable\n                            // context menu to appear. To prevent this, all linkes are removed\n                            // from the dragged elements during the drag sequence.\n                            const links = [...currentTarget.querySelectorAll(\"[href]\")];\n                            if (currentTarget.hasAttribute(\"href\")) {\n                                links.unshift(currentTarget);\n                            }\n                            for (const link of links) {\n                                dom.removeAttribute(link, \"href\");\n                            }\n                        }\n                        if (isIOS()) {\n                            // On Safari mobile, any image can be dragged regardless\n                            // of the 'user-select' property.\n                            for (const image of currentTarget.getElementsByTagName(\"img\")) {\n                                dom.setAttribute(image, \"draggable\", false);\n                            }\n                        }\n                    }\n\n                    ctx.current.timeout = browser.setTimeout(() => {\n                        ctx.current.initialPosition = { ...ctx.pointer };\n\n                        willStartDrag(target);\n\n                        const { x: px, y: py } = ctx.pointer;\n                        const { x, y, width, height } = dom.getRect(ctx.current.element);\n                        if (px < x || x + width < px || py < y || y + height < py) {\n                            // Pointer left the target\n                            // Note that the timeout is cleared in dragEnd\n                            dragEnd(null);\n                        }\n                    }, initiationDelay);\n                    cleanup.add(() => browser.clearTimeout(ctx.current.timeout));\n                } else {\n                    willStartDrag(target);\n                }\n            };\n\n            /**\n             * Window \"pointermove\" event handler.\n             * @param {PointerEvent} ev\n             */\n            const onPointerMove = (ev) => {\n                updatePointerPosition(ev);\n\n                if (!ctx.current.element || !ctx.enable()) {\n                    return;\n                }\n\n                safePrevent(ev);\n\n                if (!state.dragging) {\n                    if (!canStartDrag()) {\n                        return;\n                    }\n                    dragStart();\n                }\n\n                if (ctx.followCursor) {\n                    updateElementPosition();\n                }\n\n                callBuildHandler(\"onDrag\");\n            };\n\n            /**\n             * Window \"pointerup\" event handler.\n             * @param {PointerEvent} ev\n             */\n            const onPointerUp = (ev) => {\n                updatePointerPosition(ev);\n                dragEnd(ev.target);\n            };\n\n            /**\n             * Updates the position of the current dragged element according to\n             * the current pointer position.\n             */\n            const updateElementPosition = () => {\n                const { containerRect, element, elementRect, offset } = ctx.current;\n                const { width: ew, height: eh } = elementRect;\n                const { x: cx, y: cy, width: cw, height: ch } = containerRect;\n\n                // Updates the position of the dragged element.\n                dom.addStyle(element, {\n                    left: `${clamp(ctx.pointer.x - offset.x, cx, cx + cw - ew)}px`,\n                    top: `${clamp(ctx.pointer.y - offset.y, cy, cy + ch - eh)}px`,\n                });\n            };\n\n            /**\n             * Updates the current pointer position from a given event.\n             * @param {PointerEvent} ev\n             */\n            const updatePointerPosition = (ev) => {\n                ctx.pointer.x = ev.clientX;\n                ctx.pointer.y = ev.clientY;\n            };\n\n            const updateRects = () => {\n                const { current } = ctx;\n                const { container, element, scrollParentX, scrollParentY } = current;\n                // Container rect\n                current.containerRect = dom.getRect(container, { adjust: true });\n                // If the scrolling element is within an iframe and the draggable\n                // element is outside this iframe, the offsets must be computed taking\n                // into account the iframe.\n                let iframeOffsetX = 0;\n                let iframeOffsetY = 0;\n                const iframeEl = container.ownerDocument.defaultView.frameElement;\n                if (iframeEl && !iframeEl.contentDocument?.contains(element)) {\n                    const { x, y } = dom.getRect(iframeEl);\n                    iframeOffsetX = x;\n                    iframeOffsetY = y;\n                    current.containerRect.x += iframeOffsetX;\n                    current.containerRect.y += iframeOffsetY;\n                }\n                // Adjust container rect according to its overflowing size\n                current.containerRect.width = container.scrollWidth;\n                current.containerRect.height = container.scrollHeight;\n                // ScrollParent rect\n                current.scrollParentXRect = null;\n                current.scrollParentYRect = null;\n                if (ctx.edgeScrolling.enabled) {\n                    // Adjust container rect according to scrollParents\n                    if (scrollParentX) {\n                        current.scrollParentXRect = dom.getRect(scrollParentX, { adjust: true });\n                        current.scrollParentXRect.x += iframeOffsetX;\n                        current.scrollParentXRect.y += iframeOffsetY;\n                        const right = Math.min(\n                            current.containerRect.left + container.scrollWidth,\n                            current.scrollParentXRect.right\n                        );\n                        current.containerRect.x = Math.max(\n                            current.containerRect.x,\n                            current.scrollParentXRect.x\n                        );\n                        current.containerRect.width = right - current.containerRect.x;\n                    }\n                    if (scrollParentY) {\n                        current.scrollParentYRect = dom.getRect(scrollParentY, { adjust: true });\n                        current.scrollParentYRect.x += iframeOffsetX;\n                        current.scrollParentYRect.y += iframeOffsetY;\n                        const bottom = Math.min(\n                            current.containerRect.top + container.scrollHeight,\n                            current.scrollParentYRect.bottom\n                        );\n                        current.containerRect.y = Math.max(\n                            current.containerRect.y,\n                            current.scrollParentYRect.y\n                        );\n                        current.containerRect.height = bottom - current.containerRect.y;\n                    }\n                }\n\n                // Element rect\n                ctx.current.elementRect = dom.getRect(element);\n            };\n\n            /**\n             * @param {Element} target\n             */\n            const willStartDrag = (target) => {\n                ctx.current.element = target.closest(ctx.elementSelector);\n                ctx.current.container = ctx.ref.el;\n\n                cleanup.add(() => (ctx.current = {}));\n                state.willDrag = true;\n\n                callBuildHandler(\"onWillStartDrag\");\n\n                if (hasTouch()) {\n                    // Prevents panning/zooming after a long press\n                    dom.addListener(window, \"touchmove\", safePrevent, {\n                        passive: false,\n                        noAddedStyle: true,\n                    });\n                    if (params.iframeWindow) {\n                        dom.addListener(params.iframeWindow, \"touchmove\", safePrevent, {\n                            passive: false,\n                            noAddedStyle: true,\n                        });\n                    }\n                }\n            };\n\n            // Initialize helpers\n            const cleanup = makeCleanupManager(() => (state.dragging = false));\n            const effectCleanup = makeCleanupManager();\n            const dom = makeDOMHelpers(cleanup);\n\n            const helpers = {\n                ...dom,\n                addCleanup: cleanup.add,\n                addEffectCleanup: effectCleanup.add,\n                callHandler,\n            };\n\n            // Component infos\n            const state = setupHooks.wrapState({ dragging: false });\n\n            // Basic error handling asserting that the parameters are valid.\n            for (const prop in allAcceptedParams) {\n                const type = typeof params[prop];\n                const acceptedTypes = allAcceptedParams[prop].map((t) => t.name.toLowerCase());\n                if (params[prop]) {\n                    if (!acceptedTypes.includes(type)) {\n                        throw makeError(\n                            `invalid type for property \"${prop}\" in parameters: expected { ${acceptedTypes.join(\n                                \", \"\n                            )} } and got ${type}`\n                        );\n                    }\n                } else if (MANDATORY_PARAMS.includes(prop) && !defaultParams[prop]) {\n                    throw makeError(`missing required property \"${prop}\" in parameters`);\n                }\n            }\n\n            /** @type {DraggableHookContext} */\n            const ctx = {\n                enable: () => false,\n                preventDrag: () => false,\n                ref: params.ref,\n                ignoreSelector: null,\n                fullSelector: null,\n                followCursor: true,\n                cursor: null,\n                pointer: { x: 0, y: 0 },\n                edgeScrolling: { enabled: true },\n                get dragging() {\n                    return state.dragging;\n                },\n                get willDrag() {\n                    return state.willDrag;\n                },\n                // Current context\n                current: {},\n            };\n\n            // Effect depending on the params to update them.\n            setupHooks.setup(\n                (...deps) => {\n                    const params = Object.fromEntries(deps);\n                    const actualParams = { ...defaultParams, ...omit(params, \"edgeScrolling\") };\n                    if (params.edgeScrolling) {\n                        actualParams.edgeScrolling = {\n                            ...actualParams.edgeScrolling,\n                            ...params.edgeScrolling,\n                        };\n                    }\n\n                    if (!ctx.ref.el) {\n                        return;\n                    }\n\n                    // Enable getter\n                    ctx.enable = actualParams.enable;\n\n                    // Dragging constraint\n                    if (actualParams.preventDrag) {\n                        ctx.preventDrag = actualParams.preventDrag;\n                    }\n\n                    // Selectors\n                    ctx.elementSelector = actualParams.elements;\n                    if (!ctx.elementSelector) {\n                        throw makeError(\n                            `no value found by \"elements\" selector: ${ctx.elementSelector}`\n                        );\n                    }\n                    const allSelectors = [ctx.elementSelector];\n                    ctx.cursor = actualParams.cursor || null;\n                    if (actualParams.handle) {\n                        allSelectors.push(actualParams.handle);\n                    }\n                    if (actualParams.ignore) {\n                        ctx.ignoreSelector = actualParams.ignore;\n                    }\n                    ctx.fullSelector = allSelectors.join(\" \");\n\n                    // Edge scrolling\n                    Object.assign(ctx.edgeScrolling, actualParams.edgeScrolling);\n\n                    // Delay & tolerance\n                    ctx.delay = actualParams.delay;\n                    ctx.touchDelay = actualParams.delay || actualParams.touchDelay;\n                    ctx.tolerance = actualParams.tolerance;\n\n                    callBuildHandler(\"onComputeParams\", { params: actualParams });\n\n                    // Calls effect cleanup functions when preparing to re-render.\n                    return effectCleanup.cleanup;\n                },\n                () => computeParams(params)\n            );\n            // Firefox currently (119.0.1) does not handle our pointer events\n            // nicely when they happen from within the iframe. To work around\n            // this, we use mouse events instead of pointer events.\n            const useMouseEvents = isBrowserFirefox() && !hasTouch() && params.iframeWindow;\n            // Effect depending on the `ref.el` to add triggering pointer events listener.\n            setupHooks.setup(\n                (el) => {\n                    if (el) {\n                        const { add, cleanup } = makeCleanupManager();\n                        const { addListener } = makeDOMHelpers({ add });\n                        const event = useMouseEvents ? \"mousedown\" : \"pointerdown\";\n                        addListener(el, event, onPointerDown, { noAddedStyle: true });\n                        addListener(el, \"click\", onClick);\n                        if (hasTouch()) {\n                            addListener(el, \"contextmenu\", safePrevent);\n                            // Adds a non-passive listener on touchstart: this allows\n                            // the subsequent \"touchmove\" events to be cancelable\n                            // and thus prevent parasitic \"touchcancel\" events to\n                            // be fired. Note that we DO NOT want to prevent touchstart\n                            // events since they're responsible of the native swipe\n                            // scrolling.\n                            addListener(el, \"touchstart\", () => {}, {\n                                passive: false,\n                                noAddedStyle: true,\n                            });\n                        }\n                        return cleanup;\n                    }\n                },\n                () => [ctx.ref.el]\n            );\n            const addWindowListener = (type, listener, options) => {\n                if (params.iframeWindow) {\n                    setupHooks.addListener(params.iframeWindow, type, listener, options);\n                }\n                setupHooks.addListener(window, type, listener, options);\n            };\n            // Other global event listeners.\n            const throttledOnPointerMove = setupHooks.throttle(onPointerMove);\n            addWindowListener(\n                useMouseEvents ? \"mousemove\" : \"pointermove\",\n                throttledOnPointerMove,\n                { passive: false }\n            );\n            addWindowListener(useMouseEvents ? \"mouseup\" : \"pointerup\", onPointerUp);\n            addWindowListener(\"pointercancel\", onPointerCancel);\n            addWindowListener(\"keydown\", onKeyDown, { capture: true });\n            setupHooks.teardown(() => dragEnd(null));\n\n            return state;\n        },\n    }[hookName];\n}\n", "import { onWillUnmount, reactive, useEffect, useExternalListener } from \"@odoo/owl\";\nimport { useThrottleForAnimation } from \"./timing\";\nimport { makeDraggableHook as nativeMakeDraggableHook } from \"./draggable_hook_builder\";\n\n/**\n * Set of default `makeDraggableHook` setup hooks that makes use of Owl lifecycle\n * and reactivity hooks to properly set up, update and tear down the elements and\n * listeners added by the draggable hook builder.\n *\n * @see {nativeMakeDraggableHook}\n * @type {typeof nativeMakeDraggableHook}\n */\nexport function makeDraggableHook(params) {\n    return nativeMakeDraggableHook({\n        ...params,\n        setupHooks: {\n            addListener: useExternalListener,\n            setup: useEffect,\n            teardown: onWillUnmount,\n            throttle: useThrottleForAnimation,\n            wrapState: reactive,\n        },\n    });\n}\n", "import { humanNumber } from \"@web/core/utils/numbers\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { session } from \"@web/session\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport const DEFAULT_MAX_FILE_SIZE = 128 * 1024 * 1024;\n\n/**\n * @param {Services[\"notification\"]} notificationService\n * @param {File} file\n * @param {Number} maxUploadSize\n * @returns {boolean}\n */\nexport function checkFileSize(fileSize, notificationService) {\n    const maxUploadSize = session.max_file_upload_size || DEFAULT_MAX_FILE_SIZE;\n    if (fileSize > maxUploadSize) {\n        notificationService.add(\n            _t(\n                \"The selected file (%(size)sB) is larger than the maximum allowed file size (%(maxSize)sB).\",\n                { size: humanNumber(fileSize), maxSize: humanNumber(maxUploadSize) }\n            ),\n            {\n                type: \"danger\",\n            }\n        );\n        return false;\n    }\n    return true;\n}\n\n/**\n * Hook to upload a file to the server.\n * @returns {function}\n */\nexport function useFileUploader() {\n    const http = useService(\"http\");\n    const notification = useService(\"notification\");\n    /**\n     * @param {string} route\n     * @param {Object} params\n     */\n    return async (route, params) => {\n        if ((params.ufile && params.ufile.length) || params.file) {\n            const fileSize = (params.ufile && params.ufile[0].size) || params.file.size;\n            if (!checkFileSize(fileSize, notification)) {\n                return null;\n            }\n        }\n        const fileData = await http.post(route, params, \"text\");\n        const parsedFileData = JSON.parse(fileData);\n        if (parsedFileData.error) {\n            throw new Error(parsedFileData.error);\n        }\n        return parsedFileData;\n    };\n}\n", "/**\n * Creates a version of the function that's memoized on the value of its first\n * argument, if any.\n *\n * @template T, U\n * @param {(arg: T) => U} func the function to memoize\n * @returns {(arg: T) => U} a memoized version of the original function\n */\nexport function memoize(func) {\n    const cache = new Map();\n    const funcName = func.name ? func.name + \" (memoized)\" : \"memoized\";\n    return {\n        [funcName](...args) {\n            if (!cache.has(args[0])) {\n                cache.set(args[0], func(...args));\n            }\n            return cache.get(...args);\n        },\n    }[funcName];\n}\n\n/**\n * Generate a unique integer id (unique within the entire client session).\n * Useful for temporary DOM ids.\n *\n * @param {string} prefix\n * @returns {string}\n */\nexport function uniqueId(prefix = \"\") {\n    return `${prefix}${++uniqueId.nextId}`;\n}\n// set nextId on the function itself to be able to patch then\nuniqueId.nextId = 0;\n", "import { hasTouch, isMobileOS } from \"@web/core/browser/feature_detection\";\n\nimport { status, useComponent, useEffect, useRef, onWillUnmount } from \"@odoo/owl\";\n\n/**\n * This file contains various custom hooks.\n * Their inner working is rather simple:\n * Each custom hook simply hooks itself to any number of owl lifecycle hooks.\n * You can then use them just like an owl hook in any Component\n * e.g.:\n * import { useBus } from \"@web/core/utils/hooks\";\n * ...\n * setup() {\n *    ...\n *    useBus(someBus, someEvent, callback)\n *    ...\n * }\n */\n\n/**\n * @typedef {{ readonly el: HTMLElement | null; }} Ref\n */\n\n// -----------------------------------------------------------------------------\n// useAutofocus\n// -----------------------------------------------------------------------------\n\n/**\n * Focus an element referenced by a t-ref=\"autofocus\" in the active component\n * as soon as it appears in the DOM and if it was not displayed before.\n * If it is an input/textarea, set the selection at the end.\n * @param {Object} [params]\n * @param {string} [params.refName] override the ref name \"autofocus\"\n * @param {boolean} [params.selectAll] if true, will select the entire text value.\n * @param {boolean} [params.mobile] if true, will force autofocus on touch devices.\n * @returns {Ref} the element reference\n */\nexport function useAutofocus({ refName, selectAll, mobile } = {}) {\n    const ref = useRef(refName || \"autofocus\");\n    const uiService = useService(\"ui\");\n\n    // Prevent autofocus on touch devices to avoid the virtual keyboard from popping up unexpectedly\n    if (!mobile && hasTouch()) {\n        return ref;\n    }\n    // LEGACY\n    if (!mobile && isMobileOS()) {\n        return ref;\n    }\n    // LEGACY\n    useEffect(\n        (el) => {\n            if (el && (!uiService.activeElement || uiService.activeElement.contains(el))) {\n                el.focus();\n                if ([\"INPUT\", \"TEXTAREA\"].includes(el.tagName) && el.type !== \"number\") {\n                    el.selectionEnd = el.value.length;\n                    el.selectionStart = selectAll ? 0 : el.value.length;\n                }\n            }\n        },\n        () => [ref.el]\n    );\n    return ref;\n}\n\n// -----------------------------------------------------------------------------\n// useBus\n// -----------------------------------------------------------------------------\n\n/**\n * Ensures a bus event listener is attached and cleared the proper way.\n *\n * @param {import(\"@odoo/owl\").EventBus} bus\n * @param {string} eventName\n * @param {EventListener} callback\n */\nexport function useBus(bus, eventName, callback) {\n    const component = useComponent();\n    useEffect(\n        () => {\n            const listener = callback.bind(component);\n            bus.addEventListener(eventName, listener);\n            return () => bus.removeEventListener(eventName, listener);\n        },\n        () => []\n    );\n}\n\n// In an object so that it can be patched in tests (prevent error on blocking RPCs after tests)\nexport const useServiceProtectMethodHandling = {\n    fn() {\n        return this.original();\n    },\n    mocked() {\n        // Keep them unresolved so that no crash in test due to triggered RPCs by services\n        return new Promise(() => {});\n    },\n    original() {\n        return Promise.reject(new Error(\"Component is destroyed\"));\n    },\n};\n\n// -----------------------------------------------------------------------------\n// useService\n// -----------------------------------------------------------------------------\nfunction _protectMethod(component, fn) {\n    return function (...args) {\n        if (status(component) === \"destroyed\") {\n            return useServiceProtectMethodHandling.fn();\n        }\n\n        const prom = Promise.resolve(fn.call(this, ...args));\n        const protectedProm = prom.then((result) =>\n            status(component) === \"destroyed\" ? new Promise(() => {}) : result\n        );\n        return Object.assign(protectedProm, {\n            abort: prom.abort,\n            cancel: prom.cancel,\n        });\n    };\n}\n\nexport const SERVICES_METADATA = {};\n\n/**\n * Import a service into a component\n *\n * @template {keyof import(\"services\").ServiceFactories} K\n * @param {K} serviceName\n * @returns {import(\"services\").ServiceFactories[K]}\n */\nexport function useService(serviceName) {\n    const component = useComponent();\n    const { services } = component.env;\n    if (!(serviceName in services)) {\n        throw new Error(`Service ${serviceName} is not available`);\n    }\n    const service = services[serviceName];\n    if (serviceName in SERVICES_METADATA) {\n        if (service instanceof Function) {\n            return _protectMethod(component, service);\n        } else {\n            const methods = SERVICES_METADATA[serviceName];\n            const result = Object.create(service);\n            for (const method of methods) {\n                result[method] = _protectMethod(component, service[method]);\n            }\n            return result;\n        }\n    }\n    return service;\n}\n\n// -----------------------------------------------------------------------------\n// useSpellCheck\n// -----------------------------------------------------------------------------\n\n/**\n * To avoid elements to keep their spellcheck appearance when they are no\n * longer in focus. We only add this attribute when needed. To disable this\n * behavior, use the spellcheck attribute on the element.\n */\nexport function useSpellCheck({ refName } = {}) {\n    const elements = [];\n    const ref = useRef(refName || \"spellcheck\");\n    function toggleSpellcheck(ev) {\n        ev.target.spellcheck = document.activeElement === ev.target;\n    }\n    useEffect(\n        (el) => {\n            if (el) {\n                const inputs =\n                    [\"INPUT\", \"TEXTAREA\"].includes(el.nodeName) || el.isContentEditable\n                        ? [el]\n                        : el.querySelectorAll(\"input, textarea, [contenteditable=true]\");\n                inputs.forEach((input) => {\n                    if (input.spellcheck !== false) {\n                        elements.push(input);\n                        input.addEventListener(\"focus\", toggleSpellcheck);\n                        input.addEventListener(\"blur\", toggleSpellcheck);\n                    }\n                });\n            }\n            return () => {\n                elements.forEach((input) => {\n                    input.removeEventListener(\"focus\", toggleSpellcheck);\n                    input.removeEventListener(\"blur\", toggleSpellcheck);\n                });\n            };\n        },\n        () => [ref.el]\n    );\n}\n\n/**\n * @typedef {Function} ForwardRef\n * @property {HTMLElement | undefined} el\n */\n\n/**\n * Use a ref that was forwarded by a child @see useForwardRefToParent\n *\n * @returns {ForwardRef} a ref that can be called to set its value to that of a\n *  child ref, but can otherwise be used as a normal ref object\n */\nexport function useChildRef() {\n    let defined = false;\n    let value;\n    return function ref(v) {\n        value = v;\n        if (defined) {\n            return;\n        }\n        Object.defineProperty(ref, \"el\", {\n            get() {\n                return value.el;\n            },\n        });\n        defined = true;\n    };\n}\n/**\n * Forwards the given refName to the parent by calling the corresponding\n * ForwardRef received as prop. @see useChildRef\n *\n * @param {string} refName name of the ref to forward\n * @returns {Ref} the same ref that is forwarded to the\n *  parent\n */\nexport function useForwardRefToParent(refName) {\n    const component = useComponent();\n    const ref = useRef(refName);\n    if (component.props[refName]) {\n        component.props[refName](ref);\n    }\n    return ref;\n}\n/**\n * Use the dialog service while also automatically closing the dialogs opened\n * by the current component when it is unmounted.\n *\n * @returns {import(\"@web/core/dialog/dialog_service\").DialogServiceInterface}\n */\nexport function useOwnedDialogs() {\n    const dialogService = useService(\"dialog\");\n    const cbs = [];\n    onWillUnmount(() => {\n        cbs.forEach((cb) => cb());\n    });\n    const addDialog = (...args) => {\n        const close = dialogService.add(...args);\n        cbs.push(close);\n        return close;\n    };\n    return addDialog;\n}\n/**\n * Manages an event listener on a ref. Useful for hooks that want to manage\n * event listeners, especially more than one. Prefer using t-on directly in\n * components. If your hook only needs a single event listener, consider simply\n * returning it from the hook and letting the user attach it with t-on.\n *\n * @param {Ref} ref\n * @param {Parameters<typeof EventTarget.prototype.addEventListener>} listener\n */\nexport function useRefListener(ref, ...listener) {\n    useEffect(\n        (el) => {\n            el?.addEventListener(...listener);\n            return () => el?.removeEventListener(...listener);\n        },\n        () => [ref.el]\n    );\n}\n", "const eventHandledWeakMap = new WeakMap();\n/**\n * Returns whether the given event has been handled with the given markName.\n *\n * @param {Event} ev\n * @param {string} markName\n * @returns {boolean}\n */\nexport function isEventHandled(ev, markName) {\n    if (!eventHandledWeakMap.get(ev)) {\n        return false;\n    }\n    return eventHandledWeakMap.get(ev).includes(markName);\n}\n/**\n * Marks the given event as handled by the given markName. Useful to allow\n * handlers in the propagation chain to make a decision based on what has\n * already been done.\n *\n * @param {Event} ev\n * @param {string} markName\n */\nexport function markEventHandled(ev, markName) {\n    if (!eventHandledWeakMap.get(ev)) {\n        eventHandledWeakMap.set(ev, []);\n    }\n    eventHandledWeakMap.get(ev).push(markName);\n}\n", "import { localization } from \"@web/core/l10n/localization\";\nimport { makeDraggableHook } from \"@web/core/utils/draggable_hook_builder_owl\";\n\n/** @typedef {import(\"@web/core/utils/draggable_hook_builder\").DraggableHandlerParams} DraggableHandlerParams */\n/** @typedef {DraggableHandlerParams & { group: HTMLElement | null }} NestedSortableHandlerParams */\n\n/**\n * @typedef {import(\"./sortable\").SortableParams} NestedSortableParams\n *\n * OPTIONAL\n *\n * @property {(HTMLElement) => boolean} [preventDrag] function receiving a\n *  the current target for dragging (element) and returning a boolean, whether\n *  the element can be effectively dragged or not.\n * @property {boolean | () => boolean} [nest] whether elements are nested or not.\n * @property {string | () => string} [listTagName] type of lists (\"ul\" or \"ol\").\n * @property {number | () => number} [nestInterval] Horizontal distance needed to trigger\n * a change in the list hierarchy (i.e. changing parent when moving horizontally)\n * @property {number | () => number} [maxLevels] The maximum depth of nested items\n * the list can accept. If set to '0' the levels are unlimited. Default: 0\n * @property {(DraggableHookContext) => boolean} [isAllowed] You can specify a custom function\n * to verify if a drop location is allowed. return True by default\n * @property {boolean} [useElementSize] The placeholder use the dragged element size instead\n * of the small 8px lines. Default:false\n *\n * HANDLERS (also optional)\n *\n * @property {(params: MoveParams) => any} [onMove] called when the element has moved\n * (changed position) (@see MoveParams).\n */\n\n/**\n * @typedef MoveParams\n * @property {HTMLElement} element\n * @property {HTMLElement | null} group\n * @property {HTMLElement | null} previous\n * @property {HTMLElement | null} next\n * @property {HTMLElement | null} newGroup\n * @property {HTMLElement | null} parent\n * @property {HTMLElement} placeholder\n */\n\n/**\n * @typedef SortableState\n * @property {boolean} dragging\n */\n\n/** @type {(params: NestedSortableParams) => SortableState} */\nexport const useNestedSortable = makeDraggableHook({\n    name: \"useNestedSortable\",\n    acceptedParams: {\n        groups: [String, Function],\n        connectGroups: [Boolean, Function],\n        nest: [Boolean],\n        listTagName: [String],\n        nestInterval: [Number],\n        maxLevels: [Number],\n        isAllowed: [Function],\n        useElementSize: [Boolean],\n    },\n    defaultParams: {\n        connectGroups: false,\n        currentGroup: null,\n        cursor: \"grabbing\",\n        edgeScrolling: { speed: 20, threshold: 60 },\n        elements: \"li\",\n        groupSelector: null,\n        nest: false,\n        listTagName: \"ul\",\n        nestInterval: 15,\n        maxLevels: 0,\n        isAllowed: (ctx) => true,\n        useElementSize: false,\n    },\n\n    // Set the parameters.\n    onComputeParams({ ctx, params }) {\n        // Group selector\n        ctx.groupSelector = params.groups || null;\n        if (ctx.groupSelector) {\n            ctx.fullSelector = [ctx.groupSelector, ctx.fullSelector].join(\" \");\n        }\n        // Connection across groups\n        ctx.connectGroups = params.connectGroups;\n        // Nested elements\n        ctx.nest = params.nest;\n        // List tag name\n        ctx.listTagName = params.listTagName;\n        // Horizontal distance needed to trigger a change in the list hierarchy\n        // (i.e. changing parent when moving horizontally)\n        ctx.nestInterval = params.nestInterval;\n        ctx.isRTL = localization.direction === \"rtl\";\n        ctx.maxLevels = params.maxLevels || 0;\n        ctx.isAllowed = params.isAllowed ?? (() => true);\n        ctx.useElementSize = params.useElementSize;\n    },\n\n    // Set the current group and create the placeholder row that will take the\n    // place of the moving row.\n    onWillStartDrag({ ctx, addCleanup }) {\n        if (ctx.groupSelector) {\n            ctx.currentGroup = ctx.current.element.closest(ctx.groupSelector);\n            if (!ctx.connectGroups) {\n                ctx.current.container = ctx.currentGroup;\n            }\n        }\n\n        if (ctx.nest) {\n            ctx.prevNestX = ctx.pointer.x;\n        }\n        ctx.current.placeHolder = ctx.current.element.cloneNode(false);\n        ctx.current.placeHolder.removeAttribute(\"id\");\n        ctx.current.placeHolder.classList.add(\"w-100\", \"d-block\");\n        if (ctx.useElementSize) {\n            ctx.current.placeHolder.style.height = getComputedStyle(ctx.current.element).height;\n            ctx.current.placeHolder.classList.add(\"o_nested_sortable_placeholder_realsize\");\n        } else {\n            ctx.current.placeHolder.classList.add(\"o_nested_sortable_placeholder\");\n        }\n        addCleanup(() => ctx.current.placeHolder.remove());\n    },\n\n    // Make the placeholder take the place of the moving row, and add style on\n    // different elements to provide feedback that there is an ongoing dragging\n    // sequence.\n    onDragStart({ ctx, addStyle }) {\n        // Horizontal position which will be used to detect row changes when moving vertically, so that\n        // we do not need to be on the row to trigger row changes (only the vertical position matters).\n        // Nested rows are shorter than \"root\" rows, and do not start at the same horizontal position.\n        // However, every row ends at the same horizontal position. Therefore, we use the end of the\n        // current element - 1 as horizontal position.\n        ctx.selectorX = ctx.isRTL\n            ? ctx.current.elementRect.left + 1\n            : ctx.current.elementRect.right - 1;\n\n        // Placeholder is initially added right after the current element.\n        ctx.current.element.after(ctx.current.placeHolder);\n        addStyle(ctx.current.element, { opacity: 0.5 });\n\n        // Remove pointer-events style added by draggable_hook_builder and set\n        // it on the view elements instead as in our case we want to show the\n        // ctx.cursor style on the whole screen, not only in the ref el.\n        addStyle(document.body, { \"pointer-events\": \"auto\" });\n        addStyle(document.querySelector(\".o_navbar\"), { \"pointer-events\": \"none\" });\n        addStyle(document.querySelector(\".o_action_manager\"), { \"pointer-events\": \"none\" });\n        addStyle(ctx.current.container, { \"pointer-events\": \"auto\" });\n\n        // Calls \"onDragStart\" handler\n        return {\n            element: ctx.current.element,\n            group: ctx.currentGroup,\n        };\n    },\n    _getDeepestChildLevel(ctx, node, depth = 0) {\n        let result = 0;\n        const childSelector = `${ctx.listTagName} ${ctx.elementSelector}`;\n        for (const childNode of node.querySelectorAll(childSelector)) {\n            result = Math.max(this._getDeepestChildLevel(ctx, childNode, depth + 1), result);\n        }\n        return depth ? result + 1 : result;\n    },\n    _hasReachMaxAllowedLevel(ctx) {\n        if (!ctx.nest || ctx.maxLevels < 1) {\n            return false;\n        }\n        let level = this._getDeepestChildLevel(ctx, ctx.current.element);\n        let list = ctx.current.placeHolder.closest(ctx.listTagName);\n        while (list) {\n            level++;\n            list = list.parentNode.closest(ctx.listTagName);\n        }\n        return level > ctx.maxLevels;\n    },\n    _isAllowedNodeMove(ctx) {\n        return (\n            !this._hasReachMaxAllowedLevel(ctx) && ctx.isAllowed(ctx.current, ctx.elementSelector)\n        );\n    },\n    // Check if the cursor moved enough to trigger a move. If it did, move the\n    // placeholder accordingly.\n    onDrag({ ctx, callHandler }) {\n        const onMove = (prevPos) => {\n            if (!this._isAllowedNodeMove(ctx)) {\n                ctx.current.placeHolder.classList.add(\"d-none\");\n                return;\n            }\n            ctx.current.placeHolder.classList.remove(\"d-none\");\n            callHandler(\"onMove\", {\n                element: ctx.current.element,\n                previous: ctx.current.placeHolder.previousElementSibling,\n                next: ctx.current.placeHolder.nextElementSibling,\n                parent: ctx.nest\n                    ? ctx.current.placeHolder.parentElement.closest(ctx.elementSelector)\n                    : false,\n                group: ctx.currentGroup,\n                newGroup: ctx.connectGroups\n                    ? ctx.current.placeHolder.closest(ctx.groupSelector)\n                    : ctx.currentGroup,\n                prevPos,\n                placeholder: ctx.current.placeHolder,\n            });\n        };\n        /**\n         * Get the list element inside an element, or create one if it does not\n         * exists.\n         * @param {HTMLElement} el\n         * @return {HTMLElement} list\n         */\n        const getChildList = (el) => {\n            let list = el.querySelector(ctx.listTagName);\n            if (!list) {\n                list = document.createElement(ctx.listTagName);\n                el.appendChild(list);\n            }\n            return list;\n        };\n\n        const getPosition = (el) => {\n            return {\n                previous: el.previousElementSibling,\n                next: el.nextElementSibling,\n                parent: el.parentElement?.closest(ctx.elementSelector) || null,\n                group: ctx.groupSelector ? el.closest(ctx.groupSelector) : false,\n            };\n        };\n        const position = getPosition(ctx.current.placeHolder);\n\n        /** If nesting elements is allowed, horizontal moves may change the\n         * parent of the placeholder element (the placeholder does not move\n         * above or under an element, but it changes parent):\n         *\n         * - Moving to the left makes the placeholder a child of the previous\n         *   element up in the nested hierarchy, only if the placeholder is the\n         *   last child of its current parent:\n         *\n         *                    Allowed:\n         *    el                           el\n         *     \u2523 parent                     \u2523 parent\n         *     \u2503  \u2523 child           -->     \u2503  \u2517 child\n         *     \u2503  \u2517 placeholder             \u2523 placeholder\n         *     \u2517 el                         \u2517 el\n         *\n         *                  Not Allowed:\n         *    el                           el\n         *     \u2523 parent                     \u2523 parent\n         *     \u2503  \u2523 placeholder     -->     \u2523 p\u2503laceholder   <-- error\n         *     \u2503  \u2517 child                   \u2503  \u2517 child\n         *     \u2517 el                         \u2517 el\n         *\n         *\n         * - Moving to the right makes the placeholder the last child of the\n         * next element down in the nested hierarchy:\n         *\n         *    el                           el\n         *     \u2523 parent                    \u2523 parent\n         *     \u2503  \u2517 child           -->    \u2503  \u2523 child\n         *     \u2523 placeholder               \u2503  \u2517 placeholder\n         *     \u2517 el                        \u2517 el\n         */\n        if (ctx.nest) {\n            const xInterval = ctx.prevNestX - ctx.pointer.x;\n            if (ctx.nestInterval - (-1) ** ctx.isRTL * xInterval < 1) {\n                // Place placeholder after its parent in its parent's list only\n                // if the placeholder is the last child of its parent\n                // (ignoring the current element which is in the dom)\n                let nextElement = position.next;\n                if (nextElement === ctx.current.element) {\n                    nextElement = nextElement.nextElementSibling;\n                }\n                if (!nextElement) {\n                    const newSibling = position.parent;\n                    if (newSibling) {\n                        newSibling.after(ctx.current.placeHolder);\n                        onMove(position);\n                    }\n                }\n                // Recenter the pointer coordinates to this step\n                ctx.prevNestX = ctx.pointer.x;\n                return;\n            } else if (ctx.nestInterval + (-1) ** ctx.isRTL * xInterval < 1) {\n                // Place placeholder as the last child of its previous sibling,\n                // (ignoring the current element which is in the dom)\n                let parent = position.previous;\n                if (parent === ctx.current.element) {\n                    parent = parent.previousElementSibling;\n                }\n                if (parent && parent.matches(ctx.elementSelector)) {\n                    getChildList(parent).appendChild(ctx.current.placeHolder);\n                    onMove(position);\n                }\n                // Recenter the pointer coordinates to this step\n                ctx.prevNestX = ctx.pointer.x;\n                return;\n            }\n        }\n        const currentTop = ctx.pointer.y - ctx.current.offset.y;\n        const closestEl = document.elementFromPoint(ctx.selectorX, currentTop);\n        if (!closestEl) {\n            // Cursor outside of viewport\n            return;\n        }\n        const element = closestEl.closest(ctx.elementSelector);\n        // Vertical moves should move the placeholder element up or down.\n        if (element && element !== ctx.current.placeHolder) {\n            const elementPosition = getPosition(element);\n            const eRect = element.getBoundingClientRect();\n            const pos = ctx.current.placeHolder.compareDocumentPosition(element);\n            // Place placeholder before the hovered element in its parent's\n            // list. If the cursor is in the upper part of the element and\n            // if the placeholder is currently after or inside the hovered\n            // element. If the position is not allowed but nesting is allowed,\n            // place the placeholder as the last child of the previous sibling\n            // instead.\n            if (currentTop - eRect.y < 10) {\n                if (\n                    pos & Node.DOCUMENT_POSITION_PRECEDING &&\n                    (ctx.nest || elementPosition.parent === position.parent)\n                ) {\n                    element.before(ctx.current.placeHolder);\n                    onMove(position);\n                    // Recenter the pointer coordinates to this step\n                    ctx.prevNestX = ctx.pointer.x;\n                }\n            } else if (currentTop - eRect.y > 15 && pos === Node.DOCUMENT_POSITION_FOLLOWING) {\n                // Place placeholder after the hovered element in its parent's\n                // list if the cursor is not in the upper part of the\n                // element and if the placeholder is currently before the\n                // hovered element.\n                // If nesting is allowed and if the element has at least one\n                // child, place the placeholder above the first child of the\n                // hovered element instead.\n                if (ctx.nest) {\n                    const elementChildList = getChildList(element);\n                    if (elementChildList.querySelector(ctx.elementSelector)) {\n                        elementChildList.prepend(ctx.current.placeHolder);\n                        onMove(position);\n                    } else {\n                        element.after(ctx.current.placeHolder);\n                        onMove(position);\n                    }\n                    // Recenter the pointer coordinates to this step\n                    ctx.prevNestX = ctx.pointer.x;\n                } else if (elementPosition.parent === position.parent) {\n                    element.after(ctx.current.placeHolder);\n                    onMove(position);\n                }\n            }\n        } else {\n            const group = closestEl.closest(ctx.groupSelector);\n            if (group && group !== position.group && (ctx.nest || !position.parent)) {\n                if (\n                    group.compareDocumentPosition(position.group) ===\n                    Node.DOCUMENT_POSITION_PRECEDING\n                ) {\n                    getChildList(group).prepend(ctx.current.placeHolder);\n                    onMove(position);\n                } else {\n                    getChildList(group).appendChild(ctx.current.placeHolder);\n                    onMove(position);\n                }\n                // Recenter the pointer coordinates to this step\n                ctx.prevNestX = ctx.pointer.x;\n                callHandler(\"onGroupEnter\", { group, placeholder: ctx.current.placeHolder });\n                callHandler(\"onGroupLeave\", {\n                    group: position.group,\n                    placeholder: ctx.current.placeHolder,\n                });\n            }\n        }\n    },\n    // If the drop position is different from the starting position, run the\n    // onDrop handler from the parameters.\n    onDrop({ ctx }) {\n        if (!this._isAllowedNodeMove(ctx)) {\n            return;\n        }\n        const previous = ctx.current.placeHolder.previousElementSibling;\n        const next = ctx.current.placeHolder.nextElementSibling;\n        if (previous !== ctx.current.element && next !== ctx.current.element) {\n            return {\n                element: ctx.current.element,\n                group: ctx.currentGroup,\n                previous,\n                next,\n                newGroup: ctx.groupSelector && ctx.current.placeHolder.closest(ctx.groupSelector),\n                parent: ctx.current.placeHolder.parentElement.closest(ctx.elementSelector),\n                placeholder: ctx.current.placeHolder,\n            };\n        }\n    },\n    // Run the onDragEnd handler from the parameters.\n    onDragEnd({ ctx }) {\n        return {\n            element: ctx.current.element,\n            group: ctx.currentGroup,\n        };\n    },\n});\n", "import { localization as l10n } from \"@web/core/l10n/localization\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { intersperse } from \"@web/core/utils/strings\";\n\n/**\n * Returns value clamped to the inclusive range of min and max.\n *\n * @param {number} num\n * @param {number} min\n * @param {number} max\n * @returns {number}\n */\nexport function clamp(num, min, max) {\n    return Math.max(Math.min(num, max), min);\n}\n\n/**\n * A function to create flexibly-numbered lists of integers, handy for each and map loops.\n * step defaults to 1.\n * Returns a list of integers from start (inclusive) to stop (exclusive), incremented (or decremented) by step.\n * @param {number} start default 0\n * @param {number} stop\n * @param {number} step default 1\n * @returns {number[]}\n */\nexport function range(start, stop, step = 1) {\n    const array = [];\n    const nsteps = Math.floor((stop - start) / step);\n    for (let i = 0; i < nsteps; i++) {\n        array.push(start + step * i);\n    }\n    return array;\n}\n\n/**\n * Returns `value` rounded with `precision`, minimizing IEEE-754 floating point\n * representation errors, and applying the tie-breaking rule selected with\n * `method`, by default \"HALF-UP\" (away from zero).\n *\n * @param {number} value the value to be rounded\n * @param {number} precision a precision parameter. eg: 0.01 rounds to two digits.\n * @param {\"HALF-UP\" | \"HALF-DOWN\" | \"HALF-EVEN\" | \"UP\" | \"DOWN\"} [method=\"HALF-UP\"] the rounding method used:\n *    - \"HALF-UP\" rounds to the closest number with ties going away from zero.\n *    - \"HALF-DOWN\" rounds to the closest number with ties going towards zero.\n *    - \"HALF-EVEN\" rounds to the closest number with ties going to the closest even number.\n *    - \"UP\" always rounds away from 0.\n *    - \"DOWN\" always rounds towards 0.\n */\nexport function roundPrecision(value, precision, method = \"HALF-UP\") {\n    if (!value) {\n        return 0;\n    } else if (!precision || precision < 0) {\n        precision = 1;\n    }\n    let roundingFactor = precision;\n    let normalize = (val) => val / roundingFactor;\n    let denormalize = (val) => val * roundingFactor;\n    // inverting small rounding factors reduces rounding errors\n    if (roundingFactor < 1) {\n        roundingFactor = invertFloat(roundingFactor);\n        [normalize, denormalize] = [denormalize, normalize];\n    }\n    const normalizedValue = normalize(value);\n    const sign = Math.sign(normalizedValue);\n    const epsilonMagnitude = Math.log2(Math.abs(normalizedValue));\n    const epsilon = Math.pow(2, epsilonMagnitude - 50);\n    let roundedValue;\n\n    switch (method) {\n        case \"DOWN\": {\n            roundedValue = Math.trunc(normalizedValue + sign * epsilon);\n            break;\n        }\n        case \"HALF-DOWN\": {\n            roundedValue = Math.round(normalizedValue - sign * epsilon);\n            break;\n        }\n        case \"HALF-UP\": {\n            roundedValue = Math.round(normalizedValue + sign * epsilon);\n            break;\n        }\n        case \"HALF-EVEN\": {\n            const integral = Math.floor(normalizedValue);\n            const remainder = Math.abs(normalizedValue - integral);\n            const isHalf = Math.abs(0.5 - remainder) < epsilon;\n            roundedValue = isHalf ? integral + (integral & 1) : Math.round(normalizedValue);\n            break;\n        }\n        case \"UP\": {\n            roundedValue = Math.trunc(normalizedValue + sign * (1 - epsilon));\n            break;\n        }\n        default: {\n            throw new Error(`Unknown rounding method: ${method}`);\n        }\n    }\n\n    return denormalize(roundedValue);\n}\n\nexport function roundDecimals(value, decimals) {\n    /**\n     * The following decimals introduce numerical errors:\n     * Math.pow(10, -4) = 0.00009999999999999999\n     * Math.pow(10, -5) = 0.000009999999999999999\n     *\n     * Such errors will propagate in roundPrecision and lead to inconsistencies between Python\n     * and JavaScript. To avoid this, we parse the scientific notation.\n     */\n    return roundPrecision(value, parseFloat(\"1e\" + -decimals));\n}\n\n/**\n * @param {number} value\n * @param {integer} decimals\n * @returns {boolean}\n */\nexport function floatIsZero(value, decimals) {\n    return value === 0 || roundDecimals(value, decimals) === 0;\n}\n\n/**\n * Inserts \"thousands\" separators in the provided number.\n *\n * @param {string} string representing integer number\n * @param {string} [thousandsSep=\",\"] the separator to insert\n * @param {number[]} [grouping=[]]\n *   array of relative offsets at which to insert `thousandsSep`.\n *   See `strings.intersperse` method.\n * @returns {string}\n */\nexport function insertThousandsSep(number, thousandsSep = \",\", grouping = []) {\n    const negative = number[0] === \"-\";\n    number = negative ? number.slice(1) : number;\n    return (negative ? \"-\" : \"\") + intersperse(number, grouping, thousandsSep);\n}\n\n/**\n * Format a number to a human readable format. For example, 3000 could become 3k.\n * Or massive number can use the scientific exponential notation.\n *\n * @param {number} number to format\n * @param {Object} [options] Options to format\n * @param {number} [options.decimals=0] number of decimals to use\n *    if minDigits > 1 is used and effective on the number then decimals\n *    will be shrunk to zero, to avoid displaying irrelevant figures ( 0.01 compared to 1000 )\n * @param {number} [options.minDigits=1]\n *    the minimum number of digits to preserve when switching to another\n *    level of thousands (e.g. with a value of '2', 4321 will still be\n *    represented as 4321 otherwise it will be down to one digit (4k))\n * @returns {string}\n */\nexport function humanNumber(number, options = { decimals: 0, minDigits: 1 }) {\n    const decimals = options.decimals || 0;\n    const minDigits = options.minDigits || 1;\n    const d2 = Math.pow(10, decimals);\n    const numberMagnitude = +number.toExponential().split(\"e+\")[1];\n    number = Math.round(number * d2) / d2;\n    // the case numberMagnitude >= 21 corresponds to a number\n    // better expressed in the scientific format.\n    if (numberMagnitude >= 21) {\n        // we do not use number.toExponential(decimals) because we want to\n        // avoid the possible useless O decimals: 1e.+24 preferred to 1.0e+24\n        number = Math.round(number * Math.pow(10, decimals - numberMagnitude)) / d2;\n        return `${number}e+${numberMagnitude}`;\n    }\n    // note: we need to call toString here to make sure we manipulate the resulting\n    // string, not an object with a toString method.\n    const unitSymbols = _t(\"kMGTPE\").toString();\n    const sign = Math.sign(number);\n    number = Math.abs(number);\n    let symbol = \"\";\n    for (let i = unitSymbols.length; i > 0; i--) {\n        const s = Math.pow(10, i * 3);\n        if (s <= number / Math.pow(10, minDigits - 1)) {\n            number = Math.round((number * d2) / s) / d2;\n            symbol = unitSymbols[i - 1];\n            break;\n        }\n    }\n    const { decimalPoint, grouping, thousandsSep } = l10n;\n\n    // determine if we should keep the decimals (we don't want to display 1,020.02k for 1020020)\n    const decimalsToKeep = number >= 1000 ? 0 : decimals;\n    number = sign * number;\n    const [integerPart, decimalPart] = number.toFixed(decimalsToKeep).split(\".\");\n    const int = insertThousandsSep(integerPart, thousandsSep, grouping);\n    if (!decimalPart) {\n        return int + symbol;\n    }\n    return int + decimalPoint + decimalPart + symbol;\n}\n\n/**\n * Returns a string representing a float.  The result takes into account the\n * user settings (to display the correct decimal separator).\n *\n * @param {number} value the value that should be formatted\n * @param {Object} [options]\n * @param {number[]} [options.digits] the number of digits that should be used,\n *   instead of the default digits precision in the field.\n * @param {boolean} [options.humanReadable] if true, large numbers are formatted\n *   to a human readable format.\n * @param {string} [options.decimalPoint] decimal separating character\n * @param {string} [options.thousandsSep] thousands separator to insert\n * @param {number[]} [options.grouping] array of relative offsets at which to\n *   insert `thousandsSep`. See `insertThousandsSep` method.\n * @param {number} [options.decimals] used for humanNumber formmatter\n * @param {boolean} [options.trailingZeros=true] if false, the decimal part\n *   won't contain unnecessary trailing zeros.\n * @returns {string}\n */\nexport function formatFloat(value, options = {}) {\n    if (options.humanReadable) {\n        return humanNumber(value, options);\n    }\n    const grouping = options.grouping || l10n.grouping;\n    const thousandsSep = \"thousandsSep\" in options ? options.thousandsSep : l10n.thousandsSep;\n    const decimalPoint = \"decimalPoint\" in options ? options.decimalPoint : l10n.decimalPoint;\n    let precision;\n    if (options.digits && options.digits[1] !== undefined) {\n        precision = options.digits[1];\n    } else {\n        precision = 2;\n    }\n    const formatted = value.toFixed(precision).split(\".\");\n    formatted[0] = insertThousandsSep(formatted[0], thousandsSep, grouping);\n    if (options.trailingZeros === false && formatted[1]) {\n        formatted[1] = formatted[1].replace(/0+$/, \"\");\n    }\n    return formatted[1] ? formatted.join(decimalPoint) : formatted[0];\n}\n\nconst _INVERTDICT = Object.freeze({\n    1e-1: 1e+1, 1e-2: 1e+2, 1e-3: 1e+3, 1e-4: 1e+4, 1e-5: 1e+5,\n    1e-6: 1e+6, 1e-7: 1e+7, 1e-8: 1e+8, 1e-9: 1e+9, 1e-10: 1e+10,\n    2e-1: 5e+0, 2e-2: 5e+1, 2e-3: 5e+2, 2e-4: 5e+3, 2e-5: 5e+4,\n    2e-6: 5e+5, 2e-7: 5e+6, 2e-8: 5e+7, 2e-9: 5e+8, 2e-10: 5e+9,\n    5e-1: 2e+0, 5e-2: 2e+1, 5e-3: 2e+2, 5e-4: 2e+3, 5e-5: 2e+4,\n    5e-6: 2e+5, 5e-7: 2e+6, 5e-8: 2e+7, 5e-9: 2e+8, 5e-10: 2e+9,\n});\n\n/**\n * Invert a number with increased accuracy.\n *\n * @param {number} value\n * @returns {number}\n */\nexport function invertFloat(value) {\n    let res = _INVERTDICT[value];\n    if (res === undefined) {\n        const [coeff, expt] = value.toExponential().split(\"e\").map(Number.parseFloat);\n        res = Number.parseFloat(`${coeff}e${-expt}`) / Math.pow(coeff, 2);\n    }\n    return res;\n}\n", "/**\n * Shallow compares two objects.\n *\n * @template {unknown} T\n * @param {T} obj1\n * @param {T} obj2\n * @param {(a: T[keyof T], b: T[keyof T]) => boolean} [comparisonFn]\n */\nexport function shallowEqual(obj1, obj2, comparisonFn = (a, b) => a === b) {\n    if (!isObject(obj1) || !isObject(obj2)) {\n        return obj1 === obj2;\n    }\n    const obj1Keys = Reflect.ownKeys(obj1);\n    return (\n        obj1Keys.length === Reflect.ownKeys(obj2).length &&\n        obj1Keys.every((key) => comparisonFn(obj1[key], obj2[key]))\n    );\n}\n\n/**\n * Deeply compares two objects.\n *\n * @template {unknown} T\n * @param {T} obj1\n * @param {T} obj2\n */\nexport const deepEqual = (obj1, obj2) => shallowEqual(obj1, obj2, deepEqual);\n\n/**\n * Deep copies an object. As it relies on JSON this function as some limitations\n * - no support for circular objects\n * - no support for specific classes, that will at best be lost and at worst crash (Map, Set etc...)\n * @template T\n * @param {T} object An object that is fully JSON stringifiable\n * @return {T}\n */\nexport function deepCopy(object) {\n    return object && JSON.parse(JSON.stringify(object));\n}\n\n/**\n * @param {unknown} object\n */\nexport function isObject(object) {\n    return !!object && (typeof object === \"object\" || typeof object === \"function\");\n}\n\n/**\n * Returns a shallow copy of object with every property in properties removed\n * if present in object.\n *\n * @template T\n * @template {keyof T} K\n * @param {T} object\n * @param {K[]} properties\n */\nexport function omit(object, ...properties) {\n    /** @type {Omit<T, K>} */\n    const result = {};\n    const propertiesSet = new Set(properties);\n    for (const key in object) {\n        if (!propertiesSet.has(key)) {\n            result[key] = object[key];\n        }\n    }\n    return result;\n}\n\n/**\n * @template T\n * @template {keyof T} K\n * @param {T} object\n * @param {K[]} properties\n * @returns {Pick<T, K>}\n */\nexport function pick(object, ...properties) {\n    return Object.fromEntries(\n        properties.filter((prop) => prop in object).map((prop) => [prop, object[prop]])\n    );\n}\n\n/**\n * Deeply merges two objects, recursively combining properties.\n * Works like the spread operator but will merge nested objects.\n *\n * This function doesn't merge arrays.\n *\n * @param {Object} target - The target object to merge into.\n * @param {Object} extension - The extension to apply.\n * @returns {Object} - The merged object.\n *\n * @example\n * const target = { a: 1, b: { c: 2 } };\n * const source = { a: 2, b: { d: 3 } };\n * const output = deepMerge(target, source);\n * // output => { a: 2, b: { c: 2, d: 3 } }\n */\nexport function deepMerge(target, extension) {\n    if (!isObject(target) && !isObject(extension)) {\n        return;\n    }\n\n    target = target || {};\n    const output = Object.assign({}, target);\n    if (isObject(extension)) {\n        for (const key of Reflect.ownKeys(extension)) {\n            if (\n                key in target &&\n                isObject(extension[key]) &&\n                !Array.isArray(extension[key]) &&\n                typeof extension[key] !== \"function\"\n            ) {\n                output[key] = deepMerge(target[key], extension[key]);\n            } else {\n                Object.assign(output, { [key]: extension[key] });\n            }\n        }\n    }\n\n    return output;\n}\n", "/**\n *  @typedef {{\n *      originalProperties: Map<string, PropertyDescriptor>;\n *      skeleton: object;\n *      extensions: Set<object>;\n *  }} PatchDescription\n */\n\n/** @type {WeakMap<object, PatchDescription>} */\nconst patchDescriptions = new WeakMap();\n\n/**\n * Create or get the patch description for the given `objToPatch`.\n * @param {object} objToPatch\n * @returns {PatchDescription}\n */\nfunction getPatchDescription(objToPatch) {\n    if (!patchDescriptions.has(objToPatch)) {\n        patchDescriptions.set(objToPatch, {\n            originalProperties: new Map(),\n            skeleton: Object.create(Object.getPrototypeOf(objToPatch)),\n            extensions: new Set(),\n        });\n    }\n    return patchDescriptions.get(objToPatch);\n}\n\n/**\n * @param {object} objToPatch\n * @returns {boolean}\n */\nfunction isClassPrototype(objToPatch) {\n    // class A {}\n    // isClassPrototype(A) === false\n    // isClassPrototype(A.prototype) === true\n    // isClassPrototype(new A()) === false\n    // isClassPrototype({}) === false\n    return (\n        Object.hasOwn(objToPatch, \"constructor\") && objToPatch.constructor?.prototype === objToPatch\n    );\n}\n\n/**\n * Traverse the prototype chain to find a potential property.\n * @param {object} objToPatch\n * @param {string} key\n * @returns {object}\n */\nfunction findAncestorPropertyDescriptor(objToPatch, key) {\n    let descriptor = null;\n    let prototype = objToPatch;\n    do {\n        descriptor = Object.getOwnPropertyDescriptor(prototype, key);\n        prototype = Object.getPrototypeOf(prototype);\n    } while (!descriptor && prototype);\n    return descriptor;\n}\n\n/**\n * Patch an object\n *\n * If the intent is to patch a class, don't forget to patch the prototype, unless\n * you want to patch static properties/methods.\n *\n * @template T\n * @template {Partial<T>} U\n * @param {T} objToPatch The object to patch\n * @param {U} extension The object containing the patched properties\n * @returns {() => void} Returns an unpatch function\n */\nexport function patch(objToPatch, extension) {\n    if (typeof extension === \"string\") {\n        throw new Error(\n            `Patch \"${extension}\": Second argument is not the patch name anymore, it should be the object containing the patched properties`\n        );\n    }\n\n    const description = getPatchDescription(objToPatch);\n    description.extensions.add(extension);\n\n    const properties = Object.getOwnPropertyDescriptors(extension);\n    for (const [key, newProperty] of Object.entries(properties)) {\n        const oldProperty = Object.getOwnPropertyDescriptor(objToPatch, key);\n        if (oldProperty) {\n            // Store the old property on the skeleton.\n            Object.defineProperty(description.skeleton, key, oldProperty);\n        }\n\n        if (!description.originalProperties.has(key)) {\n            // Keep a trace of original property (prop before first patch), useful for unpatching.\n            description.originalProperties.set(key, oldProperty);\n        }\n\n        if (isClassPrototype(objToPatch)) {\n            // A property is enumerable on POJO ({ prop: 1 }) but not on classes (class A {}).\n            // Here, we only check if we patch a class prototype.\n            newProperty.enumerable = false;\n        }\n\n        if ((newProperty.get && 1) ^ (newProperty.set && 1)) {\n            // get and set are defined together. If they are both defined\n            // in the previous descriptor but only one in the new descriptor\n            // then the other will be undefined so we need to apply the\n            // previous descriptor in the new one.\n            const ancestorProperty = findAncestorPropertyDescriptor(objToPatch, key);\n            newProperty.get = newProperty.get ?? ancestorProperty?.get;\n            newProperty.set = newProperty.set ?? ancestorProperty?.set;\n        }\n\n        // Replace the old property by the new one.\n        Object.defineProperty(objToPatch, key, newProperty);\n    }\n\n    // Sets the current skeleton as the extension's prototype to make\n    // `super` keyword working and then set extension as the new skeleton.\n    description.skeleton = Object.setPrototypeOf(extension, description.skeleton);\n\n    return () => {\n        // Remove the description to start with a fresh base.\n        patchDescriptions.delete(objToPatch);\n\n        for (const [key, property] of description.originalProperties) {\n            if (property) {\n                // Restore the original property on the `objToPatch` object.\n                Object.defineProperty(objToPatch, key, property);\n            } else {\n                // Or remove the property if it did not exist at first.\n                delete objToPatch[key];\n            }\n        }\n\n        // Re-apply the patches without the current one.\n        description.extensions.delete(extension);\n        for (const extension of description.extensions) {\n            patch(objToPatch, extension);\n        }\n    };\n}\n", "import { reactive } from \"@odoo/owl\";\n\n/**\n * This class should be used as a base when creating a class that is intended to\n * be used within the reactivity system, it avoids a specific class of bug where\n * callbacks that capture `this` declared in the constructor would escape the\n * reactivity system and prevent the observers from being notified:\n *\n * const bus = new EventBus();\n * class MyClass {\n *   constructor() {\n *     this.counter = 0;\n *     bus.addEventListener(\"change\", () => this.counter++);\n *     //                                   ^ Will never be reactive, this mutation will be missed\n *   }\n * }\n * const myObj = reactive(new MyClass(bus), () => console.log(myObj.counter));\n * myObj.counter++; // logs 0;\n * bus.trigger(\"change\"); // logs nothing!\n * myObj.counter++; // logs 2. counter == 1 was missed.\n */\nexport class Reactive {\n    constructor() {\n        return reactive(this);\n    }\n}\n\n/**\n * Creates a side-effect that runs based on the content of reactive objects.\n *\n * @template {object[]} T\n * @param {(...args: [...T]) => X} cb callback for the effect\n * @param {[...T]} deps the reactive objects that the effect depends on\n */\nexport function effect(cb, deps) {\n    const reactiveDeps = reactive(deps, () => {\n        cb(...reactiveDeps);\n    });\n    cb(...reactiveDeps);\n}\n\n/**\n * Adds computed properties to a reactive object derived from multiples sources.\n *\n * @template {object} T\n * @template {object[]} U\n * @template {{[key: string]: (this: T, ...rest: [...U]) => unknown}} V\n * @param {T} obj the reactive object on which to add the computed\n * properties\n * @param {[...U]} sources the reactive objects which are needed to compute\n * the properties\n * @param {V} descriptor the object containing methods to compute the\n * properties\n * @returns {T & {[key in keyof V]: ReturnType<V[key]>}}\n */\nexport function withComputedProperties(obj, sources, descriptor) {\n    for (const [key, compute] of Object.entries(descriptor)) {\n        effect(\n            (obj, sources) => {\n                obj[key] = compute.call(obj, ...sources);\n            },\n            [obj, sources]\n        );\n    }\n    return obj;\n}\n", "import { App, blockDom, Component, markup } from \"@odoo/owl\";\nimport { getTemplate } from \"@web/core/templates\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport function renderToElement(template, context = {}) {\n    const el = render(template, context).firstElementChild;\n    if (el?.nextElementSibling) {\n        throw new Error(\n            `The rendered template '${template}' contains multiple root ` +\n                `nodes that will be ignored using renderToElement, you should ` +\n                `consider using renderToFragment or refactoring the template.`\n        );\n    }\n    el?.remove();\n    return el;\n}\n\nexport function renderToFragment(template, context = {}) {\n    const frag = document.createDocumentFragment();\n    for (const el of [...render(template, context).children]) {\n        frag.appendChild(el);\n    }\n    return frag;\n}\n\n/**\n * renders a template with an (optional) context and outputs it as a string\n *\n * @param {string} template\n * @param {Object} context\n * @returns string: the html of the template\n */\nexport function renderToString(template, context = {}) {\n    return render(template, context).innerHTML;\n}\nlet app;\nObject.defineProperty(renderToString, \"app\", {\n    get: () => {\n        if (!app) {\n            app = new App(Component, {\n                name: \"renderToString\",\n                getTemplate,\n                translatableAttributes: [\"data-tooltip\"],\n                translateFn: _t,\n            });\n        }\n        return app;\n    },\n});\n\nfunction render(template, context = {}) {\n    const app = renderToString.app;\n    const templateFn = app.getTemplate(template);\n    const bdom = templateFn(context, {});\n    const div = document.createElement(\"div\");\n    blockDom.mount(bdom, div);\n    return div;\n}\n\n/**\n * renders a template with an (optional) context and returns a Markup string,\n * suitable to be inserted in a template with a t-out directive\n *\n * @param {string} template\n * @param {Object} context\n * @returns string: the html of the template, as a markup string\n */\nexport function renderToMarkup(template, context = {}) {\n    return markup(renderToString(template, context));\n}\n", "export function isScrollableX(el) {\n    if (el.scrollWidth > el.clientWidth && el.clientWidth > 0) {\n        return couldBeScrollableX(el);\n    }\n    return false;\n}\n\nexport function couldBeScrollableX(el) {\n    if (el) {\n        const overflow = getComputedStyle(el).getPropertyValue(\"overflow-x\");\n        if (/\\bauto\\b|\\bscroll\\b/.test(overflow)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/**\n * Get the closest horizontally scrollable for a given element.\n *\n * @param {HTMLElement} el\n * @returns {HTMLElement | null}\n */\nexport function closestScrollableX(el) {\n    if (!el) {\n        return null;\n    }\n    if (isScrollableX(el)) {\n        return el;\n    }\n    return closestScrollableX(el.parentElement);\n}\n\nexport function isScrollableY(el) {\n    if (el && el.scrollHeight > el.clientHeight && el.clientHeight > 0) {\n        return couldBeScrollableY(el);\n    }\n    return false;\n}\n\nexport function couldBeScrollableY(el) {\n    if (el) {\n        const overflow = getComputedStyle(el).getPropertyValue(\"overflow-y\");\n        if (/\\bauto\\b|\\bscroll\\b/.test(overflow)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/**\n * Get the closest vertically scrollable for a given element.\n *\n * @param {HTMLElement} el\n * @returns {HTMLElement | null}\n */\nexport function closestScrollableY(el) {\n    if (!el) {\n        return null;\n    }\n    if (isScrollableY(el)) {\n        return el;\n    }\n    return closestScrollableY(el.parentElement);\n}\n\n/**\n * Ensures that `element` will be visible in its `scrollable`.\n *\n * @param {HTMLElement} element\n * @param {object} options\n * @param {HTMLElement} [options.scrollable] a scrollable area\n * @param {boolean} [options.isAnchor] states if the scroll is to an anchor\n * @param {string} [options.behavior] \"smooth\", \"instant\", \"auto\" <=> undefined\n *        @url https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo#behavior\n * @param {number} [options.offset] applies a vertical offset\n */\nexport function scrollTo(element, options = {}) {\n    const { behavior = \"auto\", isAnchor = false, offset = 0 } = options;\n    const scrollable = closestScrollableY(options.scrollable || element.parentElement);\n    if (!scrollable) {\n        return;\n    }\n\n    const scrollBottom = scrollable.getBoundingClientRect().bottom;\n    const scrollTop = scrollable.getBoundingClientRect().top;\n    const elementBottom = element.getBoundingClientRect().bottom;\n    const elementTop = element.getBoundingClientRect().top;\n\n    const scrollPromises = [];\n\n    if (elementBottom > scrollBottom && !isAnchor) {\n        // The scroll place the element at the bottom border of the scrollable\n        scrollPromises.push(\n            new Promise((resolve) => {\n                scrollable.addEventListener(\"scrollend\", () => resolve(), { once: true });\n            })\n        );\n\n        scrollable.scrollTo({\n            top:\n                scrollable.scrollTop +\n                elementTop -\n                scrollBottom +\n                Math.ceil(element.getBoundingClientRect().height) +\n                offset,\n            behavior,\n        });\n    } else if (elementTop < scrollTop || isAnchor) {\n        // The scroll place the element at the top of the scrollable\n        scrollPromises.push(\n            new Promise((resolve) => {\n                scrollable.addEventListener(\"scrollend\", () => resolve(), { once: true });\n            })\n        );\n\n        scrollable.scrollTo({\n            top: scrollable.scrollTop - scrollTop + elementTop + offset,\n            behavior,\n        });\n\n        if (options.isAnchor) {\n            // If the scrollable is within a scrollable, another scroll should be done\n            const parentScrollable = closestScrollableY(scrollable.parentElement);\n            if (parentScrollable) {\n                scrollPromises.push(\n                    scrollTo(scrollable, {\n                        behavior,\n                        isAnchor: true,\n                        scrollable: parentScrollable,\n                    })\n                );\n            }\n        }\n    }\n\n    return Promise.all(scrollPromises);\n}\n\nexport function compensateScrollbar(\n    el,\n    add = true,\n    isScrollElement = true,\n    cssProperty = \"padding-right\"\n) {\n    if (!el) {\n        return;\n    }\n    // Compensate scrollbar\n    const scrollableEl = isScrollElement ? el : closestScrollableY(el.parentElement);\n    if (!scrollableEl) {\n        return;\n    }\n    const isRTL = scrollableEl.classList.contains(\".o_rtl\");\n    if (isRTL) {\n        cssProperty = cssProperty.replace(\"right\", \"left\");\n    }\n    el.style.removeProperty(cssProperty);\n    if (!add) {\n        return;\n    }\n    const style = window.getComputedStyle(el);\n    // Round up to the nearest integer to be as close as possible to\n    // the correct value in case of browser zoom.\n    const borderLeftWidth = Math.ceil(parseFloat(style.borderLeftWidth.replace(\"px\", \"\")));\n    const borderRightWidth = Math.ceil(parseFloat(style.borderRightWidth.replace(\"px\", \"\")));\n    const bordersWidth = borderLeftWidth + borderRightWidth;\n    const newValue =\n        parseInt(style[cssProperty]) +\n        scrollableEl.offsetWidth -\n        scrollableEl.clientWidth -\n        bordersWidth;\n    el.style.setProperty(cssProperty, `${newValue}px`, \"important\");\n}\n\nexport function getScrollingElement(document = window.document) {\n    const baseScrollingElement = document.scrollingElement;\n    if (isScrollableY(baseScrollingElement)) {\n        return baseScrollingElement;\n    }\n    const bodyHeight = window.getComputedStyle(document.body).height;\n    for (const el of document.body.children) {\n        // Search for a body child which is at least as tall as the body\n        // and which has the ability to scroll if enough content in it. If\n        // found, suppose this is the top scrolling element.\n        if (bodyHeight - el.scrollHeight > 1.5) {\n            continue;\n        }\n        if (isScrollableY(el)) {\n            return el;\n        }\n    }\n    return baseScrollingElement;\n}\n", "import { unaccent } from \"./strings\";\n\n/**\n * @param {string} pattern\n * @param {string|string[]} strs\n * @returns {number}\n */\nfunction match(pattern, strs) {\n    if (!Array.isArray(strs)) {\n        strs = [strs];\n    }\n    let globalScore = 0;\n    for (const str of strs) {\n        globalScore = Math.max(globalScore, _match(pattern, str));\n    }\n    return globalScore;\n}\n\n/**\n * This private function computes a score that represent the fact that the\n * string contains the pattern, or not\n *\n * - If the score is 0, the string does not contain the letters of the pattern in\n *   the correct order.\n * - if the score is > 0, it actually contains the letters.\n *\n * Better matches will get a higher score: consecutive letters are better,\n * and a match closer to the beginning of the string is also scored higher.\n *\n * @param {string} pattern\n * @param {string} str\n * @returns {number}\n */\nfunction _match(pattern, str) {\n    let totalScore = 0;\n    let currentScore = 0;\n    const len = str.length;\n    let patternIndex = 0;\n\n    pattern = unaccent(pattern, false);\n    str = unaccent(str, false);\n\n    for (let i = 0; i < len; i++) {\n        if (str[i] === pattern[patternIndex]) {\n            patternIndex++;\n            currentScore += 100 + currentScore - i / 200;\n        } else {\n            currentScore = 0;\n        }\n        totalScore = totalScore + currentScore;\n    }\n\n    return patternIndex === pattern.length ? totalScore : 0;\n}\n\n/**\n * Return a list of things that matches a pattern, ordered by their 'score' (\n * higher score first). An higher score means that the match is better. For\n * example, consecutive letters are considered a better match.\n *\n * @template T\n * @param {string} pattern\n * @param {T[]} list\n * @param {(element: T) => (string|string[])} fn\n * @returns {T[]}\n */\nexport function fuzzyLookup(pattern, list, fn) {\n    const results = [];\n    list.forEach((data) => {\n        const score = match(pattern, fn(data));\n        if (score > 0) {\n            results.push({ score, elem: data });\n        }\n    });\n\n    // we want better matches first\n    results.sort((a, b) => b.score - a.score);\n\n    return results.map((r) => r.elem);\n}\n\n// Does `pattern` fuzzy match `string`?\n/**\n * @param {string} pattern\n * @param {string} string\n * @returns {boolean}\n */\nexport function fuzzyTest(pattern, string) {\n    return _match(pattern, string) !== 0;\n}\n", "import {\n    DRAGGED_CLASS,\n    makeDraggableHook as nativeMakeDraggableHook,\n} from \"@web/core/utils/draggable_hook_builder\";\nimport { pick } from \"@web/core/utils/objects\";\n\n/** @typedef {import(\"@web/core/utils/draggable_hook_builder\").DraggableHandlerParams} DraggableHandlerParams */\n/** @typedef {DraggableHandlerParams & { group: HTMLElement | null }} SortableHandlerParams */\n\n/**\n * @typedef SortableParams\n *\n * MANDATORY\n *\n * @property {{ el: HTMLElement | null }} ref\n * @property {string} elements defines sortable elements\n *\n * OPTIONAL\n *\n * @property {boolean | (() => boolean)} [enable] whether the sortable system should\n *  be enabled.\n * @property {number} [delay] delay before starting a sequence after a \"pointerdown\".\n * @property {number} [touchDelay] same as \"delay\", but specific to touch environments.\n * @property {string | (() => string)} [groups] defines parent groups of sortable\n *  elements. This allows to add `onGroupEnter` and `onGroupLeave` callbacks to\n *  work on group elements during the dragging sequence.\n * @property {string | (() => string)} [handle] additional selector for when the\n *  dragging sequence must be initiated when dragging on a certain part of the element.\n * @property {string | (() => string)} [ignore] selector targetting elements that\n *  must initiate a drag.\n * @property {boolean | (() => boolean)} [connectGroups] whether elements can be\n *  dragged accross different parent groups. Note that it requires a `groups` param to work.\n * @property {string | (() => string)} [cursor] cursor style during the dragging\n *  sequence.\n * @property {boolean} [clone] the placeholder is a clone of the drag element.\n * @property {string[]} [placeholderClasses] array of classes added to the placeholder\n *  element.\n * @property {boolean} [applyChangeOnDrop] on drop the change is applied to the DOM.\n * @property {string[]} [followingElementClasses] array of classes added to the\n *  element that follow the pointer.\n *\n * HANDLERS (also optional)\n *\n * @property {(params: SortableHandlerParams) => any} [onDragStart]\n *  called when a dragging sequence is initiated.\n * @property {(params: DraggableHandlerParams) => any} [onElementEnter] called when\n *  the cursor enters another sortable element.\n * @property {(params: DraggableHandlerParams) => any} [onElementLeave] called when\n *  the cursor leaves another sortable element.\n * @property {(params: SortableHandlerParams) => any} [onGroupEnter] (if a `groups`\n *  is specified): will be called when the cursor enters another group element.\n * @property {(params: SortableHandlerParams) => any} [onGroupLeave] (if a `groups`\n *  is specified): will be called when the cursor leaves another group element.\n * @property {(params: SortableHandlerParams) => any} [onDragEnd]\n *  called when the dragging sequence ends, regardless of the reason.\n * @property {(params: DropParams) => any} [onDrop] called when the dragging sequence\n *  ends on a pointerup action AND the dragged element has been moved elsewhere.\n *  The callback will be given an object with any useful element regarding the new\n *  position of the dragged element (@see DropParams ).\n */\n\n/**\n * @typedef DropParams\n * @property {HTMLElement} element\n * @property {HTMLElement | null} group\n * @property {HTMLElement | null} previous\n * @property {HTMLElement | null} next\n * @property {HTMLElement | null} parent\n */\n\n/**\n * @typedef SortableState\n * @property {boolean} dragging\n */\n\n/** @type SortableParams */\nconst hookParams = {\n    name: \"useSortable\",\n    acceptedParams: {\n        groups: [String, Function],\n        connectGroups: [Boolean, Function],\n        clone: [Boolean],\n        placeholderClasses: [Object],\n        applyChangeOnDrop: [Boolean],\n        followingElementClasses: [Object],\n    },\n    defaultParams: {\n        connectGroups: false,\n        edgeScrolling: { speed: 20, threshold: 60 },\n        groupSelector: null,\n        clone: true,\n        placeholderClasses: [],\n        applyChangeOnDrop: false,\n        followingElementClasses: [],\n    },\n\n    // Build steps\n    onComputeParams({ ctx, params }) {\n        // Group selector\n        ctx.groupSelector = params.groups || null;\n        if (ctx.groupSelector) {\n            ctx.fullSelector = [ctx.groupSelector, ctx.fullSelector].join(\" \");\n        }\n\n        // Connection accross groups\n        ctx.connectGroups = params.connectGroups;\n\n        ctx.placeholderClone = params.clone;\n        ctx.placeholderClasses = params.placeholderClasses;\n        ctx.applyChangeOnDrop = params.applyChangeOnDrop;\n        ctx.followingElementClasses = params.followingElementClasses;\n    },\n\n    // Runtime steps\n    onDragStart({ ctx, addListener, addStyle, callHandler }) {\n        /**\n         * Element \"pointerenter\" event handler.\n         * @param {PointerEvent} ev\n         */\n        const onElementPointerEnter = (ev) => {\n            const element = ev.currentTarget;\n            if (\n                connectGroups ||\n                !groupSelector ||\n                current.group === element.closest(groupSelector)\n            ) {\n                const pos = current.placeHolder.compareDocumentPosition(element);\n                if (pos === Node.DOCUMENT_POSITION_PRECEDING) {\n                    element.before(current.placeHolder);\n                } else if (pos === Node.DOCUMENT_POSITION_FOLLOWING) {\n                    element.after(current.placeHolder);\n                }\n            }\n            callHandler(\"onElementEnter\", { element });\n        };\n\n        /**\n         * Element \"pointerleave\" event handler.\n         * @param {PointerEvent} ev\n         */\n        const onElementPointerLeave = (ev) => {\n            const element = ev.currentTarget;\n            callHandler(\"onElementLeave\", { element });\n        };\n\n        const onElementComplexPointerEnter = (ev) => {\n            if (ctx.haveAlreadyChanged) {\n                return;\n            }\n            const element = ev.currentTarget;\n\n            const siblingArray = [...element.parentElement.children].filter(\n                (el) =>\n                    el === current.placeHolder ||\n                    (el.matches(elementSelector) && !el.classList.contains(DRAGGED_CLASS))\n            );\n            const elementIndex = siblingArray.indexOf(element);\n            const placeholderIndex = siblingArray.indexOf(current.placeHolder);\n            const isDirectSibling = Math.abs(elementIndex - placeholderIndex) === 1;\n            if (\n                connectGroups ||\n                !groupSelector ||\n                current.group === element.closest(groupSelector)\n            ) {\n                const pos = current.placeHolder.compareDocumentPosition(element);\n                if (isDirectSibling) {\n                    if (pos === Node.DOCUMENT_POSITION_PRECEDING) {\n                        element.before(current.placeHolder);\n                        ctx.haveAlreadyChanged = true;\n                    } else if (pos === Node.DOCUMENT_POSITION_FOLLOWING) {\n                        element.after(current.placeHolder);\n                        ctx.haveAlreadyChanged = true;\n                    }\n                } else {\n                    if (pos === Node.DOCUMENT_POSITION_FOLLOWING) {\n                        element.before(current.placeHolder);\n                        ctx.haveAlreadyChanged = true;\n                    } else if (pos === Node.DOCUMENT_POSITION_PRECEDING) {\n                        element.after(current.placeHolder);\n                        ctx.haveAlreadyChanged = true;\n                    }\n                }\n            }\n            callHandler(\"onElementEnter\", { element });\n        };\n\n        /**\n         * Element \"pointerleave\" event handler.\n         * @param {PointerEvent} ev\n         */\n        const onElementComplexPointerLeave = (ev) => {\n            if (ctx.haveAlreadyChanged) {\n                return;\n            }\n            const element = ev.currentTarget;\n            const elementRect = element.getBoundingClientRect();\n\n            const relatedElement = ev.relatedTarget;\n            const relatedElementRect = element.getBoundingClientRect();\n\n            const siblingArray = [...element.parentElement.children].filter(\n                (el) =>\n                    el === current.placeHolder ||\n                    (el.matches(elementSelector) && !el.classList.contains(DRAGGED_CLASS))\n            );\n            const pointerOnSiblings = siblingArray.indexOf(relatedElement) > -1;\n            const elementIndex = siblingArray.indexOf(element);\n            const isFirst = elementIndex === 0;\n            const isAbove = relatedElementRect.top <= elementRect.top;\n            const isLast = elementIndex === siblingArray.length - 1;\n            const isBelow = relatedElementRect.bottom >= elementRect.bottom;\n            const pos = current.placeHolder.compareDocumentPosition(element);\n            if (!pointerOnSiblings) {\n                if (isFirst && isAbove && pos === Node.DOCUMENT_POSITION_PRECEDING) {\n                    element.before(current.placeHolder);\n                    ctx.haveAlreadyChanged = true;\n                } else if (isLast && isBelow && pos === Node.DOCUMENT_POSITION_FOLLOWING) {\n                    element.after(current.placeHolder);\n                    ctx.haveAlreadyChanged = true;\n                }\n            }\n            callHandler(\"onElementLeave\", { element });\n        };\n\n        /**\n         * Group \"pointerenter\" event handler.\n         * @param {PointerEvent} ev\n         */\n        const onGroupPointerEnter = (ev) => {\n            const group = ev.currentTarget;\n            group.appendChild(current.placeHolder);\n            callHandler(\"onGroupEnter\", { group });\n        };\n\n        /**\n         * Group \"pointerleave\" event handler.\n         * @param {PointerEvent} ev\n         */\n        const onGroupPointerLeave = (ev) => {\n            const group = ev.currentTarget;\n            callHandler(\"onGroupLeave\", { group });\n        };\n\n        const { connectGroups, current, elementSelector, groupSelector, ref } = ctx;\n        if (ctx.placeholderClone) {\n            const { width, height } = current.elementRect;\n\n            // Adjusts size for the placeholder element\n            addStyle(current.placeHolder, {\n                visibility: \"hidden\",\n                display: \"block\",\n                width: `${width}px`,\n                height: `${height}px`,\n            });\n        }\n\n        // Binds handlers on eligible groups, if the elements are not confined to\n        // their parents and a 'groupSelector' has been provided.\n        if (connectGroups && groupSelector) {\n            for (const siblingGroup of ref.el.querySelectorAll(groupSelector)) {\n                addListener(siblingGroup, \"pointerenter\", onGroupPointerEnter);\n                addListener(siblingGroup, \"pointerleave\", onGroupPointerLeave);\n            }\n        }\n\n        // Binds handlers on eligible elements\n        for (const siblingEl of ref.el.querySelectorAll(elementSelector)) {\n            if (siblingEl !== current.element && siblingEl !== current.placeHolder) {\n                if (ctx.placeholderClone) {\n                    addListener(siblingEl, \"pointerenter\", onElementPointerEnter);\n                    addListener(siblingEl, \"pointerleave\", onElementPointerLeave);\n                } else {\n                    addListener(siblingEl, \"pointerenter\", onElementComplexPointerEnter);\n                    addListener(siblingEl, \"pointerleave\", onElementComplexPointerLeave);\n                }\n            }\n        }\n\n        // Placeholder is initially added right after the current element.\n        current.element.after(current.placeHolder);\n\n        return pick(current, \"element\", \"group\");\n    },\n    onDrag({ ctx }) {\n        ctx.haveAlreadyChanged = false;\n    },\n    onDragEnd({ ctx }) {\n        return pick(ctx.current, \"element\", \"group\");\n    },\n    onDrop({ ctx }) {\n        const { current, groupSelector } = ctx;\n        const previous = current.placeHolder.previousElementSibling;\n        const next = current.placeHolder.nextElementSibling;\n        if (previous !== current.element && next !== current.element) {\n            const element = current.element;\n            if (ctx.applyChangeOnDrop) {\n                // Apply to the DOM the result of sortable()\n                if (previous) {\n                    previous.after(element);\n                } else if (next) {\n                    next.before(element);\n                }\n            }\n            return {\n                element,\n                group: current.group,\n                previous,\n                next,\n                parent: groupSelector && current.placeHolder.closest(groupSelector),\n            };\n        }\n    },\n    onWillStartDrag({ ctx, addCleanup }) {\n        const { connectGroups, current, groupSelector } = ctx;\n\n        if (groupSelector) {\n            current.group = current.element.closest(groupSelector);\n            if (!connectGroups) {\n                current.container = current.group;\n            }\n        }\n\n        if (ctx.placeholderClone) {\n            current.placeHolder = current.element.cloneNode(false);\n        } else {\n            current.placeHolder = document.createElement(\"div\");\n        }\n        current.placeHolder.classList.add(...ctx.placeholderClasses);\n        current.element.classList.add(...ctx.followingElementClasses);\n\n        addCleanup(() => current.element.classList.remove(...ctx.followingElementClasses));\n        addCleanup(() => current.placeHolder.remove());\n\n        return pick(current, \"element\", \"group\");\n    },\n};\n\n/** @type {(params: SortableParams) => SortableState} */\nexport const useSortable = (sortableParams) => {\n    const { setupHooks } = sortableParams;\n    delete sortableParams.setupHooks;\n    return nativeMakeDraggableHook({ ...hookParams, setupHooks })(sortableParams);\n};\n", "import { onWillUnmount, reactive, useEffect, useExternalListener } from \"@odoo/owl\";\nimport { useThrottleForAnimation } from \"./timing\";\nimport { useSortable as nativeUseSortable } from \"@web/core/utils/sortable\";\n\n/**\n * Set of default `useSortable` setup hooks that makes use of Owl lifecycle\n * and reactivity hooks to properly set up, update and tear down the elements and\n * listeners added by the draggable hook builder.\n *\n * @see {nativeUseSortable}\n * @type {typeof nativeUseSortable}\n */\nexport function useSortable(params) {\n    return nativeUseSortable({\n        ...params,\n        setupHooks: {\n            addListener: useExternalListener,\n            setup: useEffect,\n            teardown: onWillUnmount,\n            throttle: useThrottleForAnimation,\n            wrapState: reactive,\n        },\n    });\n}\n", "import { registry } from \"../registry\";\nimport { useSortable } from \"@web/core/utils/sortable\";\nimport { throttleForAnimation } from \"@web/core/utils/timing\";\nimport { reactive } from \"@odoo/owl\";\n\n/**\n * @typedef SortableServiceHookParams\n * @extends SortableParams\n * @property {{el: HTMLElement} | ReturnType<typeof import(\"@odoo/owl\").useRef>} [ref] container of sortable\n * @property {string | Symbol} [sortableId] identifier when multiple sortable on the same container\n */\n\nconst DEFAULT_SORTABLE_ID = Symbol.for(\"defaultSortable\");\nexport const sortableService = {\n    start() {\n        /**\n         * Map to avoid to setup/enable twice or more time the same element\n         * @type {Map<Element, Object>}\n         */\n        const boundElements = new Map();\n        return {\n            /**\n             * @param {SortableServiceHookParams} hookParams\n             */\n            create: (hookParams) => {\n                const element = hookParams.ref.el;\n                const sortableId = hookParams.sortableId ?? DEFAULT_SORTABLE_ID;\n                if (boundElements.has(element)) {\n                    const boundElement = boundElements.get(element);\n                    if (sortableId in boundElement) {\n                        return {\n                            enable() {\n                                return {\n                                    cleanup: boundElement[sortableId],\n                                };\n                            },\n                        };\n                    }\n                }\n                /**\n                 * @type {Map<Function, function():Array>}\n                 */\n                const setupFunctions = new Map();\n                /**\n                 * @type {Array<Function>}\n                 */\n                const cleanupFunctions = [];\n\n                const cleanup = () => {\n                    const boundElement = boundElements.get(element);\n                    if (sortableId in boundElement) {\n                        delete boundElement[sortableId];\n                        if (boundElement.length === 0) {\n                            boundElements.delete(element);\n                        }\n                    }\n                    cleanupFunctions.forEach((fn) => fn());\n                };\n\n                // Setup hookParam\n                const setupHooks = {\n                    wrapState: reactive,\n                    throttle: throttleForAnimation,\n                    addListener: (el, type, listener) => {\n                        el.addEventListener(type, listener);\n                        cleanupFunctions.push(() => el.removeEventListener(type, listener));\n                    },\n                    setup: (setupFn, dependenciesFn) => setupFunctions.set(setupFn, dependenciesFn),\n                    teardown: (fn) => cleanupFunctions.push(fn),\n                };\n\n                useSortable({ setupHooks, ...hookParams });\n\n                const boundElement = boundElements.get(element);\n                if (boundElement) {\n                    boundElement[sortableId] = cleanup;\n                } else {\n                    boundElements.set(element, { [sortableId]: cleanup });\n                }\n\n                return {\n                    enable() {\n                        setupFunctions.forEach((dependenciesFn, setupFn) =>\n                            setupFn(...dependenciesFn())\n                        );\n                        return {\n                            cleanup,\n                        };\n                    },\n                };\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"sortable\", sortableService);\n", "export const nbsp = \"\\u00a0\";\n\n/**\n * Escapes a string for HTML.\n *\n * @param {string | number} [str] the string to escape\n * @returns {string} an escaped string\n */\nexport function escape(str) {\n    if (str === undefined) {\n        return \"\";\n    }\n    if (typeof str === \"number\") {\n        return String(str);\n    }\n    [\n        [\"&\", \"&amp;\"],\n        [\"<\", \"&lt;\"],\n        [\">\", \"&gt;\"],\n        [\"'\", \"&#x27;\"],\n        ['\"', \"&quot;\"],\n        [\"`\", \"&#x60;\"],\n    ].forEach((pairs) => {\n        str = String(str).replaceAll(pairs[0], pairs[1]);\n    });\n    return str;\n}\n\n/**\n * Escapes a string to use as a RegExp.\n * @url https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping\n *\n * @param {string} str\n * @returns {string} escaped string to use as a RegExp\n */\nexport function escapeRegExp(str) {\n    return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Intersperses ``separator`` in ``str`` at the positions indicated by\n * ``indices``.\n *\n * ``indices`` is an array of relative offsets (from the previous insertion\n * position, starting from the end of the string) at which to insert\n * ``separator``.\n *\n * There are two special values:\n *\n * ``-1``\n *   indicates the insertion should end now\n * ``0``\n *   indicates that the previous section pattern should be repeated (until all\n *   of ``str`` is consumed)\n *\n * @param {string} str\n * @param {number[]} indices\n * @param {string} separator\n * @returns {string}\n */\nexport function intersperse(str, indices, separator = \"\") {\n    separator = separator || \"\";\n    const result = [];\n    let last = str.length;\n    for (let i = 0; i < indices.length; ++i) {\n        let section = indices[i];\n        if (section === -1 || last <= 0) {\n            // Done with string, or -1 (stops formatting string)\n            break;\n        } else if (section === 0 && i === 0) {\n            // repeats previous section, which there is none => stop\n            break;\n        } else if (section === 0) {\n            // repeat previous section forever\n            //noinspection AssignmentToForLoopParameterJS\n            section = indices[--i];\n        }\n        result.push(str.substring(last - section, last));\n        last -= section;\n    }\n    const s = str.substring(0, last);\n    if (s) {\n        result.push(s);\n    }\n    return result.reverse().join(separator);\n}\n\n/**\n * Returns a string formatted using given values.\n * If the value is an object, its keys will replace `%(key)s` expressions.\n * If the values are a set of strings, they will replace `%s` expressions.\n * If no value is given, the string will not be formatted.\n *\n * @param {string} s\n * @param {any[]} values\n * @returns {string}\n */\nexport function sprintf(s, ...values) {\n    if (values.length === 1 && Object.prototype.toString.call(values[0]) === \"[object Object]\") {\n        const valuesDict = values[0];\n        s = s.replace(/%\\(([^)]+)\\)s/g, (match, value) => valuesDict[value]);\n    } else if (values.length > 0) {\n        s = s.replace(/%s/g, () => values.shift());\n    }\n    return s;\n}\n\n/**\n * Capitalizes a string: \"abc def\" => \"Abc def\"\n *\n * @param {string} s the input string\n * @returns {string}\n */\nexport function capitalize(s) {\n    return s ? s[0].toUpperCase() + s.slice(1) : \"\";\n}\n\n/* eslint-disable */\n// prettier-ignore\nconst diacriticsMap = {\n'\\u0041': 'A','\\u24B6': 'A','\\uFF21': 'A','\\u00C0': 'A','\\u00C1': 'A','\\u00C2': 'A','\\u1EA6': 'A','\\u1EA4': 'A','\\u1EAA': 'A','\\u1EA8': 'A',\n'\\u00C3': 'A','\\u0100': 'A','\\u0102': 'A','\\u1EB0': 'A','\\u1EAE': 'A','\\u1EB4': 'A','\\u1EB2': 'A','\\u0226': 'A','\\u01E0': 'A','\\u00C4': 'A',\n'\\u01DE': 'A','\\u1EA2': 'A','\\u00C5': 'A','\\u01FA': 'A','\\u01CD': 'A','\\u0200': 'A','\\u0202': 'A','\\u1EA0': 'A','\\u1EAC': 'A','\\u1EB6': 'A',\n'\\u1E00': 'A','\\u0104': 'A','\\u023A': 'A','\\u2C6F': 'A',\n\n'\\uA732': 'AA',\n'\\u00C6': 'AE','\\u01FC': 'AE','\\u01E2': 'AE',\n'\\uA734': 'AO',\n'\\uA736': 'AU',\n'\\uA738': 'AV','\\uA73A': 'AV',\n'\\uA73C': 'AY',\n'\\u0042': 'B','\\u24B7': 'B','\\uFF22': 'B','\\u1E02': 'B','\\u1E04': 'B','\\u1E06': 'B','\\u0243': 'B','\\u0182': 'B','\\u0181': 'B',\n\n'\\u0043': 'C','\\u24B8': 'C','\\uFF23': 'C','\\u0106': 'C','\\u0108': 'C','\\u010A': 'C','\\u010C': 'C','\\u00C7': 'C','\\u1E08': 'C','\\u0187': 'C',\n'\\u023B': 'C','\\uA73E': 'C',\n\n'\\u0044': 'D','\\u24B9': 'D','\\uFF24': 'D','\\u1E0A': 'D','\\u010E': 'D','\\u1E0C': 'D','\\u1E10': 'D','\\u1E12': 'D','\\u1E0E': 'D','\\u0110': 'D',\n'\\u018B': 'D','\\u018A': 'D','\\u0189': 'D','\\uA779': 'D',\n\n'\\u01F1': 'DZ','\\u01C4': 'DZ',\n'\\u01F2': 'Dz','\\u01C5': 'Dz',\n\n'\\u0045': 'E','\\u24BA': 'E','\\uFF25': 'E','\\u00C8': 'E','\\u00C9': 'E','\\u00CA': 'E','\\u1EC0': 'E','\\u1EBE': 'E','\\u1EC4': 'E','\\u1EC2': 'E',\n'\\u1EBC': 'E','\\u0112': 'E','\\u1E14': 'E','\\u1E16': 'E','\\u0114': 'E','\\u0116': 'E','\\u00CB': 'E','\\u1EBA': 'E','\\u011A': 'E','\\u0204': 'E',\n'\\u0206': 'E','\\u1EB8': 'E','\\u1EC6': 'E','\\u0228': 'E','\\u1E1C': 'E','\\u0118': 'E','\\u1E18': 'E','\\u1E1A': 'E','\\u0190': 'E','\\u018E': 'E',\n\n'\\u0046': 'F','\\u24BB': 'F','\\uFF26': 'F','\\u1E1E': 'F','\\u0191': 'F','\\uA77B': 'F',\n\n'\\u0047': 'G','\\u24BC': 'G','\\uFF27': 'G','\\u01F4': 'G','\\u011C': 'G','\\u1E20': 'G','\\u011E': 'G','\\u0120': 'G','\\u01E6': 'G','\\u0122': 'G',\n'\\u01E4': 'G','\\u0193': 'G','\\uA7A0': 'G','\\uA77D': 'G','\\uA77E': 'G',\n\n'\\u0048': 'H','\\u24BD': 'H','\\uFF28': 'H','\\u0124': 'H','\\u1E22': 'H','\\u1E26': 'H','\\u021E': 'H','\\u1E24': 'H','\\u1E28': 'H','\\u1E2A': 'H',\n'\\u0126': 'H','\\u2C67': 'H','\\u2C75': 'H','\\uA78D': 'H',\n\n'\\u0049': 'I','\\u24BE': 'I','\\uFF29': 'I','\\u00CC': 'I','\\u00CD': 'I','\\u00CE': 'I','\\u0128': 'I','\\u012A': 'I','\\u012C': 'I','\\u0130': 'I',\n'\\u00CF': 'I','\\u1E2E': 'I','\\u1EC8': 'I','\\u01CF': 'I','\\u0208': 'I','\\u020A': 'I','\\u1ECA': 'I','\\u012E': 'I','\\u1E2C': 'I','\\u0197': 'I',\n\n'\\u004A': 'J','\\u24BF': 'J','\\uFF2A': 'J','\\u0134': 'J','\\u0248': 'J',\n\n'\\u004B': 'K','\\u24C0': 'K','\\uFF2B': 'K','\\u1E30': 'K','\\u01E8': 'K','\\u1E32': 'K','\\u0136': 'K','\\u1E34': 'K','\\u0198': 'K','\\u2C69': 'K',\n'\\uA740': 'K','\\uA742': 'K','\\uA744': 'K','\\uA7A2': 'K',\n\n'\\u004C': 'L','\\u24C1': 'L','\\uFF2C': 'L','\\u013F': 'L','\\u0139': 'L','\\u013D': 'L','\\u1E36': 'L','\\u1E38': 'L','\\u013B': 'L','\\u1E3C': 'L',\n'\\u1E3A': 'L','\\u0141': 'L','\\u023D': 'L','\\u2C62': 'L','\\u2C60': 'L','\\uA748': 'L','\\uA746': 'L','\\uA780': 'L',\n\n'\\u01C7': 'LJ',\n'\\u01C8': 'Lj',\n'\\u004D': 'M','\\u24C2': 'M','\\uFF2D': 'M','\\u1E3E': 'M','\\u1E40': 'M','\\u1E42': 'M','\\u2C6E': 'M','\\u019C': 'M',\n\n'\\u004E': 'N','\\u24C3': 'N','\\uFF2E': 'N','\\u01F8': 'N','\\u0143': 'N','\\u00D1': 'N','\\u1E44': 'N','\\u0147': 'N','\\u1E46': 'N','\\u0145': 'N',\n'\\u1E4A': 'N','\\u1E48': 'N','\\u0220': 'N','\\u019D': 'N','\\uA790': 'N','\\uA7A4': 'N',\n\n'\\u01CA': 'NJ',\n'\\u01CB': 'Nj',\n\n'\\u004F': 'O','\\u24C4': 'O','\\uFF2F': 'O','\\u00D2': 'O','\\u00D3': 'O','\\u00D4': 'O','\\u1ED2': 'O','\\u1ED0': 'O','\\u1ED6': 'O','\\u1ED4': 'O',\n'\\u00D5': 'O','\\u1E4C': 'O','\\u022C': 'O','\\u1E4E': 'O','\\u014C': 'O','\\u1E50': 'O','\\u1E52': 'O','\\u014E': 'O','\\u022E': 'O','\\u0230': 'O',\n'\\u00D6': 'O','\\u022A': 'O','\\u1ECE': 'O','\\u0150': 'O','\\u01D1': 'O','\\u020C': 'O','\\u020E': 'O','\\u01A0': 'O','\\u1EDC': 'O','\\u1EDA': 'O',\n'\\u1EE0': 'O','\\u1EDE': 'O','\\u1EE2': 'O','\\u1ECC': 'O','\\u1ED8': 'O','\\u01EA': 'O','\\u01EC': 'O','\\u00D8': 'O','\\u01FE': 'O','\\u0186': 'O',\n'\\u019F': 'O','\\uA74A': 'O','\\uA74C': 'O',\n\n'\\u01A2': 'OI',\n'\\uA74E': 'OO',\n'\\u0222': 'OU',\n'\\u0050': 'P','\\u24C5': 'P','\\uFF30': 'P','\\u1E54': 'P','\\u1E56': 'P','\\u01A4': 'P','\\u2C63': 'P','\\uA750': 'P','\\uA752': 'P','\\uA754': 'P',\n'\\u0051': 'Q','\\u24C6': 'Q','\\uFF31': 'Q','\\uA756': 'Q','\\uA758': 'Q','\\u024A': 'Q',\n\n'\\u0052': 'R','\\u24C7': 'R','\\uFF32': 'R','\\u0154': 'R','\\u1E58': 'R','\\u0158': 'R','\\u0210': 'R','\\u0212': 'R','\\u1E5A': 'R','\\u1E5C': 'R',\n'\\u0156': 'R','\\u1E5E': 'R','\\u024C': 'R','\\u2C64': 'R','\\uA75A': 'R','\\uA7A6': 'R','\\uA782': 'R',\n\n'\\u0053': 'S','\\u24C8': 'S','\\uFF33': 'S','\\u1E9E': 'S','\\u015A': 'S','\\u1E64': 'S','\\u015C': 'S','\\u1E60': 'S','\\u0160': 'S','\\u1E66': 'S',\n'\\u1E62': 'S','\\u1E68': 'S','\\u0218': 'S','\\u015E': 'S','\\u2C7E': 'S','\\uA7A8': 'S','\\uA784': 'S',\n\n'\\u0054': 'T','\\u24C9': 'T','\\uFF34': 'T','\\u1E6A': 'T','\\u0164': 'T','\\u1E6C': 'T','\\u021A': 'T','\\u0162': 'T','\\u1E70': 'T','\\u1E6E': 'T',\n'\\u0166': 'T','\\u01AC': 'T','\\u01AE': 'T','\\u023E': 'T','\\uA786': 'T',\n\n'\\uA728': 'TZ',\n\n'\\u0055': 'U','\\u24CA': 'U','\\uFF35': 'U','\\u00D9': 'U','\\u00DA': 'U','\\u00DB': 'U','\\u0168': 'U','\\u1E78': 'U','\\u016A': 'U','\\u1E7A': 'U',\n'\\u016C': 'U','\\u00DC': 'U','\\u01DB': 'U','\\u01D7': 'U','\\u01D5': 'U','\\u01D9': 'U','\\u1EE6': 'U','\\u016E': 'U','\\u0170': 'U','\\u01D3': 'U',\n'\\u0214': 'U','\\u0216': 'U','\\u01AF': 'U','\\u1EEA': 'U','\\u1EE8': 'U','\\u1EEE': 'U','\\u1EEC': 'U','\\u1EF0': 'U','\\u1EE4': 'U','\\u1E72': 'U',\n'\\u0172': 'U','\\u1E76': 'U','\\u1E74': 'U','\\u0244': 'U',\n\n'\\u0056': 'V','\\u24CB': 'V','\\uFF36': 'V','\\u1E7C': 'V','\\u1E7E': 'V','\\u01B2': 'V','\\uA75E': 'V','\\u0245': 'V',\n'\\uA760': 'VY',\n'\\u0057': 'W','\\u24CC': 'W','\\uFF37': 'W','\\u1E80': 'W','\\u1E82': 'W','\\u0174': 'W','\\u1E86': 'W','\\u1E84': 'W','\\u1E88': 'W','\\u2C72': 'W',\n'\\u0058': 'X','\\u24CD': 'X','\\uFF38': 'X','\\u1E8A': 'X','\\u1E8C': 'X',\n\n'\\u0059': 'Y','\\u24CE': 'Y','\\uFF39': 'Y','\\u1EF2': 'Y','\\u00DD': 'Y','\\u0176': 'Y','\\u1EF8': 'Y','\\u0232': 'Y','\\u1E8E': 'Y','\\u0178': 'Y',\n'\\u1EF6': 'Y','\\u1EF4': 'Y','\\u01B3': 'Y','\\u024E': 'Y','\\u1EFE': 'Y',\n\n'\\u005A': 'Z','\\u24CF': 'Z','\\uFF3A': 'Z','\\u0179': 'Z','\\u1E90': 'Z','\\u017B': 'Z','\\u017D': 'Z','\\u1E92': 'Z','\\u1E94': 'Z','\\u01B5': 'Z',\n'\\u0224': 'Z','\\u2C7F': 'Z','\\u2C6B': 'Z','\\uA762': 'Z',\n\n'\\u0061': 'a','\\u24D0': 'a','\\uFF41': 'a','\\u1E9A': 'a','\\u00E0': 'a','\\u00E1': 'a','\\u00E2': 'a','\\u1EA7': 'a','\\u1EA5': 'a','\\u1EAB': 'a',\n'\\u1EA9': 'a','\\u00E3': 'a','\\u0101': 'a','\\u0103': 'a','\\u1EB1': 'a','\\u1EAF': 'a','\\u1EB5': 'a','\\u1EB3': 'a','\\u0227': 'a','\\u01E1': 'a',\n'\\u00E4': 'a','\\u01DF': 'a','\\u1EA3': 'a','\\u00E5': 'a','\\u01FB': 'a','\\u01CE': 'a','\\u0201': 'a','\\u0203': 'a','\\u1EA1': 'a','\\u1EAD': 'a',\n'\\u1EB7': 'a','\\u1E01': 'a','\\u0105': 'a','\\u2C65': 'a','\\u0250': 'a',\n\n'\\uA733': 'aa',\n'\\u00E6': 'ae','\\u01FD': 'ae','\\u01E3': 'ae',\n'\\uA735': 'ao',\n'\\uA737': 'au',\n'\\uA739': 'av','\\uA73B': 'av',\n'\\uA73D': 'ay',\n'\\u0062': 'b','\\u24D1': 'b','\\uFF42': 'b','\\u1E03': 'b','\\u1E05': 'b','\\u1E07': 'b','\\u0180': 'b','\\u0183': 'b','\\u0253': 'b',\n\n'\\u0063': 'c','\\u24D2': 'c','\\uFF43': 'c','\\u0107': 'c','\\u0109': 'c','\\u010B': 'c','\\u010D': 'c','\\u00E7': 'c','\\u1E09': 'c','\\u0188': 'c',\n'\\u023C': 'c','\\uA73F': 'c','\\u2184': 'c',\n\n'\\u0064': 'd','\\u24D3': 'd','\\uFF44': 'd','\\u1E0B': 'd','\\u010F': 'd','\\u1E0D': 'd','\\u1E11': 'd','\\u1E13': 'd','\\u1E0F': 'd','\\u0111': 'd',\n'\\u018C': 'd','\\u0256': 'd','\\u0257': 'd','\\uA77A': 'd',\n\n'\\u01F3': 'dz','\\u01C6': 'dz',\n\n'\\u0065': 'e','\\u24D4': 'e','\\uFF45': 'e','\\u00E8': 'e','\\u00E9': 'e','\\u00EA': 'e','\\u1EC1': 'e','\\u1EBF': 'e','\\u1EC5': 'e','\\u1EC3': 'e',\n'\\u1EBD': 'e','\\u0113': 'e','\\u1E15': 'e','\\u1E17': 'e','\\u0115': 'e','\\u0117': 'e','\\u00EB': 'e','\\u1EBB': 'e','\\u011B': 'e','\\u0205': 'e',\n'\\u0207': 'e','\\u1EB9': 'e','\\u1EC7': 'e','\\u0229': 'e','\\u1E1D': 'e','\\u0119': 'e','\\u1E19': 'e','\\u1E1B': 'e','\\u0247': 'e','\\u025B': 'e',\n'\\u01DD': 'e',\n\n'\\u0066': 'f','\\u24D5': 'f','\\uFF46': 'f','\\u1E1F': 'f','\\u0192': 'f','\\uA77C': 'f',\n\n'\\u0067': 'g','\\u24D6': 'g','\\uFF47': 'g','\\u01F5': 'g','\\u011D': 'g','\\u1E21': 'g','\\u011F': 'g','\\u0121': 'g','\\u01E7': 'g','\\u0123': 'g',\n'\\u01E5': 'g','\\u0260': 'g','\\uA7A1': 'g','\\u1D79': 'g','\\uA77F': 'g',\n\n'\\u0068': 'h','\\u24D7': 'h','\\uFF48': 'h','\\u0125': 'h','\\u1E23': 'h','\\u1E27': 'h','\\u021F': 'h','\\u1E25': 'h','\\u1E29': 'h','\\u1E2B': 'h',\n'\\u1E96': 'h','\\u0127': 'h','\\u2C68': 'h','\\u2C76': 'h','\\u0265': 'h',\n\n'\\u0195': 'hv',\n\n'\\u0069': 'i','\\u24D8': 'i','\\uFF49': 'i','\\u00EC': 'i','\\u00ED': 'i','\\u00EE': 'i','\\u0129': 'i','\\u012B': 'i','\\u012D': 'i','\\u00EF': 'i',\n'\\u1E2F': 'i','\\u1EC9': 'i','\\u01D0': 'i','\\u0209': 'i','\\u020B': 'i','\\u1ECB': 'i','\\u012F': 'i','\\u1E2D': 'i','\\u0268': 'i','\\u0131': 'i',\n\n'\\u006A': 'j','\\u24D9': 'j','\\uFF4A': 'j','\\u0135': 'j','\\u01F0': 'j','\\u0249': 'j',\n\n'\\u006B': 'k','\\u24DA': 'k','\\uFF4B': 'k','\\u1E31': 'k','\\u01E9': 'k','\\u1E33': 'k','\\u0137': 'k','\\u1E35': 'k','\\u0199': 'k','\\u2C6A': 'k',\n'\\uA741': 'k','\\uA743': 'k','\\uA745': 'k','\\uA7A3': 'k',\n\n'\\u006C': 'l','\\u24DB': 'l','\\uFF4C': 'l','\\u0140': 'l','\\u013A': 'l','\\u013E': 'l','\\u1E37': 'l','\\u1E39': 'l','\\u013C': 'l','\\u1E3D': 'l',\n'\\u1E3B': 'l','\\u017F': 'l','\\u0142': 'l','\\u019A': 'l','\\u026B': 'l','\\u2C61': 'l','\\uA749': 'l','\\uA781': 'l','\\uA747': 'l',\n\n'\\u01C9': 'lj',\n'\\u006D': 'm','\\u24DC': 'm','\\uFF4D': 'm','\\u1E3F': 'm','\\u1E41': 'm','\\u1E43': 'm','\\u0271': 'm','\\u026F': 'm',\n\n'\\u006E': 'n','\\u24DD': 'n','\\uFF4E': 'n','\\u01F9': 'n','\\u0144': 'n','\\u00F1': 'n','\\u1E45': 'n','\\u0148': 'n','\\u1E47': 'n','\\u0146': 'n',\n'\\u1E4B': 'n','\\u1E49': 'n','\\u019E': 'n','\\u0272': 'n','\\u0149': 'n','\\uA791': 'n','\\uA7A5': 'n',\n\n'\\u01CC': 'nj',\n\n'\\u006F': 'o','\\u24DE': 'o','\\uFF4F': 'o','\\u00F2': 'o','\\u00F3': 'o','\\u00F4': 'o','\\u1ED3': 'o','\\u1ED1': 'o','\\u1ED7': 'o','\\u1ED5': 'o',\n'\\u00F5': 'o','\\u1E4D': 'o','\\u022D': 'o','\\u1E4F': 'o','\\u014D': 'o','\\u1E51': 'o','\\u1E53': 'o','\\u014F': 'o','\\u022F': 'o','\\u0231': 'o',\n'\\u00F6': 'o','\\u022B': 'o','\\u1ECF': 'o','\\u0151': 'o','\\u01D2': 'o','\\u020D': 'o','\\u020F': 'o','\\u01A1': 'o','\\u1EDD': 'o','\\u1EDB': 'o',\n'\\u1EE1': 'o','\\u1EDF': 'o','\\u1EE3': 'o','\\u1ECD': 'o','\\u1ED9': 'o','\\u01EB': 'o','\\u01ED': 'o','\\u00F8': 'o','\\u01FF': 'o','\\u0254': 'o',\n'\\uA74B': 'o','\\uA74D': 'o','\\u0275': 'o',\n\n'\\u01A3': 'oi',\n'\\u0223': 'ou',\n'\\uA74F': 'oo',\n'\\u0070': 'p','\\u24DF': 'p','\\uFF50': 'p','\\u1E55': 'p','\\u1E57': 'p','\\u01A5': 'p','\\u1D7D': 'p','\\uA751': 'p','\\uA753': 'p','\\uA755': 'p',\n'\\u0071': 'q','\\u24E0': 'q','\\uFF51': 'q','\\u024B': 'q','\\uA757': 'q','\\uA759': 'q',\n\n'\\u0072': 'r','\\u24E1': 'r','\\uFF52': 'r','\\u0155': 'r','\\u1E59': 'r','\\u0159': 'r','\\u0211': 'r','\\u0213': 'r','\\u1E5B': 'r','\\u1E5D': 'r',\n'\\u0157': 'r','\\u1E5F': 'r','\\u024D': 'r','\\u027D': 'r','\\uA75B': 'r','\\uA7A7': 'r','\\uA783': 'r',\n\n'\\u0073': 's','\\u24E2': 's','\\uFF53': 's','\\u00DF': 's','\\u015B': 's','\\u1E65': 's','\\u015D': 's','\\u1E61': 's','\\u0161': 's','\\u1E67': 's',\n'\\u1E63': 's','\\u1E69': 's','\\u0219': 's','\\u015F': 's','\\u023F': 's','\\uA7A9': 's','\\uA785': 's','\\u1E9B': 's',\n\n'\\u0074': 't','\\u24E3': 't','\\uFF54': 't','\\u1E6B': 't','\\u1E97': 't','\\u0165': 't','\\u1E6D': 't','\\u021B': 't','\\u0163': 't','\\u1E71': 't',\n'\\u1E6F': 't','\\u0167': 't','\\u01AD': 't','\\u0288': 't','\\u2C66': 't','\\uA787': 't',\n\n'\\uA729': 'tz',\n\n'\\u0075': 'u','\\u24E4': 'u','\\uFF55': 'u','\\u00F9': 'u','\\u00FA': 'u','\\u00FB': 'u','\\u0169': 'u','\\u1E79': 'u','\\u016B': 'u','\\u1E7B': 'u',\n'\\u016D': 'u','\\u00FC': 'u','\\u01DC': 'u','\\u01D8': 'u','\\u01D6': 'u','\\u01DA': 'u','\\u1EE7': 'u','\\u016F': 'u','\\u0171': 'u','\\u01D4': 'u',\n'\\u0215': 'u','\\u0217': 'u','\\u01B0': 'u','\\u1EEB': 'u','\\u1EE9': 'u','\\u1EEF': 'u','\\u1EED': 'u','\\u1EF1': 'u','\\u1EE5': 'u','\\u1E73': 'u',\n'\\u0173': 'u','\\u1E77': 'u','\\u1E75': 'u','\\u0289': 'u',\n\n'\\u0076': 'v','\\u24E5': 'v','\\uFF56': 'v','\\u1E7D': 'v','\\u1E7F': 'v','\\u028B': 'v','\\uA75F': 'v','\\u028C': 'v',\n'\\uA761': 'vy',\n'\\u0077': 'w','\\u24E6': 'w','\\uFF57': 'w','\\u1E81': 'w','\\u1E83': 'w','\\u0175': 'w','\\u1E87': 'w','\\u1E85': 'w','\\u1E98': 'w','\\u1E89': 'w',\n'\\u2C73': 'w',\n'\\u0078': 'x','\\u24E7': 'x','\\uFF58': 'x','\\u1E8B': 'x','\\u1E8D': 'x',\n\n'\\u0079': 'y','\\u24E8': 'y','\\uFF59': 'y','\\u1EF3': 'y','\\u00FD': 'y','\\u0177': 'y','\\u1EF9': 'y','\\u0233': 'y','\\u1E8F': 'y','\\u00FF': 'y',\n'\\u1EF7': 'y','\\u1E99': 'y','\\u1EF5': 'y','\\u01B4': 'y','\\u024F': 'y','\\u1EFF': 'y',\n\n'\\u007A': 'z','\\u24E9': 'z','\\uFF5A': 'z','\\u017A': 'z','\\u1E91': 'z','\\u017C': 'z','\\u017E': 'z','\\u1E93': 'z','\\u1E95': 'z','\\u01B6': 'z',\n'\\u0225': 'z','\\u0240': 'z','\\u2C6C': 'z','\\uA763': 'z',\n};\n\n/**\n * Replace diacritics character with ASCII character\n *\n * @param {string} str diacritics string\n * @param {boolean} caseSensitive\n * @returns {string} ASCII string\n */\nexport function unaccent(str, caseSensitive) {\n    str = str.replace(/[^\\u0000-\\u007E]/g, function (accented) {\n        return diacriticsMap[accented] || accented;\n    });\n    return caseSensitive ? str : str.toLowerCase();\n}\n\n/**\n * @param {string} value\n * @returns boolean\n */\nexport function isEmail(value) {\n    // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript\n    const re = /^(([^<>()\\[\\]\\.,;:\\s@\\\"]+(\\.[^<>()\\[\\]\\.,;:\\s@\\\"]+)*)|(\\\".+\\\"))@(([^<>()[\\]\\.,;:\\s@\\\"]+\\.)+[^<>()[\\]\\.,;:\\s@\\\"]{2,})$/i;\n    return re.test(value);\n}\n\n/**\n * Return true if the string is composed of only digits\n *\n * @param {string} value\n * @returns boolean\n */\n\nexport function isNumeric(value) {\n    return Boolean(value?.match(/^\\d+$/));\n}\n\n/**\n * Parse the string to check if the value is true or false\n * If the string is empty, 0, False or false it's considered as false\n * The rest is considered as true\n *\n * @param {string} str\n * @param {boolean} [trueIfEmpty=false]\n * @returns {boolean}\n */\nexport function exprToBoolean(str, trueIfEmpty = false) {\n    return str ? !/^false|0$/i.test(str) : trueIfEmpty;\n}\n", "import { browser } from \"@web/core/browser/browser\";\nimport { onWillUnmount, useComponent } from \"@odoo/owl\";\n\n/**\n * Creates a batched version of a callback so that all calls to it in the same\n * time frame will only call the original callback once.\n * @param callback the callback to batch\n * @param synchronize this function decides the granularity of the batch (a microtick by default)\n * @returns a batched version of the original callback\n */\nexport function batched(callback, synchronize = () => Promise.resolve()) {\n    let scheduled = false;\n    return async (...args) => {\n        if (!scheduled) {\n            scheduled = true;\n            await synchronize();\n            scheduled = false;\n            callback(...args);\n        }\n    };\n}\n\n/**\n * Creates and returns a new debounced version of the passed function (func)\n * which will postpone its execution until after 'delay' milliseconds\n * have elapsed since the last time it was invoked. The debounced function\n * will return a Promise that will be resolved when the function (func)\n * has been fully executed.\n *\n * If both `options.trailing` and `options.leading` are true, the function\n * will only be invoked at the trailing edge if the debounced function was\n * called at least once more during the wait time.\n *\n * @template {Function} T the return type of the original function\n * @param {T} func the function to debounce\n * @param {number | \"animationFrame\"} delay how long should elapse before the function\n *      is called. If 'animationFrame' is given instead of a number, 'requestAnimationFrame'\n *      will be used instead of 'setTimeout'.\n * @param {boolean} [options] if true, equivalent to exclusive leading. If false, equivalent to exclusive trailing.\n * @param {object} [options]\n * @param {boolean} [options.leading=false] whether the function should be invoked at the leading edge of the timeout\n * @param {boolean} [options.trailing=true] whether the function should be invoked at the trailing edge of the timeout\n * @returns {T & { cancel: () => void }} the debounced function\n */\nexport function debounce(func, delay, options) {\n    let handle;\n    const funcName = func.name ? func.name + \" (debounce)\" : \"debounce\";\n    const useAnimationFrame = delay === \"animationFrame\";\n    const setFnName = useAnimationFrame ? \"requestAnimationFrame\" : \"setTimeout\";\n    const clearFnName = useAnimationFrame ? \"cancelAnimationFrame\" : \"clearTimeout\";\n    let lastArgs;\n    let leading = false;\n    let trailing = true;\n    if (typeof options === \"boolean\") {\n        leading = options;\n        trailing = !options;\n    } else if (options) {\n        leading = options.leading ?? leading;\n        trailing = options.trailing ?? trailing;\n    }\n\n    return Object.assign(\n        {\n            /** @type {any} */\n            [funcName](...args) {\n                return new Promise((resolve) => {\n                    if (leading && !handle) {\n                        Promise.resolve(func.apply(this, args)).then(resolve);\n                    } else {\n                        lastArgs = args;\n                    }\n                    browser[clearFnName](handle);\n                    handle = browser[setFnName](() => {\n                        handle = null;\n                        if (trailing && lastArgs) {\n                            Promise.resolve(func.apply(this, lastArgs)).then(resolve);\n                            lastArgs = null;\n                        }\n                    }, delay);\n                });\n            },\n        }[funcName],\n        {\n            cancel(execNow = false) {\n                browser[clearFnName](handle);\n                if (execNow && lastArgs) {\n                    func.apply(this, lastArgs);\n                }\n            },\n        }\n    );\n}\n\n/**\n * Function that calls recursively a request to an animation frame.\n * Useful to call a function repetitively, until asked to stop, that needs constant rerendering.\n * The provided callback gets as argument the time the last frame took.\n * @param {(deltaTime: number) => void} callback\n * @returns {() => void} stop function\n */\nexport function setRecurringAnimationFrame(callback) {\n    const handler = (timestamp) => {\n        callback(timestamp - lastTimestamp);\n        lastTimestamp = timestamp;\n        handle = browser.requestAnimationFrame(handler);\n    };\n\n    const stop = () => {\n        browser.cancelAnimationFrame(handle);\n    };\n\n    let lastTimestamp = browser.performance.now();\n    let handle = browser.requestAnimationFrame(handler);\n\n    return stop;\n}\n\n/**\n * Creates a version of the function where only the last call between two\n * animation frames is executed before the browser's next repaint. This\n * effectively throttles the function to the display's refresh rate.\n * Note that the throttled function can be any callback. It is not\n * specifically an event handler, no assumption is made about its\n * signature.\n * NB: The first call is always called immediately (leading edge).\n *\n * @template {Function} T\n * @param {T} func the function to throttle\n * @returns {T & { cancel: () => void }} the throttled function\n */\nexport function throttleForAnimation(func) {\n    let handle = null;\n    const calls = new Set();\n    const funcName = func.name ? `${func.name} (throttleForAnimation)` : \"throttleForAnimation\";\n    const pending = () => {\n        if (calls.size) {\n            handle = browser.requestAnimationFrame(pending);\n            const { args, resolve } = [...calls].pop();\n            calls.clear();\n            Promise.resolve(func.apply(this, args)).then(resolve);\n        } else {\n            handle = null;\n        }\n    };\n    return Object.assign(\n        {\n            /** @type {any} */\n            [funcName](...args) {\n                return new Promise((resolve) => {\n                    const isNew = handle === null;\n                    if (isNew) {\n                        handle = browser.requestAnimationFrame(pending);\n                        Promise.resolve(func.apply(this, args)).then(resolve);\n                    } else {\n                        calls.add({ args, resolve });\n                    }\n                });\n            },\n        }[funcName],\n        {\n            cancel() {\n                browser.cancelAnimationFrame(handle);\n                calls.clear();\n                handle = null;\n            },\n        }\n    );\n}\n\n// ----------------------------------- HOOKS -----------------------------------\n\n/**\n * Hook that returns a debounced version of the given function, and cancels\n * the potential pending execution on willUnmount.\n * @see debounce\n * @template {Function} T\n * @param {T} callback\n * @param {number | \"animationFrame\"} delay\n * @param {Object} [options]\n * @param {string} [options.execBeforeUnmount=false] executes the callback if the debounced function\n *      has been called and not resolved before destroying the component.\n * @param {boolean} [options.immediate=false] whether the function should be called on\n *      the leading edge instead of the trailing edge.\n * @returns {T & { cancel: () => void }}\n */\nexport function useDebounced(\n    callback,\n    delay,\n    { execBeforeUnmount = false, immediate = false } = {}\n) {\n    const component = useComponent();\n    const debounced = debounce(callback.bind(component), delay, immediate);\n    onWillUnmount(() => debounced.cancel(execBeforeUnmount));\n    return debounced;\n}\n\n/**\n * Hook that returns a throttled for animation version of the given function,\n * and cancels the potential pending execution on willUnmount.\n * @see throttleForAnimation\n * @template {Function} T\n * @param {T} func the function to throttle\n * @returns {T & { cancel: () => void }} the throttled function\n */\nexport function useThrottleForAnimation(func) {\n    const component = useComponent();\n    const throttledForAnimation = throttleForAnimation(func.bind(component));\n    onWillUnmount(() => throttledForAnimation.cancel());\n    return throttledForAnimation;\n}\n", "import { session } from \"@web/session\";\nimport { browser } from \"../browser/browser\";\nimport { shallowEqual } from \"@web/core/utils/objects\";\nconst { DateTime } = luxon;\n\nexport class RedirectionError extends Error {}\n\n/**\n * Transforms a key value mapping to a string formatted as url hash, e.g.\n * {a: \"x\", b: 2} -> \"a=x&b=2\"\n *\n * @param {Object} obj\n * @returns {string}\n */\nexport function objectToUrlEncodedString(obj) {\n    return Object.entries(obj)\n        .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v || \"\")}`)\n        .join(\"&\");\n}\n\n/**\n * Gets the origin url of the page, or cleans a given one\n *\n * @param {string} [origin]: a given origin url\n * @return {string} a cleaned origin url\n */\nexport function getOrigin(origin) {\n    if (origin) {\n        // remove trailing slashes\n        origin = origin.replace(/\\/+$/, \"\");\n    } else {\n        const { host, protocol } = browser.location;\n        origin = `${protocol}//${host}`;\n    }\n    return origin;\n}\n\n/**\n * @param {string} route: the relative route, or absolute in the case of cors urls\n * @param {object} [queryParams]: parameters to be appended as the url's queryString\n * @param {object} [options]\n * @param {string} [options.origin]: a precomputed origin\n */\nexport function url(route, queryParams, options = {}) {\n    const origin = getOrigin(options.origin ?? session.origin);\n    if (!route) {\n        return origin;\n    }\n\n    let queryString = objectToUrlEncodedString(queryParams || {});\n    queryString = queryString.length > 0 ? `?${queryString}` : queryString;\n\n    // Compare the wanted url against the current origin\n    let prefix = [\"http://\", \"https://\", \"//\"].some(\n        (el) => route.length >= el.length && route.slice(0, el.length) === el\n    );\n    prefix = prefix ? \"\" : origin;\n    return `${prefix}${route}${queryString}`;\n}\n\n/**\n * @param {string} model\n * @param {number} id\n * @param {string} field\n * @param {Object} [options]\n * @param {string} [options.filename]\n * @param {number} [options.height]\n * @param {string|import('luxon').DateTime} [options.unique]\n * @param {number} [options.width]\n */\nexport function imageUrl(model, id, field, { access_token, filename, height, unique, width } = {}) {\n    let route = `/web/image/${model}/${id}/${field}`;\n    if (width && height) {\n        route = `${route}/${width}x${height}`;\n    }\n    if (filename) {\n        route = `${route}/${filename}`;\n    }\n    const urlParams = {};\n    if (access_token) {\n        Object.assign(urlParams, { access_token });\n    }\n    if (unique) {\n        if (unique instanceof DateTime) {\n            urlParams.unique = unique.ts;\n        } else {\n            const dateTimeFromUnique = DateTime.fromSQL(unique);\n            if (dateTimeFromUnique.isValid) {\n                urlParams.unique = dateTimeFromUnique.ts;\n            } else if (typeof unique === \"string\" && unique.length > 0) {\n                urlParams.unique = unique;\n            }\n        }\n    }\n    return url(route, urlParams);\n}\n\n/**\n * Gets dataURL (base64 data) from the given file or blob.\n * Technically wraps FileReader.readAsDataURL in Promise.\n *\n * @param {Blob | File} file\n * @returns {Promise} resolved with the dataURL, or rejected if the file is\n *  empty or if an error occurs.\n */\nexport function getDataURLFromFile(file) {\n    if (!file) {\n        return Promise.reject();\n    }\n    return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.addEventListener(\"load\", () => {\n            // Handle Chrome bug that creates invalid data URLs for empty files\n            if (reader.result === \"data:\") {\n                resolve(`data:${file.type};base64,`);\n            } else {\n                resolve(reader.result);\n            }\n        });\n        reader.addEventListener(\"abort\", reject);\n        reader.addEventListener(\"error\", reject);\n        reader.readAsDataURL(file);\n    });\n}\n\n/**\n * Safely redirects to the given url within the same origin.\n *\n * @param {string} url\n * @throws {RedirectionError} if the given url has a different origin\n */\nexport function redirect(url) {\n    const { origin, pathname } = browser.location;\n    const _url = new URL(url, `${origin}${pathname}`);\n    if (_url.origin !== origin) {\n        throw new RedirectionError(\"Can't redirect to another origin\");\n    }\n    browser.location.assign(_url.href);\n}\n\n/**\n * This function compares two URLs. It doesn't care about the order of the search parameters.\n *\n * @param {string} _url1\n * @param {string} _url2\n * @returns {boolean} true if the urls are identical, false otherwise\n */\nexport function compareUrls(_url1, _url2) {\n    const url1 = new URL(_url1);\n    const url2 = new URL(_url2);\n    return (\n        url1.origin === url2.origin &&\n        url1.pathname === url2.pathname &&\n        shallowEqual(\n            Object.fromEntries(url1.searchParams),\n            Object.fromEntries(url2.searchParams)\n        ) &&\n        url1.hash === url2.hash\n    );\n}\n", "import { isIterable } from \"./arrays\";\n\n/**\n * XML document to create new elements from. The fact that this is a \"text/xml\"\n * document ensures that tagNames and attribute names are case sensitive.\n */\nconst serializer = new XMLSerializer();\nconst parser = new DOMParser();\nconst xmlDocument = parser.parseFromString(\"<templates/>\", \"text/xml\");\n\nfunction hasParsingError(parsedDocument) {\n    return parsedDocument.getElementsByTagName(\"parsererror\").length > 0;\n}\n\n/**\n * @param {string} str\n * @returns {Element}\n */\nexport function parseXML(str) {\n    const xml = parser.parseFromString(str, \"text/xml\");\n    if (hasParsingError(xml)) {\n        throw new Error(\n            `An error occured while parsing ${str}: ${xml.getElementsByTagName(\"parsererror\")}`\n        );\n    }\n    return xml.documentElement;\n}\n\n/**\n * @param {Element} xml\n * @returns {string}\n */\nexport function serializeXML(xml) {\n    return serializer.serializeToString(xml);\n}\n\n/**\n * @param {Element | string} xml\n * @param {(el: Element, visitChildren: () => any) => any} callback\n */\nexport function visitXML(xml, callback) {\n    const visit = (el) => {\n        if (el) {\n            let didVisitChildren = false;\n            const visitChildren = () => {\n                for (const child of el.children) {\n                    visit(child);\n                }\n                didVisitChildren = true;\n            };\n            const shouldVisitChildren = callback(el, visitChildren);\n            if (shouldVisitChildren !== false && !didVisitChildren) {\n                visitChildren();\n            }\n        }\n    };\n    const xmlDoc = typeof xml === \"string\" ? parseXML(xml) : xml;\n    visit(xmlDoc);\n}\n\n/**\n * @param {Element} parent\n * @param {Node | Node[] | void} node\n */\nexport function append(parent, node) {\n    const nodes = Array.isArray(node) ? node : [node];\n    parent.append(...nodes.filter(Boolean));\n    return parent;\n}\n\n/**\n * Combines the existing value of a node attribute with new given parts. The glue\n * is the string used to join the parts.\n *\n * @param {Element} el\n * @param {string} attr\n * @param {string | string[]} parts\n * @param {string} [glue=\" \"]\n */\nexport function combineAttributes(el, attr, parts, glue = \" \") {\n    const allValues = [];\n    if (el.hasAttribute(attr)) {\n        allValues.push(el.getAttribute(attr));\n    }\n    parts = Array.isArray(parts) ? parts : [parts];\n    parts = parts.filter((part) => !!part);\n    allValues.push(...parts);\n    el.setAttribute(attr, allValues.join(glue));\n}\n\n/**\n * XML equivalent of `document.createElement`.\n *\n * @param {string} tagName\n * @param {...(Iterable<Element> | Record<string, string>)} args\n * @returns {Element}\n */\nexport function createElement(tagName, ...args) {\n    const el = xmlDocument.createElement(tagName);\n    for (const arg of args) {\n        if (!arg) {\n            continue;\n        }\n        if (isIterable(arg)) {\n            // Children list\n            el.append(...arg);\n        } else if (typeof arg === \"object\") {\n            // Attributes\n            for (const name in arg) {\n                el.setAttribute(name, arg[name]);\n            }\n        }\n    }\n    return el;\n}\n\n/**\n * XML equivalent of `document.createTextNode`.\n *\n * @param {string} data\n * @returns {Text}\n */\nexport function createTextNode(data) {\n    return xmlDocument.createTextNode(data);\n}\n\n/**\n * Removes the given attributes on the given element and returns them as a dictionnary.\n * @param {Element} el\n * @param {string[]} attributes\n * @returns {Record<string, string>}\n */\nexport function extractAttributes(el, attributes) {\n    const attrs = Object.create(null);\n    for (const attr of attributes) {\n        attrs[attr] = el.getAttribute(attr) || \"\";\n        el.removeAttribute(attr);\n    }\n    return attrs;\n}\n\n/**\n * @param {Node} [node]\n * @param {boolean} [lower=false]\n * @returns {string}\n */\nexport function getTag(node, lower = false) {\n    const tag = (node && node.nodeName) || \"\";\n    return lower ? tag.toLowerCase() : tag;\n}\n\n/**\n * @param {Node} node\n * @param {Object} attributes\n */\nexport function setAttributes(node, attributes) {\n    for (const [name, value] of Object.entries(attributes)) {\n        node.setAttribute(name, value);\n    }\n}\n", "import { useComponent, useEffect, useExternalListener } from \"@odoo/owl\";\nimport { pick, shallowEqual } from \"@web/core/utils/objects\";\nimport { useThrottleForAnimation } from \"@web/core/utils/timing\";\n\n/**\n * @template T\n * @typedef VirtualGridParams\n * @property {ReturnType<typeof import(\"@odoo/owl\").useRef>} scrollableRef\n *  a ref to the scrollable element\n * @property {ScrollPosition} [initialScroll={ left: 0, top: 0 }]\n *  the initial scroll position of the scrollable element\n * @property {(changed: Partial<VirtualGridIndexes>) => void} [onChange=() => this.render()]\n *  a callback called when the visible items change, i.e. when on scroll or resize.\n *  the default implementation is to re-render the component.\n * @property {number} [bufferCoef=1]\n *  the coefficient to calculate the buffer size around the visible area.\n *  The buffer size is equal to bufferCoef * windowSize.\n *  The default value is 1: it means that the buffer size takes one more window size on each side.\n *  So the whole area that will be rendered is 3 times the window size.\n *  If you use each direction, it could be up to 9 times the window size (3x3).\n *  Consider lowering this value if you have a costful rendering.\n *  A value of 0 means no buffer.\n */\n\n/**\n * @typedef VirtualGridIndexes\n * @property {[number, number] | undefined} columnsIndexes\n * @property {[number, number] | undefined} rowsIndexes\n */\n\n/**\n * @typedef VirtualGridSetters\n * @property {(widths: number[]) => void} setColumnsWidths\n *  Use it to set the width of each column.\n *  Indexes should match the indexes of the columns.\n * @property {(heights: number[]) => void} setRowsHeights\n *  Use it to set the height of each row.\n *  Indexes should match the indexes of the rows.\n */\n\n/**\n * @typedef ScrollPosition\n * @property {number} left\n * @property {number} top\n */\n\nconst BUFFER_COEFFICIENT = 1;\n\n/**\n * @typedef GetIndexesParams\n * @property {number[]} sizes contains the sizes of the items. Each size is the sum of the sizes of the previous items and the size of the current item.\n * @property {number} start it is the start position of the visible area, here it is the scroll position.\n * @property {number} span it is the size of the visible area, here it is the window size.\n * @property {number} [prevStartIndex] the previous start index, it is used to optimize the calculation.\n * @property {number} [bufferCoef=BUFFER_COEFFICIENT] the coefficient to calculate the buffer size.\n */\n\n/**\n * This function calculates the indexes of the visible items in a virtual list.\n *\n * @param {GetIndexesParams} param0\n * @returns {[number, number] | undefined} the indexes of the visible items with a surrounding buffer of totalSize on each side.\n */\nfunction getIndexes({ sizes, start, span, prevStartIndex, bufferCoef = BUFFER_COEFFICIENT }) {\n    if (!sizes || !sizes.length) {\n        return [];\n    }\n    if (sizes.at(-1) < span) {\n        // all items could be displayed\n        return [0, sizes.length - 1];\n    }\n    const bufferSize = Math.round(span * bufferCoef);\n    const bufferStart = start - bufferSize;\n    const bufferEnd = start + span + bufferSize;\n\n    let startIndex = prevStartIndex ?? 0;\n    // we search the first index such that sizes[index] > bufferStart\n    while (startIndex > 0 && sizes[startIndex] > bufferStart) {\n        startIndex--;\n    }\n    while (startIndex < sizes.length - 1 && sizes[startIndex] <= bufferStart) {\n        startIndex++;\n    }\n\n    let endIndex = startIndex;\n    // we search the last index such that (sizes[index - 1] ?? 0) < bufferEnd\n    while (endIndex < sizes.length - 1 && (sizes[endIndex - 1] ?? 0) < bufferEnd) {\n        endIndex++;\n    }\n    while (endIndex > startIndex && (sizes[endIndex - 1] ?? 0) >= bufferEnd) {\n        endIndex--;\n    }\n    return [startIndex, endIndex];\n}\n\n/**\n * Calculates the displayed items in a virtual grid.\n *\n * Requirements:\n *  - the scrollable area has a fixed height and width.\n *  - the items are rendered with a proper offset inside the scrollable area.\n *    This can be achieved e.g. with a css grid or an absolute positioning.\n *\n * @template T\n * @param {VirtualGridParams<T>} params\n * @returns {VirtualGridIndexes & VirtualGridSetters}\n */\nexport function useVirtualGrid({ scrollableRef, initialScroll, onChange, bufferCoef }) {\n    const comp = useComponent();\n    onChange ||= () => comp.render();\n\n    const current = { scroll: { left: 0, top: 0, ...initialScroll } };\n    const computeColumnsIndexes = () => {\n        return getIndexes({\n            sizes: current.summedColumnsWidths,\n            start: Math.abs(current.scroll.left),\n            span: window.innerWidth,\n            prevStartIndex: current.columnsIndexes?.[0],\n            bufferCoef,\n        });\n    };\n    const computeRowsIndexes = () => {\n        return getIndexes({\n            sizes: current.summedRowsHeights,\n            start: current.scroll.top,\n            span: window.innerHeight,\n            prevStartIndex: current.rowsIndexes?.[0],\n            bufferCoef,\n        });\n    };\n    const throttledCompute = useThrottleForAnimation(() => {\n        const changed = [];\n        const columnsVisibleIndexes = computeColumnsIndexes();\n        if (!shallowEqual(columnsVisibleIndexes, current.columnsIndexes)) {\n            current.columnsIndexes = columnsVisibleIndexes;\n            changed.push(\"columnsIndexes\");\n        }\n        const rowsVisibleIndexes = computeRowsIndexes();\n        if (!shallowEqual(rowsVisibleIndexes, current.rowsIndexes)) {\n            current.rowsIndexes = rowsVisibleIndexes;\n            changed.push(\"rowsIndexes\");\n        }\n        if (changed.length) {\n            onChange(pick(current, ...changed));\n        }\n    });\n    const scrollListener = (/** @type {Event & { target: Element }} */ ev) => {\n        current.scroll.left = ev.target.scrollLeft;\n        current.scroll.top = ev.target.scrollTop;\n        throttledCompute();\n    };\n    useEffect(\n        (el) => {\n            el?.addEventListener(\"scroll\", scrollListener);\n            return () => el?.removeEventListener(\"scroll\", scrollListener);\n        },\n        () => [scrollableRef.el]\n    );\n    useExternalListener(window, \"resize\", () => throttledCompute());\n    return {\n        get columnsIndexes() {\n            return current.columnsIndexes;\n        },\n        get rowsIndexes() {\n            return current.rowsIndexes;\n        },\n        setColumnsWidths(widths) {\n            let acc = 0;\n            current.summedColumnsWidths = widths.map((w) => (acc += w));\n            delete current.columnsIndexes;\n            current.columnsIndexes = computeColumnsIndexes();\n        },\n        setRowsHeights(heights) {\n            let acc = 0;\n            current.summedRowsHeights = heights.map((h) => (acc += h));\n            delete current.rowsIndexes;\n            current.rowsIndexes = computeRowsIndexes();\n        },\n    };\n}\n", "import { isMacOS } from \"@web/core/browser/feature_detection\";\nimport { useHotkey } from \"@web/core/hotkeys/hotkey_hook\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"@web/core/registry\";\nimport { capitalize } from \"@web/core/utils/strings\";\nimport { getVisibleElements } from \"@web/core/utils/ui\";\nimport { DefaultCommandItem } from \"./command_palette\";\n\nimport { Component } from \"@odoo/owl\";\n\nconst commandSetupRegistry = registry.category(\"command_setup\");\ncommandSetupRegistry.add(\"default\", {\n    emptyMessage: _t(\"No command found\"),\n    placeholder: _t(\"Search for a command...\"),\n});\n\nexport class HotkeyCommandItem extends Component {\n    static template = \"web.HotkeyCommandItem\";\n    static props = [\"hotkey\", \"hotkeyOptions?\", \"name?\", \"searchValue?\", \"executeCommand\", \"slots\"];\n    setup() {\n        useHotkey(this.props.hotkey, this.props.executeCommand);\n    }\n\n    getKeysToPress(command) {\n        const { hotkey } = command;\n        let result = hotkey.split(\"+\");\n        if (isMacOS()) {\n            result = result\n                .map((x) => x.replace(\"control\", \"command\"))\n                .map((x) => x.replace(\"alt\", \"control\"));\n        }\n        return result.map((key) => key.toUpperCase());\n    }\n}\n\nconst commandCategoryRegistry = registry.category(\"command_categories\");\nconst commandProviderRegistry = registry.category(\"command_provider\");\ncommandProviderRegistry.add(\"command\", {\n    provide: (env, options = {}) => {\n        const commands = env.services.command\n            .getCommands(options.activeElement)\n            .map((cmd) => {\n                cmd.category = commandCategoryRegistry.contains(cmd.category)\n                    ? cmd.category\n                    : \"default\";\n                return cmd;\n            })\n            .filter((command) => command.isAvailable === undefined || command.isAvailable());\n        // Filter out same category dupplicate commands\n        const uniqueCommands = commands.filter((obj, index) => {\n            return (\n                index ===\n                commands.findIndex((o) => obj.name === o.name && obj.category === o.category)\n            );\n        });\n        return uniqueCommands.map((command) => ({\n            Component: command.hotkey ? HotkeyCommandItem : DefaultCommandItem,\n            action: command.action,\n            category: command.category,\n            name: command.name,\n            props: {\n                hotkey: command.hotkey,\n                hotkeyOptions: command.hotkeyOptions,\n            },\n        }));\n    },\n});\n\ncommandProviderRegistry.add(\"data-hotkeys\", {\n    provide: (env, options = {}) => {\n        const commands = [];\n        const overlayModifier = registry.category(\"services\").get(\"hotkey\").overlayModifier;\n        // Also retrieve all hotkeyables elements\n        for (const el of getVisibleElements(\n            options.activeElement,\n            \"[data-hotkey]:not(:disabled)\"\n        )) {\n            const closest = el.closest(\"[data-command-category]\");\n            const category = closest ? closest.dataset.commandCategory : \"default\";\n            if (category === \"disabled\") {\n                continue;\n            }\n\n            const description =\n                el.title ||\n                el.dataset.bsOriginalTitle || // LEGACY: bootstrap moves title to data-bs-original-title\n                el.dataset.tooltip ||\n                el.placeholder ||\n                (el.innerText &&\n                    `${el.innerText.slice(0, 50)}${el.innerText.length > 50 ? \"...\" : \"\"}`) ||\n                _t(\"no description provided\");\n\n            commands.push({\n                Component: HotkeyCommandItem,\n                action: () => {\n                    // AAB: not sure it is enough, we might need to trigger all events that occur when you actually click\n                    el.focus();\n                    el.click();\n                },\n                category,\n                name: capitalize(description.trim().toLowerCase()),\n                props: {\n                    hotkey: `${overlayModifier}+${el.dataset.hotkey}`,\n                },\n            });\n        }\n        return commands;\n    },\n});\n", "import { Dialog } from \"@web/core/dialog/dialog\";\nimport { useHotkey } from \"@web/core/hotkeys/hotkey_hook\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { KeepLast, Race } from \"@web/core/utils/concurrency\";\nimport { useAutofocus, useService } from \"@web/core/utils/hooks\";\nimport { scrollTo } from \"@web/core/utils/scrolling\";\nimport { fuzzyLookup } from \"@web/core/utils/search\";\nimport { debounce } from \"@web/core/utils/timing\";\nimport { isMacOS, isMobileOS } from \"@web/core/browser/feature_detection\";\nimport { escapeRegExp } from \"@web/core/utils/strings\";\n\nimport {\n    Component,\n    onWillStart,\n    onWillDestroy,\n    EventBus,\n    useRef,\n    useState,\n    markRaw,\n    useExternalListener,\n} from \"@odoo/owl\";\n\nconst DEFAULT_PLACEHOLDER = _t(\"Search...\");\nconst DEFAULT_EMPTY_MESSAGE = _t(\"No result found\");\nconst FUZZY_NAMESPACES = [\"default\"];\n\n/**\n * @typedef {import(\"./command_service\").Command} Command\n */\n\n/**\n * @typedef {Command & {\n *  Component?: Component;\n *  props?: object;\n * }} CommandItem\n */\n\n/**\n * @typedef {{\n *  namespace?: string;\n *  provide: ()=>CommandItem[];\n * }} Provider\n */\n\n/**\n * @typedef {{\n *  categories: string[];\n *  debounceDelay: number;\n *  emptyMessage: string;\n *  placeholder: string;\n * }} NamespaceConfig\n */\n\n/**\n * @typedef {{\n *  configByNamespace?: {[namespace: string]: NamespaceConfig};\n *  FooterComponent?: Component;\n *  providers: Provider[];\n *  searchValue?: string;\n * }} CommandPaletteConfig\n */\n\n/**\n * Util used to filter commands that are within category.\n * Note: for the default category, also get all commands having invalid category.\n *\n * @param {string} categoryName the category key\n * @param {string[]} categories\n * @returns an array filter predicate\n */\nfunction commandsWithinCategory(categoryName, categories) {\n    return (cmd) => {\n        const inCurrentCategory = categoryName === cmd.category;\n        const fallbackCategory = categoryName === \"default\" && !categories.includes(cmd.category);\n        return inCurrentCategory || fallbackCategory;\n    };\n}\n\nexport function splitCommandName(name, searchValue) {\n    if (name) {\n        const splitName = name.split(new RegExp(`(${escapeRegExp(searchValue)})`, \"ig\"));\n        return searchValue.length && splitName.length > 1 ? splitName : [name];\n    }\n    return [];\n}\n\nexport class DefaultCommandItem extends Component {\n    static template = \"web.DefaultCommandItem\";\n    static props = {\n        slots: { type: Object, optional: true },\n        // Props send by the command palette:\n        hotkey: { type: String, optional: true },\n        hotkeyOptions: { type: String, optional: true },\n        name: { type: String, optional: true },\n        searchValue: { type: String, optional: true },\n        executeCommand: { type: Function, optional: true },\n    };\n}\n\nexport class CommandPalette extends Component {\n    static template = \"web.CommandPalette\";\n    static components = { Dialog };\n    static lastSessionId = 0;\n    static props = {\n        bus: { type: EventBus, optional: true },\n        close: Function,\n        config: Object,\n        closeMe: { type: Function, optional: true },\n    };\n\n    setup() {\n        if (this.props.bus) {\n            const setConfig = ({ detail }) => this.setCommandPaletteConfig(detail);\n            this.props.bus.addEventListener(`SET-CONFIG`, setConfig);\n            onWillDestroy(() => this.props.bus.removeEventListener(`SET-CONFIG`, setConfig));\n        }\n\n        this.keyId = 1;\n        this.race = new Race();\n        this.keepLast = new KeepLast();\n        this._sessionId = CommandPalette.lastSessionId++;\n        this.DefaultCommandItem = DefaultCommandItem;\n        this.activeElement = useService(\"ui\").activeElement;\n        this.inputRef = useAutofocus();\n\n        useHotkey(\"Enter\", () => this.executeSelectedCommand(), { bypassEditableProtection: true });\n        useHotkey(\"Control+Enter\", () => this.executeSelectedCommand(true), {\n            bypassEditableProtection: true,\n        });\n        useHotkey(\"ArrowUp\", () => this.selectCommandAndScrollTo(\"PREV\"), {\n            bypassEditableProtection: true,\n            allowRepeat: true,\n        });\n        useHotkey(\"ArrowDown\", () => this.selectCommandAndScrollTo(\"NEXT\"), {\n            bypassEditableProtection: true,\n            allowRepeat: true,\n        });\n        useExternalListener(window, \"mousedown\", this.onWindowMouseDown);\n\n        /**\n         * @type {{ commands: CommandItem[],\n         *          emptyMessage: string,\n         *          FooterComponent: Component,\n         *          namespace: string,\n         *          placeholder: string,\n         *          searchValue: string,\n         *          selectedCommand: CommandItem }}\n         */\n        this.state = useState({});\n\n        this.root = useRef(\"root\");\n        this.listboxRef = useRef(\"listbox\");\n\n        onWillStart(() => this.setCommandPaletteConfig(this.props.config));\n    }\n\n    get commandsByCategory() {\n        const categories = [];\n        for (const category of this.categoryKeys) {\n            const commands = this.state.commands.filter(\n                commandsWithinCategory(category, this.categoryKeys)\n            );\n            if (commands.length) {\n                categories.push({\n                    commands,\n                    name: this.categoryNames[category],\n                    keyId: category,\n                });\n            }\n        }\n        return categories;\n    }\n\n    /**\n     * Apply the new config to the command pallet\n     * @param {CommandPaletteConfig} config\n     */\n    async setCommandPaletteConfig(config) {\n        this.configByNamespace = config.configByNamespace || {};\n        this.state.FooterComponent = config.FooterComponent;\n\n        this.providersByNamespace = { default: [] };\n        for (const provider of config.providers) {\n            const namespace = provider.namespace || \"default\";\n            if (namespace in this.providersByNamespace) {\n                this.providersByNamespace[namespace].push(provider);\n            } else {\n                this.providersByNamespace[namespace] = [provider];\n            }\n        }\n\n        const { namespace, searchValue } = this.processSearchValue(config.searchValue || \"\");\n        this.switchNamespace(namespace);\n        this.state.searchValue = searchValue;\n        await this.race.add(this.search(searchValue));\n    }\n\n    /**\n     * Modifies the commands to be displayed according to the namespace and the options.\n     * Selects the first command in the new list.\n     * @param {string} namespace\n     * @param {object} options\n     */\n    async setCommands(namespace, options = {}) {\n        this.categoryKeys = [\"default\"];\n        this.categoryNames = {};\n        const proms = this.providersByNamespace[namespace].map((provider) => {\n            const { provide } = provider;\n            const result = provide(this.env, options);\n            return result;\n        });\n        let commands = (await this.keepLast.add(Promise.all(proms))).flat();\n        const namespaceConfig = this.configByNamespace[namespace] || {};\n        if (options.searchValue && FUZZY_NAMESPACES.includes(namespace)) {\n            commands = fuzzyLookup(options.searchValue, commands, (c) => c.name);\n        } else {\n            // we have to sort the commands by category to avoid navigation issues with the arrows\n            if (namespaceConfig.categories) {\n                let commandsSorted = [];\n                this.categoryKeys = namespaceConfig.categories;\n                this.categoryNames = namespaceConfig.categoryNames || {};\n                if (!this.categoryKeys.includes(\"default\")) {\n                    this.categoryKeys.push(\"default\");\n                }\n                for (const category of this.categoryKeys) {\n                    commandsSorted = commandsSorted.concat(\n                        commands.filter(commandsWithinCategory(category, this.categoryKeys))\n                    );\n                }\n                commands = commandsSorted;\n            }\n        }\n\n        this.state.commands = markRaw(\n            commands.slice(0, 100).map((command) => ({\n                ...command,\n                keyId: this.keyId++,\n                splitName: splitCommandName(command.name, options.searchValue),\n            }))\n        );\n        this.selectCommand(this.state.commands.length ? 0 : -1);\n        this.mouseSelectionActive = false;\n        this.state.emptyMessage = (\n            namespaceConfig.emptyMessage || DEFAULT_EMPTY_MESSAGE\n        ).toString();\n    }\n\n    selectCommand(index) {\n        if (index === -1 || index >= this.state.commands.length) {\n            this.state.selectedCommand = null;\n            return;\n        }\n        this.state.selectedCommand = markRaw(this.state.commands[index]);\n    }\n\n    selectCommandAndScrollTo(type) {\n        // In case the mouse is on the palette command, it avoids the selection\n        // of a command caused by a scroll.\n        this.mouseSelectionActive = false;\n        const index = this.state.commands.indexOf(this.state.selectedCommand);\n        if (index === -1) {\n            return;\n        }\n        let nextIndex;\n        if (type === \"NEXT\") {\n            nextIndex = index < this.state.commands.length - 1 ? index + 1 : 0;\n        } else if (type === \"PREV\") {\n            nextIndex = index > 0 ? index - 1 : this.state.commands.length - 1;\n        }\n        this.selectCommand(nextIndex);\n\n        const command = this.listboxRef.el.querySelector(`#o_command_${nextIndex}`);\n        scrollTo(command, { scrollable: this.listboxRef.el });\n    }\n\n    onCommandClicked(event, index) {\n        event.preventDefault(); // Prevent redirect for commands with href\n        this.selectCommand(index);\n        const ctrlKey = isMacOS() ? event.metaKey : event.ctrlKey;\n        this.executeSelectedCommand(ctrlKey);\n    }\n\n    /**\n     * Execute the action related to the order.\n     * If this action returns a config, then we will use it in the command palette,\n     * otherwise we close the command palette.\n     * @param {CommandItem} command\n     */\n    async executeCommand(command) {\n        const config = await command.action();\n        if (config) {\n            this.setCommandPaletteConfig(config);\n        } else {\n            this.props.close();\n        }\n    }\n\n    async executeSelectedCommand(ctrlKey) {\n        await this.searchValuePromise;\n        const selectedCommand = this.state.selectedCommand;\n        if (selectedCommand) {\n            if (!ctrlKey) {\n                this.executeCommand(selectedCommand);\n            } else if (selectedCommand.href) {\n                window.open(selectedCommand.href, \"_blank\");\n            }\n        }\n    }\n\n    onCommandMouseEnter(index) {\n        if (this.mouseSelectionActive) {\n            this.selectCommand(index);\n        } else {\n            this.mouseSelectionActive = true;\n        }\n    }\n\n    async search(searchValue) {\n        this.state.isLoading = true;\n        try {\n            await this.setCommands(this.state.namespace, {\n                searchValue,\n                activeElement: this.activeElement,\n                sessionId: this._sessionId,\n            });\n        } finally {\n            this.state.isLoading = false;\n        }\n        if (this.inputRef.el) {\n            this.inputRef.el.focus();\n        }\n    }\n\n    debounceSearch(value) {\n        const { namespace, searchValue } = this.processSearchValue(value);\n        if (namespace !== \"default\" && this.state.namespace !== namespace) {\n            this.switchNamespace(namespace);\n        }\n        this.state.searchValue = searchValue;\n        this.searchValuePromise = this.lastDebounceSearch(searchValue).catch(() => {\n            this.searchValuePromise = null;\n        });\n    }\n\n    onSearchInput(ev) {\n        this.debounceSearch(ev.target.value);\n    }\n\n    onKeyDown(ev) {\n        if (ev.key.toLowerCase() === \"backspace\" && !ev.target.value.length && !ev.repeat) {\n            this.switchNamespace(\"default\");\n            this.state.searchValue = \"\";\n            this.searchValuePromise = this.lastDebounceSearch(\"\").catch(() => {\n                this.searchValuePromise = null;\n            });\n        }\n    }\n\n    /**\n     * Close the palette on outside click.\n     */\n    onWindowMouseDown(ev) {\n        if (!this.root.el.contains(ev.target)) {\n            this.props.close();\n        }\n    }\n\n    switchNamespace(namespace) {\n        if (this.lastDebounceSearch) {\n            this.lastDebounceSearch.cancel();\n        }\n        const namespaceConfig = this.configByNamespace[namespace] || {};\n        this.lastDebounceSearch = debounce(\n            (value) => this.search(value),\n            namespaceConfig.debounceDelay || 0\n        );\n        this.state.namespace = namespace;\n        this.state.placeholder = namespaceConfig.placeholder || DEFAULT_PLACEHOLDER.toString();\n    }\n\n    processSearchValue(searchValue) {\n        let namespace = \"default\";\n        if (searchValue.length && this.providersByNamespace[searchValue[0]]) {\n            namespace = searchValue[0];\n            searchValue = searchValue.slice(1);\n        }\n        return { namespace, searchValue };\n    }\n\n    get isMacOS() {\n        return isMacOS();\n    }\n    get isMobileOS() {\n        return isMobileOS();\n    }\n}\n", "// This module makes it so that some errors only display a notification instead of an error dialog\n\nimport { registry } from \"@web/core/registry\";\nimport { odooExceptionTitleMap } from \"@web/core/errors/error_dialogs\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nodooExceptionTitleMap.forEach((title, exceptionName) => {\n    registry.category(\"error_notifications\").add(exceptionName, {\n        title: title,\n        type: \"warning\",\n        sticky: true,\n    });\n});\n\nconst sessionExpired = {\n    title: _t(\"Odoo Session Expired\"),\n    message: _t(\"Your Odoo session expired. The current page is about to be refreshed.\"),\n    buttons: [\n        {\n            text: _t(\"Ok\"),\n            click: () => window.location.reload(true),\n            close: true,\n        },\n    ],\n};\n\nregistry\n    .category(\"error_notifications\")\n    .add(\"odoo.http.SessionExpiredException\", sessionExpired)\n    .add(\"werkzeug.exceptions.Forbidden\", sessionExpired)\n    .add(\"504\", {\n        title: _t(\"Request timeout\"),\n        message: _t(\n            \"The operation was interrupted. This usually means that the current operation is taking too much time.\"\n        ),\n    });\n", "import { App } from \"@odoo/owl\";\nimport { registry } from \"@web/core/registry\";\nimport { getTemplate } from \"@web/core/templates\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nclass ComponentManager {\n    constructor(env) {\n        this.env = env;\n        this.appConfig = {\n            getTemplate,\n            env: env,\n            dev: env.debug,\n            translateFn: _t,\n            translatableAttributes: [\"data-tooltip\"],\n        };\n        /** @type {Map<HTMLElement, { app: App, mountProm: Promise<any> }>} */\n        this.apps = new Map();\n    }\n    async mountComponents() {\n        for (const [key, component] of registry.category(\"public_components\").getEntries()) {\n            for (const el of document.querySelectorAll(\n                `owl-component[name=\"${CSS.escape(key)}\"]`\n            )) {\n                if (!this.apps.has(el)) {\n                    const props = JSON.parse(el.getAttribute(\"props\") || \"{}\");\n\n                    const app = new App(component, {\n                        ...this.appConfig,\n                        props,\n                    });\n                    this.apps.set(el, { app, mountProm: app.mount(el) });\n                }\n            }\n        }\n        await Promise.all([...this.apps.values()].map(({ mountProm }) => mountProm));\n    }\n    destroyComponents() {\n        for (const { app } of this.apps.values()) {\n            app.destroy();\n        }\n        this.apps.clear();\n    }\n}\n\nexport const publicComponentService = {\n    start(env) {\n        return new ComponentManager(env);\n    },\n};\n\nregistry.category(\"services\").add(\"public_component\", publicComponentService);\n", "import {\n    deserializeDate,\n    deserializeDateTime,\n    parseDate,\n    parseDateTime,\n} from \"@web/core/l10n/dates\";\nimport PublicWidget from \"@web/legacy/js/public/public_widget\";\n\nexport const DateTimePickerWidget = PublicWidget.Widget.extend({\n    selector: \"[data-widget='datetime-picker']\",\n    disabledInEditableMode: true,\n\n    /**\n     * @override\n     */\n    start() {\n        this._super(...arguments);\n        const { widgetType, minDate, maxDate } = this.el.dataset;\n        const type = widgetType || \"datetime\";\n        const { value } = this.el;\n        const [parse, deserialize] =\n            type === \"date\" ? [parseDate, deserializeDate] : [parseDateTime, deserializeDateTime];\n        this.disableDateTimePicker = this.call(\"datetime_picker\", \"create\", {\n            target: this.el,\n            pickerProps: {\n                type,\n                minDate: minDate && deserialize(minDate),\n                maxDate: maxDate && deserialize(maxDate),\n                value: parse(value),\n            },\n        }).enable();\n    },\n    /**\n     * @override\n     */\n    destroy() {\n        this.disableDateTimePicker();\n        return this._super(...arguments);\n    },\n});\n\nPublicWidget.registry.DateTimePickerWidget = DateTimePickerWidget;\n", "/** @odoo-module **/\n\nimport { isMobileOS } from \"@web/core/browser/feature_detection\";\nimport { loadJS } from \"@web/core/assets\";\n\n/**\n * Until we have our own implementation of the /web/static/lib/pdfjs/web/viewer.{html,js,css}\n * (currently based on Firefox), this method allows us to hide the buttons that we do not want:\n * * \"Open File\"\n * * \"View Bookmark\"\n * * \"Print\" (Hidden on mobile device like Android, iOS, ...)\n * * \"Download\" (Hidden on mobile device like Android, iOS, ...)\n *\n * @link https://mozilla.github.io/pdf.js/getting_started/\n *\n * @param {Element} rootElement\n */\nexport function hidePDFJSButtons(rootElement) {\n    const cssStyle = document.createElement(\"style\");\n    cssStyle.rel = \"stylesheet\";\n    cssStyle.textContent = `button#secondaryOpenFile.secondaryToolbarButton, button#openFile.toolbarButton,\n    button#editorFreeText.toolbarButton, button#editorInk.toolbarButton, button#editorStamp.toolbarButton,\n    button#secondaryOpenFile.secondaryToolbarButton,\na#secondaryViewBookmark.secondaryToolbarButton, a#viewBookmark.toolbarButton {\ndisplay: none !important;\n}`;\n    if (isMobileOS()) {\n        cssStyle.textContent = `${cssStyle.innerHTML}\nbutton#secondaryDownload.secondaryToolbarButton, button#download.toolbarButton,\nbutton#editorFreeText.toolbarButton, button#editorInk.toolbarButton, button#editorStamp.toolbarButton,\nbutton#secondaryPrint.secondaryToolbarButton, button#print.toolbarButton{\ndisplay: none !important;\n}`;\n    }\n    const iframe =\n        rootElement.tagName === \"IFRAME\" ? rootElement : rootElement.querySelector(\"iframe\");\n    if (iframe) {\n        if (!iframe.dataset.hideButtons) {\n            iframe.dataset.hideButtons = \"true\";\n            iframe.addEventListener(\"load\", (event) => {\n                if (iframe.contentDocument && iframe.contentDocument.head) {\n                    iframe.contentDocument.head.appendChild(cssStyle);\n                }\n            });\n        }\n    } else {\n        console.warn(\"No IFRAME found\");\n    }\n}\n\nexport async function loadPDFJSAssets() {\n    return Promise.all([\n        loadJS(\"/web/static/lib/pdfjs/build/pdf.js\"),\n        loadJS(\"/web/static/lib/pdfjs/build/pdf.worker.js\"),\n    ]);\n}\n", "import { cookie } from \"@web/core/browser/cookie\";\nimport publicWidget from '@web/legacy/js/public/public_widget';\n\nimport lazyloader from \"@web/legacy/js/public/lazyloader\";\n\nimport { makeEnv, startServices } from \"@web/env\";\nimport { getTemplate } from '@web/core/templates';\nimport { MainComponentsContainer } from \"@web/core/main_components_container\";\nimport { browser } from '@web/core/browser/browser';\nimport { _t } from \"@web/core/l10n/translation\";\nimport { jsToPyLocale, pyToJsLocale } from \"@web/core/l10n/utils\";\nimport { App, Component, whenReady } from \"@odoo/owl\";\nimport { RPCError } from '@web/core/network/rpc';\n\nconst { Settings } = luxon;\n\n// Load localizations outside the PublicRoot to not wait for DOM ready (but\n// wait for them in PublicRoot)\nfunction getLang() {\n    var html = document.documentElement;\n    return jsToPyLocale(html.getAttribute('lang')) || 'en_US';\n}\nconst lang = cookie.get('frontend_lang') || getLang(); // FIXME the cookie value should maybe be in the ctx?\n\n\n/**\n * Element which is designed to be unique and that will be the top-most element\n * in the widget hierarchy. So, all other widgets will be indirectly linked to\n * this Class instance. Its main role will be to retrieve RPC demands from its\n * children and handle them.\n */\nexport const PublicRoot = publicWidget.Widget.extend({\n    events: {\n        'submit .js_website_submit_form': '_onWebsiteFormSubmit',\n        'click .js_disable_on_click': '_onDisableOnClick',\n    },\n    custom_events: {\n        call_service: '_onCallService',\n        context_get: '_onContextGet',\n        main_object_request: '_onMainObjectRequest',\n        widgets_start_request: '_onWidgetsStartRequest',\n        widgets_stop_request: '_onWidgetsStopRequest',\n    },\n\n    /**\n     * @constructor\n     */\n    init: function (_, env) {\n        this._super.apply(this, arguments);\n        this.env = env;\n        this.publicWidgets = [];\n    },\n    /**\n     * @override\n     */\n    start: function () {\n        var defs = [\n            this._super.apply(this, arguments),\n            this._startWidgets()\n        ];\n\n        // Display image thumbnail\n        this.$(\".o_image[data-mimetype^='image']\").each(function () {\n            var $img = $(this);\n            if (/gif|jpe|jpg|png|webp/.test($img.data('mimetype')) && $img.data('src')) {\n                $img.css('background-image', \"url('\" + $img.data('src') + \"')\");\n            }\n        });\n\n        // Auto scroll\n        if (window.location.hash.indexOf(\"scrollTop=\") > -1) {\n            this.el.scrollTop = +window.location.hash.match(/scrollTop=([0-9]+)/)[1];\n        }\n\n        return Promise.all(defs);\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * Retrieves the global context of the public environment. This is the\n     * context which is automatically added to each RPC.\n     *\n     * @private\n     * @param {Object} [context]\n     * @returns {Object}\n     */\n    _getContext: function (context) {\n        return Object.assign({\n            'lang': getLang(),\n        }, context || {});\n    },\n    /**\n     * Retrieves the global context of the public environment (as\n     * @see _getContext) but with extra informations that would be useless to\n     * send with each RPC.\n     *\n     * @private\n     * @param {Object} [context]\n     * @returns {Object}\n     */\n    _getExtraContext: function (context) {\n        return this._getContext(context);\n    },\n    /**\n     * @private\n     * @param {Object} [options]\n     * @returns {Object}\n     */\n    _getPublicWidgetsRegistry: function (options) {\n        return publicWidget.registry;\n    },\n    /**\n     * Creates an PublicWidget instance for each DOM element which matches the\n     * `selector` key of one of the registered widgets\n     * (@see PublicWidget.selector).\n     *\n     * @private\n     * @param {jQuery} [$from]\n     *        only initialize the public widgets whose `selector` matches the\n     *        element or one of its descendant (default to the wrapwrap element)\n     * @param {Object} [options]\n     * @returns {Deferred}\n     */\n    _startWidgets: function ($from, options) {\n        var self = this;\n\n        if ($from === undefined) {\n            $from = this.$('#wrapwrap');\n            if (!$from.length) {\n                // TODO Remove this once all frontend layouts possess a\n                // #wrapwrap element (which is necessary for those pages to be\n                // adapted correctly if the user installs website).\n                $from = this.$el;\n            }\n        }\n        options = Object.assign({}, options, {\n            wysiwyg: $('#wrapwrap').data('wysiwyg'),\n        });\n\n        this._stopWidgets($from);\n\n        var defs = Object.values(this._getPublicWidgetsRegistry(options)).map((PublicWidget) => {\n            const selector = PublicWidget.prototype.selector;\n            if (!selector) {\n                return;\n            }\n            const selectorHas = PublicWidget.prototype.selectorHas;\n            const selectorFunc = typeof selector === 'function'\n                ? selector\n                : fromEl => {\n                    const els = [...fromEl.querySelectorAll(selector)];\n                    if (fromEl.matches(selector)) {\n                        els.push(fromEl);\n                    }\n                    return els;\n                };\n\n            let targetEls = [];\n            for (const fromEl of $from) {\n                targetEls.push(...selectorFunc(fromEl));\n            }\n            if (selectorHas) {\n                targetEls = targetEls.filter(el => !!el.querySelector(selectorHas));\n            }\n\n            const proms = targetEls.map(el => {\n                var widget = new PublicWidget(self, options);\n                self.publicWidgets.push(widget);\n                return widget.attachTo(el);\n            });\n            return Promise.all(proms);\n        });\n        return Promise.all(defs);\n    },\n    /**\n     * Destroys all registered widget instances. Website would need this before\n     * saving while in edition mode for example.\n     *\n     * @private\n     * @param {jQuery} [$from]\n     *        only stop the public widgets linked to the given element(s) or one\n     *        of its descendants\n     */\n    _stopWidgets: function ($from) {\n        var removedWidgets = this.publicWidgets.map((widget) => {\n            if (!$from\n                || $from.filter(widget.el).length\n                || $from.find(widget.el).length) {\n                widget.destroy();\n                return widget;\n            }\n            return null;\n        });\n        this.publicWidgets = this.publicWidgets.filter((x) => removedWidgets.indexOf(x) < 0);\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * Calls the requested service from the env. Automatically adds the global\n     * context to RPCs.\n     *\n     * @private\n     * @param {OdooEvent} event\n     */\n    _onCallService: function (ev) {\n        const payload = ev.data;\n        const service = this.env.services[payload.service];\n        const result = service[payload.method].apply(service, payload.args || []);\n        payload.callback(result);\n        ev.stopPropagation();\n    },\n    /**\n     * Called when someone asked for the global public context.\n     *\n     * @private\n     * @param {OdooEvent} ev\n     */\n    _onContextGet: function (ev) {\n        if (ev.data.extra) {\n            ev.data.callback(this._getExtraContext(ev.data.context));\n        } else {\n            ev.data.callback(this._getContext(ev.data.context));\n        }\n    },\n    /**\n     * Checks information about the page main object.\n     *\n     * @private\n     * @param {OdooEvent} ev\n     */\n    _onMainObjectRequest: function (ev) {\n        var repr = $('html').data('main-object');\n        var m = repr.match(/(.+)\\((\\d+),(.*)\\)/);\n        ev.data.callback({\n            model: m[1],\n            id: m[2] | 0,\n        });\n    },\n    /**\n     * Called when the root is notified that the public widgets have to be\n     * (re)started.\n     *\n     * @private\n     * @param {OdooEvent} ev\n     */\n    _onWidgetsStartRequest: function (ev) {\n        this._startWidgets(ev.data.$target, ev.data.options)\n            .then(ev.data.onSuccess)\n            .catch((e) => {\n                if (ev.data.onFailure) {\n                    ev.data.onFailure(e);\n                }\n                if (!(e instanceof RPCError)) {\n                    return Promise.reject(e);\n                }\n            });\n    },\n    /**\n     * Called when the root is notified that the public widgets have to be\n     * stopped.\n     *\n     * @private\n     * @param {OdooEvent} ev\n     */\n    _onWidgetsStopRequest: function (ev) {\n        this._stopWidgets(ev.data.$target);\n    },\n    /**\n     * @todo review\n     * @private\n     */\n    _onWebsiteFormSubmit: function (ev) {\n        var $buttons = $(ev.currentTarget).find('button[type=\"submit\"], a.a-submit').toArray();\n        $buttons.forEach((btn) => {\n            var $btn = $(btn);\n            $btn.prepend('<i class=\"fa fa-circle-o-notch fa-spin\"></i> ');\n            $btn.prop('disabled', true);\n        });\n    },\n    /**\n     * Called when the root is notified that the button should be\n     * disabled after the first click.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    _onDisableOnClick: function (ev) {\n        $(ev.currentTarget).addClass('disabled');\n    },\n    /**\n     * Library clears the wrong date format so just ignore error\n     *\n     * @private\n     * @param {Event} ev\n     */\n    _onDateTimePickerError: function (ev) {\n        return false;\n    },\n});\n\n/**\n * This widget is important, because the tour manager needs a root widget in\n * order to work. The root widget must be a service provider with the ajax\n * service, so that the tour manager can let the server know when tours have\n * been consumed.\n */\nexport async function createPublicRoot(RootWidget) {\n    await lazyloader.allScriptsLoaded;\n    await whenReady();\n    const env = makeEnv();\n    await startServices(env);\n    Component.env = env;\n    await env.services.public_component.mountComponents();\n    const publicRoot = new RootWidget(null, env);\n    const app = new App(MainComponentsContainer, {\n        getTemplate,\n        env,\n        dev: env.debug,\n        translateFn: _t,\n        translatableAttributes: [\"data-tooltip\"],\n    });\n    const locale = pyToJsLocale(lang) || browser.navigator.language;\n    Settings.defaultLocale = locale;\n    const [root] = await Promise.all([\n        app.mount(document.body),\n        publicRoot.attachTo(document.body),\n    ]);\n    odoo.__WOWL_DEBUG__ = { root };\n    return publicRoot;\n}\n\nexport default { PublicRoot, createPublicRoot };\n", "/** @odoo-module alias=root.widget */\nimport { PublicRoot, createPublicRoot } from \"./public_root\";\nimport lazyloader from \"@web/legacy/js/public/lazyloader\";\n\nconst prom = createPublicRoot(PublicRoot);\nlazyloader.registerPageReadinessDelay(prom);\nexport default prom;\n", "/**\n * Provides a way to start JS code for public contents.\n */\n\nimport { Component } from \"@odoo/owl\";\nimport Class from \"@web/legacy/js/core/class\";\nimport { loadBundle, loadCSS, loadJS } from '@web/core/assets';\nimport { SERVICES_METADATA } from \"@web/core/utils/hooks\";\nimport { renderToElement } from \"@web/core/utils/render\";\nimport { makeAsyncHandler, makeButtonHandler } from \"@web/legacy/js/public/minimal_dom\";\n\n/**\n * Mixin to structure objects' life-cycles following a parent-children\n * relationship. Each object can a have a parent and multiple children.\n * When an object is destroyed, all its children are destroyed too releasing\n * any resource they could have reserved before.\n *\n * @name ParentedMixin\n * @mixin\n */\nconst ParentedMixin = {\n    __parentedMixin: true,\n\n    init: function () {\n        this.__parentedDestroyed = false;\n        this.__parentedChildren = [];\n        this.__parentedParent = null;\n    },\n    /**\n     * Set the parent of the current object. When calling this method, the\n     * parent will also be informed and will return the current object\n     * when its getChildren() method is called. If the current object did\n     * already have a parent, it is unregistered before, which means the\n     * previous parent will not return the current object anymore when its\n     * getChildren() method is called.\n     */\n    setParent(parent) {\n        if (this.getParent()) {\n            if (this.getParent().__parentedMixin) {\n                const children = this.getParent().getChildren();\n                this.getParent().__parentedChildren = children.filter(\n                    (child) => child.$el !== this.$el\n                );\n            }\n        }\n        this.__parentedParent = parent;\n        if (parent && parent.__parentedMixin) {\n            parent.__parentedChildren.push(this);\n        }\n    },\n    /**\n     * Return the current parent of the object (or null).\n     */\n    getParent() {\n        return this.__parentedParent;\n    },\n    /**\n     * Return a list of the children of the current object.\n     */\n    getChildren() {\n        return [...this.__parentedChildren];\n    },\n    /**\n     * Returns true if destroy() was called on the current object.\n     */\n    isDestroyed() {\n        return this.__parentedDestroyed;\n    },\n    /**\n     * Releases any resource the instance could have reserved.\n     */\n    destroy() {\n        this.getChildren().forEach(function (child) {\n            child.destroy();\n        });\n        this.setParent(undefined);\n        this.__parentedDestroyed = true;\n    },\n};\n\nfunction OdooEvent(target, name, data) {\n    this.target = target;\n    this.name = name;\n    this.data = Object.create(null);\n    Object.assign(this.data, data);\n    this.stopped = false;\n}\nOdooEvent.prototype.stopPropagation = function () {\n    this.stopped = true;\n};\nOdooEvent.prototype.is_stopped = function () {\n    return this.stopped;\n};\n\n/**\n * Do not ever use it directly, use EventDispatcherMixin instead. This class\n * just handles the dispatching of events, it is not meant to be extended, nor\n * used directly. All integration with parenting and automatic unregistration of\n * events is done in EventDispatcherMixin.\n *\n * Copyright notice for the following Class and its uses:\n *\n * (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.\n * Backbone may be freely distributed under the MIT license.\n * For all details and documentation:\n * http://backbonejs.org\n *\n * See the debian/copyright file for the text of the MIT license.\n */\nclass Events {\n    on(events, callback, context) {\n        var ev;\n        events = events.split(/\\s+/);\n        var calls = this._callbacks || (this._callbacks = {});\n        while ((ev = events.shift())) {\n            var list = calls[ev] || (calls[ev] = {});\n            var tail = list.tail || (list.tail = list.next = {});\n            tail.callback = callback;\n            tail.context = context;\n            list.tail = tail.next = {};\n        }\n        return this;\n    }\n    off(events, callback, context) {\n        var ev, calls, node;\n        if (!events) {\n            delete this._callbacks;\n        } else if ((calls = this._callbacks)) {\n            events = events.split(/\\s+/);\n            while ((ev = events.shift())) {\n                node = calls[ev];\n                delete calls[ev];\n                if (!callback || !node) {\n                    continue;\n                }\n                while ((node = node.next) && node.next) {\n                    if (node.callback === callback\n                            && (!context || node.context === context)) {\n                        continue;\n                    }\n                    this.on(ev, node.callback, node.context);\n                }\n            }\n        }\n        return this;\n    }\n    callbackList() {\n        var lst = [];\n        for (const [eventName, el] of Object.entries(this._callbacks || {})) {\n            var node = el;\n            while ((node = node.next) && node.next) {\n                lst.push([eventName, node.callback, node.context]);\n            }\n        }\n        return lst;\n    }\n    trigger(events) {\n        var event, node, calls, tail, args, all, rest;\n        if (!(calls = this._callbacks)) {\n            return this;\n        }\n        all = calls.all;\n        (events = events.split(/\\s+/)).push(null);\n        // Save references to the current heads & tails.\n        while ((event = events.shift())) {\n            if (all) {\n                events.push({\n                    next: all.next,\n                    tail: all.tail,\n                    event: event\n                });\n            }\n            if (!(node = calls[event])) {\n                continue;\n            }\n            events.push({\n                next: node.next,\n                tail: node.tail\n            });\n        }\n        rest = Array.prototype.slice.call(arguments, 1);\n        while ((node = events.pop())) {\n            tail = node.tail;\n            args = node.event ? [node.event].concat(rest) : rest;\n            while ((node = node.next) !== tail) {\n                node.callback.apply(node.context || this, args);\n            }\n        }\n        return this;\n    }\n}\n\n/**\n * Mixin containing an event system. Events are also registered by specifying\n * the target object (the object which will receive the event when raised). Both\n * the event-emitting object and the target object store or reference to each\n * other. This is used to correctly remove all reference to the event handler\n * when any of the object is destroyed (when the destroy() method from\n * ParentedMixin is called). Removing those references is necessary to avoid\n * memory leak and phantom events (events which are raised and sent to a\n * previously destroyed object).\n *\n * @name EventDispatcherMixin\n * @mixin\n */\nconst EventDispatcherMixin = Object.assign({}, ParentedMixin, {\n    __eventDispatcherMixin: true,\n    \"custom_events\": {},\n\n    init() {\n        ParentedMixin.init.call(this);\n        this.__edispatcherEvents = new Events();\n        this.__edispatcherRegisteredEvents = [];\n        this._delegateCustomEvents();\n    },\n    /**\n     * Proxies a method of the object, in order to keep the right ``this`` on\n     * method invocations.\n     *\n     * This method is similar to ``Function.prototype.bind``, and\n     * even more so to ``jQuery.proxy`` with a fundamental difference: its\n     * resolution of the method being called is lazy, meaning it will use the\n     * method as it is when the proxy is called, not when the proxy is created.\n     *\n     * Other methods will fix the bound method to what it is when creating the\n     * binding/proxy, which is fine in most javascript code but problematic in\n     * Odoo where developers may want to replace existing callbacks with theirs.\n     *\n     * The semantics of this precisely replace closing over the method call.\n     *\n     * @param {String|Function} method function or name of the method to invoke\n     * @returns {Function} proxied method\n     */\n    proxy(method) {\n        var self = this;\n        return function () {\n            var fn = (typeof method === 'string') ? self[method] : method;\n            if (fn === void 0) {\n                throw new Error(\"Couldn't find method '\" + method + \"' in widget \" + self);\n            }\n            return fn.apply(self, arguments);\n        };\n    },\n    _delegateCustomEvents() {\n        if (Object.keys(this.custom_events || {}).length === 0) {\n            return;\n        }\n        for (var key in this.custom_events) {\n            if (!Object.prototype.hasOwnProperty.call(this.custom_events, key)) {\n                continue;\n            }\n\n            var method = this.proxy(this.custom_events[key]);\n            this.on(key, this, method);\n        }\n    },\n    on(events, dest, func) {\n        var self = this;\n        if (typeof func !== \"function\") {\n            throw new Error(\"Event handler must be a function.\");\n        }\n        events = events.split(/\\s+/);\n        events.forEach((eventName) => {\n            self.__edispatcherEvents.on(eventName, func, dest);\n            if (dest && dest.__eventDispatcherMixin) {\n                dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});\n            }\n        });\n        return this;\n    },\n    off(events, dest, func) {\n        var self = this;\n        events = events.split(/\\s+/);\n        events.forEach((eventName) => {\n            self.__edispatcherEvents.off(eventName, func, dest);\n            if (dest && dest.__eventDispatcherMixin) {\n                dest.__edispatcherRegisteredEvents = dest.__edispatcherRegisteredEvents.filter(el => {\n                    return !(el.name === eventName && el.func === func && el.source === self);\n                });\n            }\n        });\n        return this;\n    },\n    trigger() {\n        this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);\n        return this;\n    },\n    \"trigger_up\": function (name, info) {\n        var event = new OdooEvent(this, name, info);\n        //console.info('event: ', name, info);\n        this._trigger_up(event);\n        return event;\n    },\n    \"_trigger_up\": function (event) {\n        var parent;\n        this.__edispatcherEvents.trigger(event.name, event);\n        if (!event.is_stopped() && (parent = this.getParent())) {\n            parent._trigger_up(event);\n        }\n    },\n    destroy() {\n        var self = this;\n        this.__edispatcherRegisteredEvents.forEach((event) => {\n            event.source.__edispatcherEvents.off(event.name, event.func, self);\n        });\n        this.__edispatcherRegisteredEvents = [];\n        this.__edispatcherEvents.callbackList().forEach(\n            ((cal) => {\n                this.off(cal[0], cal[2], cal[1]);\n            }).bind(this)\n        );\n        this.__edispatcherEvents.off();\n        ParentedMixin.destroy.call(this);\n    },\n});\n\nfunction protectMethod(widget, fn) {\n    return function (...args) {\n        return new Promise((resolve, reject) => {\n            Promise.resolve(fn.call(this, ...args))\n                .then((result) => {\n                    if (!widget.isDestroyed()) {\n                        resolve(result);\n                    }\n                })\n                .catch((reason) => {\n                    if (!widget.isDestroyed()) {\n                        reject(reason);\n                    }\n                });\n        });\n    };\n}\n\nconst ServicesMixin = {\n    bindService: function (serviceName) {\n        const { services } = Component.env;\n        const service = services[serviceName];\n        if (!service) {\n            throw new Error(`Service ${serviceName} is not available`);\n        }\n        if (serviceName in SERVICES_METADATA) {\n            if (service instanceof Function) {\n                return protectMethod(this, service);\n            } else {\n                const methods = SERVICES_METADATA[serviceName];\n                const result = Object.create(service);\n                for (const method of methods) {\n                    result[method] = protectMethod(this, service[method]);\n                }\n                return result;\n            }\n        }\n        return service;\n    },\n    /**\n     * @param  {string} service\n     * @param  {string} method\n     * @return {any} result of the service called\n     */\n    call: function (service, method) {\n        var args = Array.prototype.slice.call(arguments, 2);\n        var result;\n        this.trigger_up('call_service', {\n            service: service,\n            method: method,\n            args: args,\n            callback: function (r) {\n                result = r;\n            },\n        });\n        return result;\n    },\n};\n\n/**\n * Base class for all visual components. Provides a lot of functions helpful\n * for the management of a part of the DOM.\n *\n * Widget handles:\n *\n * - Rendering with QWeb.\n * - Life-cycle management and parenting (when a parent is destroyed, all its\n *   children are destroyed too).\n * - Insertion in DOM.\n *\n * **Guide to create implementations of the Widget class**\n *\n * Here is a sample child class::\n *\n *     var MyWidget = Widget.extend({\n *         // the name of the QWeb template to use for rendering\n *         template: \"MyQWebTemplate\",\n *\n *         init: function (parent) {\n *             this._super(parent);\n *             // stuff that you want to init before the rendering\n *         },\n *         willStart: function () {\n *             // async work that need to be done before the widget is ready\n *             // this method should return a promise\n *         },\n *         start: function() {\n *             // stuff you want to make after the rendering, `this.$el` holds a correct value\n *             this.$(\".my_button\").click(/* an example of event binding * /);\n *\n *             // if you have some asynchronous operations, it's a good idea to return\n *             // a promise in start(). Note that this is quite rare, and if you\n *             // need to fetch some data, this should probably be done in the\n *             // willStart method\n *             var promise = this._rpc(...);\n *             return promise;\n *         }\n *     });\n *\n * Now this class can simply be used with the following syntax::\n *\n *     var myWidget = new MyWidget(this);\n *     myWidget.appendTo($(\".some-div\"));\n *\n * With these two lines, the MyWidget instance was initialized, rendered,\n * inserted into the DOM inside the ``.some-div`` div and its events were\n * bound.\n *\n * This class can also be initialized and started on an existing DOM element\n * using the `selector` property. See below for more documentation.\n *\n * And of course, when you don't need that widget anymore, just do::\n *\n *     myWidget.destroy();\n *\n * That will kill the widget in a clean way and erase its content from the dom.\n *\n * This class also provides a way for executing code once a website DOM element\n * is loaded in the dom.\n * @see PublicWidget.selector\n */\nexport const PublicWidget = Class.extend(EventDispatcherMixin, ServicesMixin, {\n    // Backbone-ish API\n    tagName: 'div',\n    id: null,\n    className: null,\n    attributes: {},\n    /**\n     * The name of the QWeb template that will be used for rendering. Must be\n     * redefined in subclasses or the default render() method can not be used.\n     *\n     * @type {null|string}\n     */\n    template: null,\n    /**\n     * List of paths to css files that need to be loaded before the widget can\n     * be rendered. This will not induce loading anything that has already been\n     * loaded.\n     *\n     * @type {null|string[]}\n     */\n    cssLibs: null,\n    /**\n     * List of paths to js files that need to be loaded before the widget can\n     * be rendered. This will not induce loading anything that has already been\n     * loaded.\n     *\n     * @type {null|string[]}\n     */\n    jsLibs: null,\n    /**\n     * List of xmlID that need to be loaded before the widget can be rendered.\n     * The content css (link file or style tag) and js (file or inline) of the\n     * assets are loaded.\n     * This will not induce loading anything that has already been\n     * loaded.\n     *\n     * @type {null|string[]}\n     */\n    assetLibs: null,\n    /**\n     * The selector attribute, if defined, allows to automatically create an\n     * instance of this widget on page load for each DOM element according to\n     * this selector. The `PublicWidget.$el / el` element will then be that\n     * particular DOM element. This should be the main way of instantiating\n     * `PublicWidget` elements.\n     *\n     * The value can either be a string in which case it is considered as a\n     * `querySelectorAll` selector to match, or a function expecting to return\n     * all DOM elements to consider, which are inside the element received as\n     * parameter of the function (or that element itself).\n     *\n     * @see selectorHas\n     *\n     * @todo do not make this part of the Widget but rather an info to give when\n     * registering the widget.\n     *\n     * @type {string|function|false}\n     */\n    selector: false,\n    /**\n     * The `selectorHas` attribute, if defined, allows to filter elements found\n     * through the `selector` attribute by only considering those which contain\n     * at least an element which matches this `selectorHas` selector.\n     *\n     * Note that this is the equivalent of setting up a `selector` using the\n     * `:has` pseudo-selector but that pseudo-selector is known to not be fully\n     * supported in all browsers. To prevent useless crashes, using this\n     * `selectorHas` attribute should be preferred.\n     *\n     * @type {string|false}\n     */\n    selectorHas: false,\n    /**\n     * Extension of @see Widget.events\n     *\n     * A description of the event handlers to bind/delegate once the widget\n     * has been rendered::\n     *\n     *   'click .hello .world': 'async _onHelloWorldClick',\n     *     _^_      _^_           _^_        _^_\n     *      |        |             |          |\n     *      |  (Optional) jQuery   |  Handler method name\n     *      |  delegate selector   |\n     *      |                      |_ (Optional) space separated options\n     *      |                          * async: use the automatic system\n     *      |_ Event name with           making handlers promise-ready (see\n     *         potential jQuery          makeButtonHandler, makeAsyncHandler)\n     *         namespaces\n     *\n     * Note: the values may be replaced by a function declaration. This is\n     * however a deprecated behavior.\n     *\n     * @type {Object}\n     */\n    events: {},\n\n    /**\n     * @constructor\n     * @param {Object} parent\n     * @param {Object} [options]\n     */\n    init: function (parent, options) {\n        EventDispatcherMixin.init.call(this);\n        this.setParent(parent);\n        this.options = options || {};\n    },\n    /**\n     * Method called between @see init and @see start. Performs asynchronous\n     * calls required by the rendering and the start method.\n     *\n     * This method should return a Promise which is resolved when start can be\n     * executed.\n     *\n     * @returns {Promise}\n     */\n    willStart: function () {\n        var proms = [];\n        if (this.jsLibs || this.cssLibs || this.assetLibs) {\n            var assetsPromise = Promise.all([\n                ...(this.cssLibs || []).map(loadCSS),\n                ...(this.jsLibs || []).map(loadJS),\n            ]);\n            for (const bundleName of this.assetLibs || []) {\n                if (typeof bundleName === \"string\") {\n                    assetsPromise = assetsPromise.then(() => {\n                        return loadBundle(bundleName);\n                    });\n                } else {\n                    assetsPromise = assetsPromise.then(() => {\n                        return Promise.all([...bundleName.map(loadBundle)]);\n                    });\n                }\n            }\n            proms.push(assetsPromise);\n        }\n        return Promise.all(proms);\n    },\n    /**\n     * Method called after rendering. Mostly used to bind actions, perform\n     * asynchronous calls, etc...\n     *\n     * By convention, this method should return an object that can be passed to\n     * Promise.resolve() to inform the caller when this widget has been initialized.\n     *\n     * Note that, for historic reasons, many widgets still do work in the start\n     * method that would be more suited to the willStart method.\n     *\n     * @returns {Promise}\n     */\n    start: function () {\n        return Promise.resolve();\n    },\n    /**\n     * Destroys the widget and basically restores the target to the state it\n     * was before the start method was called (unlike standard widget, the\n     * associated $el DOM is not removed, if this was instantiated thanks to the\n     * selector property).\n     */\n    destroy: function () {\n        EventDispatcherMixin.destroy.call(this);\n        if (this.$el) {\n            this._undelegateEvents();\n\n            // If not done with a selector (attached to existing DOM), then\n            // remove the elements added to the DOM.\n            if (!this.selector) {\n                this.$el.remove();\n            }\n        }\n    },\n\n    //--------------------------------------------------------------------------\n    // Public\n    //--------------------------------------------------------------------------\n\n    /**\n     * Renders the current widget and appends it to the given jQuery object.\n     *\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    appendTo: function (target) {\n        var self = this;\n        return this._widgetRenderAndInsert(function (t) {\n            self.$el.appendTo(t);\n        }, target);\n    },\n    /**\n     * Attach the current widget to a dom element\n     *\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    attachTo: function (target) {\n        var self = this;\n        this.setElement(target.$el || target);\n        return this.willStart().then(function () {\n            if (self.__parentedDestroyed) {\n                return;\n            }\n            return self.start();\n        });\n    },\n    /**\n     * Renders the current widget and inserts it after to the given jQuery\n     * object.\n     *\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    insertAfter: function (target) {\n        var self = this;\n        return this._widgetRenderAndInsert(function (t) {\n            self.$el.insertAfter(t);\n        }, target);\n    },\n    /**\n     * Renders the current widget and inserts it before to the given jQuery\n     * object.\n     *\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    insertBefore: function (target) {\n        var self = this;\n        return this._widgetRenderAndInsert(function (t) {\n            self.$el.insertBefore(t);\n        }, target);\n    },\n    /**\n     * Renders the current widget and prepends it to the given jQuery object.\n     *\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    prependTo: function (target) {\n        var self = this;\n        return this._widgetRenderAndInsert(function (t) {\n            self.$el.prependTo(t);\n        }, target);\n    },\n    /**\n     * Renders the element. The default implementation renders the widget using\n     * QWeb, `this.template` must be defined. The context given to QWeb contains\n     * the \"widget\" key that references `this`.\n     */\n    renderElement: function () {\n        var $el;\n        if (this.template) {\n            $el = $(renderToElement(this.template, {widget: this}));\n        } else {\n            $el = this._makeDescriptive();\n        }\n        this._replaceElement($el);\n    },\n    /**\n     * Renders the current widget and replaces the given jQuery object.\n     *\n     * @param target A jQuery object or a Widget instance.\n     * @returns {Promise}\n     */\n    replace: function (target) {\n        return this._widgetRenderAndInsert((t) => {\n            this.$el.replaceAll(t);\n        }, target);\n    },\n    /**\n     * Re-sets the widget's root element (el/$el/$el).\n     *\n     * Includes:\n     *\n     * * re-delegating events\n     * * re-binding sub-elements\n     * * if the widget already had a root element, replacing the pre-existing\n     *   element in the DOM\n     *\n     * @param {HTMLElement | jQuery} element new root element for the widget\n     * @return {Widget} this\n     */\n    setElement: function (element) {\n        if (this.$el) {\n            this._undelegateEvents();\n        }\n\n        this.$el = (element instanceof $) ? element : $(element);\n        this.el = this.$el[0];\n\n        this._delegateEvents();\n\n        if (this.selector) {\n            this.$target = this.$el;\n            this.target = this.el;\n        }\n\n        return this;\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * Helper method, for ``this.$el.find(selector)``\n     *\n     * @private\n     * @param {string} selector CSS selector, rooted in $el\n     * @returns {jQuery} selector match\n     */\n    $: function (selector) {\n        if (selector === undefined) {\n            return this.$el;\n        }\n        return this.$el.find(selector);\n    },\n    /**\n     * @see this.events\n     * @override\n     */\n    _delegateEvents: function () {\n        var self = this;\n\n        const _delegateEvent = (method, key) => {\n            var match = /^(\\S+)(\\s+(.*))?$/.exec(key);\n            var event = match[1];\n            var selector = match[3];\n\n            event += '.widget_events';\n            if (!selector) {\n                self.$el.on(event, method);\n            } else {\n                self.$el.on(event, selector, method);\n            }\n        };\n        Object.entries(this.events || {}).forEach(([event, method]) => {\n            // If the method is a function, use the default Widget system\n            if (typeof method !== 'string') {\n                _delegateEvent(self.proxy(method), event);\n                return;\n            }\n            // If the method is only a function name without options, use the\n            // default Widget system\n            var methodOptions = method.split(' ');\n            if (methodOptions.length <= 1) {\n                _delegateEvent(self.proxy(method), event);\n                return;\n            }\n            // If the method has no meaningful options, use the default Widget\n            // system\n            var isAsync = methodOptions.includes('async');\n            if (!isAsync) {\n                _delegateEvent(self.proxy(method), event);\n                return;\n            }\n\n            method = self.proxy(methodOptions[methodOptions.length - 1]);\n            if (String(event).startsWith(\"click\")) {\n                // Protect click handler to be called multiple times by\n                // mistake by the user and add a visual disabling effect\n                // for buttons.\n                method = makeButtonHandler(method);\n            } else {\n                // Protect all handlers to be recalled while the previous\n                // async handler call is not finished.\n                method = makeAsyncHandler(method);\n            }\n            _delegateEvent(method, event);\n        });\n    },\n    /**\n     * @private\n     * @param {boolean} [extra=false]\n     * @param {Object} [extraContext]\n     * @returns {Object}\n     */\n    _getContext: function (extra, extraContext) {\n        var context;\n        this.trigger_up('context_get', {\n            extra: extra || false,\n            context: extraContext,\n            callback: function (ctx) {\n                context = ctx;\n            },\n        });\n        return context;\n    },\n    /**\n     * Makes a potential root element from the declarative builder of the\n     * widget\n     *\n     * @private\n     * @return {jQuery}\n     */\n    _makeDescriptive: function () {\n        var attrs = Object.assign({}, this.attributes || {});\n        if (this.id) {\n            attrs.id = this.id;\n        }\n        if (this.className) {\n            attrs['class'] = this.className;\n        }\n        var $el = $(document.createElement(this.tagName));\n        if (Object.keys(attrs || {}).length > 0) {\n            $el.attr(attrs);\n        }\n        return $el;\n    },\n    /**\n     * Re-sets the widget's root element and replaces the old root element\n     * (if any) by the new one in the DOM.\n     *\n     * @private\n     * @param {HTMLElement | jQuery} $el\n     * @returns {Widget} this instance, so it can be chained\n     */\n    _replaceElement: function ($el) {\n        var $oldel = this.$el;\n        this.setElement($el);\n        if ($oldel && !$oldel.is(this.$el)) {\n            if ($oldel.length > 1) {\n                $oldel.wrapAll('<div/>');\n                $oldel.parent().replaceWith(this.$el);\n            } else {\n                $oldel.replaceWith(this.$el);\n            }\n        }\n        return this;\n    },\n    /**\n     * Remove all handlers registered on this.$el\n     *\n     * @private\n     */\n    _undelegateEvents: function () {\n        this.$el.off('.widget_events');\n    },\n    /**\n     * Render the widget.  This is a private method, and should really never be\n     * called by anyone (except this widget).  It assumes that the widget was\n     * not willStarted yet.\n     *\n     * @private\n     * @param {function: jQuery -> any} insertion\n     * @param {jQuery} target\n     * @returns {Promise}\n     */\n    _widgetRenderAndInsert: function (insertion, target) {\n        var self = this;\n        return this.willStart().then(function () {\n            if (self.__parentedDestroyed) {\n                return;\n            }\n            self.renderElement();\n            insertion(target);\n            return self.start();\n        });\n    },\n});\n\n//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n\n/**\n * The registry object contains the list of widgets that should be instantiated\n * thanks to their selector property if any.\n */\nvar registry = {};\n\nexport default {\n    Widget: PublicWidget,\n    registry: registry,\n\n    ParentedMixin: ParentedMixin,\n    EventDispatcherMixin: EventDispatcherMixin,\n    ServicesMixin: ServicesMixin,\n};\n", "import publicWidget from '@web/legacy/js/public/public_widget';\nimport { addLoadingEffect } from '@web/core/utils/ui';\n\npublicWidget.registry.login = publicWidget.Widget.extend({\n    selector: '.oe_login_form',\n    events: {\n        'submit': '_onSubmit',\n    },\n\n    //-------------------------------------------------------------------------\n    // Handlers\n    //-------------------------------------------------------------------------\n\n    /**\n     * Prevents the user from crazy clicking:\n     * Gives the button a loading effect if preventDefault was not already\n     * called and modifies the preventDefault function of the event so that the\n     * loading effect is removed if preventDefault() is called in a following\n     * customization.\n     *\n     * @private\n     * @param {Event} ev\n     */\n    _onSubmit(ev) {\n        if (!ev.defaultPrevented) {\n            const btnEl = ev.currentTarget.querySelector('button[type=\"submit\"]');\n            const removeLoadingEffect = addLoadingEffect(btnEl);\n            const oldPreventDefault = ev.preventDefault.bind(ev);\n            ev.preventDefault = () => {\n                removeLoadingEffect();\n                oldPreventDefault();\n            };\n        }\n    },\n});\n", "/** @odoo-module */\n\nimport { registry } from \"@web/core/registry\";\n\nexport const busParametersService = {\n    start() {\n        return {\n            serverURL: window.origin,\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"bus.parameters\", busParametersService);\n", "/** @odoo-module **/\n\nimport { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\nimport { user } from \"@web/core/user\";\nimport { session } from \"@web/session\";\n\nexport const AWAY_DELAY = 30 * 60 * 1000; // 30 minutes\nexport const FIRST_UPDATE_DELAY = 500;\nexport const UPDATE_BUS_PRESENCE_DELAY = 60000;\n\n/**\n * This service keeps the user's presence up to date with the server. When the\n * connection to the server is established, the user's presence is updated. If\n * another device or browser updates the user's presence, the presence is sent to\n * the server if relevant (e.g., another device is away or offline, but this one\n * is online). To receive updates through the bus, subscribe to presence channels\n * (e.g., subscribe to `odoo-presence-res.partner_3` to receive updates about\n * this partner).\n */\nexport const imStatusService = {\n    dependencies: [\"bus_service\", \"presence\"],\n\n    start(env, { bus_service, presence }) {\n        let lastSentInactivity;\n        let becomeAwayTimeout;\n\n        const updateBusPresence = () => {\n            lastSentInactivity = presence.getInactivityPeriod();\n            startAwayTimeout();\n            bus_service.send(\"update_presence\", {\n                inactivity_period: lastSentInactivity,\n                im_status_ids_by_model: {},\n            });\n        };\n        this.updateBusPresence = updateBusPresence;\n\n        const startAwayTimeout = () => {\n            clearTimeout(becomeAwayTimeout);\n            const awayTime = AWAY_DELAY - presence.getInactivityPeriod();\n            if (awayTime > 0) {\n                becomeAwayTimeout = browser.setTimeout(() => updateBusPresence(), awayTime);\n            }\n        };\n        bus_service.addEventListener(\"connect\", () => updateBusPresence(), { once: true });\n        bus_service.subscribe(\"bus.bus/im_status_updated\", async ({ partner_id, im_status }) => {\n            if (session.is_public || !partner_id || partner_id !== user.partnerId) {\n                return;\n            }\n            const isOnline = presence.getInactivityPeriod() < AWAY_DELAY;\n            if (im_status === \"offline\" || (im_status === \"away\" && isOnline)) {\n                this.updateBusPresence();\n            }\n        });\n        presence.bus.addEventListener(\"presence\", () => {\n            if (lastSentInactivity >= AWAY_DELAY) {\n                this.updateBusPresence();\n            }\n            startAwayTimeout();\n        });\n    },\n};\n\nregistry.category(\"services\").add(\"im_status\", imStatusService);\n", "/** @odoo-module */\n\nimport { browser } from \"@web/core/browser/browser\";\n\n/**\n * Returns a function, that, when invoked, will only be triggered at most once\n * during a given window of time. Normally, the throttled function will run\n * as much as it can, without ever going more than once per `wait` duration;\n * but if you'd like to disable the execution on the leading edge, pass\n * `{leading: false}`. To disable execution on the trailing edge, ditto.\n *\n * credit to `underscore.js`\n */\nfunction throttle(func, wait, options) {\n    let timeout, context, args, result;\n    let previous = 0;\n    if (!options) {\n        options = {};\n    }\n\n    const later = function () {\n        previous = options.leading === false ? 0 : luxon.DateTime.now().ts;\n        timeout = null;\n        result = func.apply(context, args);\n        if (!timeout) {\n            context = args = null;\n        }\n    };\n\n    const throttled = function () {\n        const _now = luxon.DateTime.now().ts;\n        if (!previous && options.leading === false) {\n            previous = _now;\n        }\n        const remaining = wait - (_now - previous);\n        context = this;\n        args = arguments;\n        if (remaining <= 0 || remaining > wait) {\n            if (timeout) {\n                browser.clearTimeout(timeout);\n                timeout = null;\n            }\n            previous = _now;\n            result = func.apply(context, args);\n            if (!timeout) {\n                context = args = null;\n            }\n        } else if (!timeout && options.trailing !== false) {\n            timeout = browser.setTimeout(later, remaining);\n        }\n        return result;\n    };\n\n    throttled.cancel = function () {\n        browser.clearTimeout(timeout);\n        previous = 0;\n        timeout = context = args = null;\n    };\n\n    return throttled;\n}\n\nexport const timings = {\n    throttle,\n};\n", "/** @odoo-module **/\n\nimport { registry } from \"@web/core/registry\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { EventBus } from \"@odoo/owl\";\n\nlet multiTabId = 0;\n/**\n * This service uses a Master/Slaves with Leader Election architecture in\n * order to keep track of the main tab. Tabs are synchronized thanks to the\n * localStorage.\n *\n * localStorage used keys are:\n * - {LOCAL_STORAGE_PREFIX}.{sanitizedOrigin}.lastPresenceByTab:\n *   mapping of tab ids to their last recorded presence.\n * - {LOCAL_STORAGE_PREFIX}.{sanitizedOrigin}.main : id of the current\n *   main tab.\n * - {LOCAL_STORAGE_PREFIX}.{sanitizedOrigin}.heartbeat : last main tab\n *   heartbeat time.\n *\n * trigger:\n * - become_main_tab : when this tab became the main.\n * - no_longer_main_tab : when this tab is no longer the main.\n * - shared_value_updated: when one of the shared values changes.\n */\nexport const multiTabService = {\n    start() {\n        const bus = new EventBus();\n\n        // CONSTANTS\n        const TAB_HEARTBEAT_PERIOD = 10000; // 10 seconds\n        const MAIN_TAB_HEARTBEAT_PERIOD = 1500; // 1.5 seconds\n        const HEARTBEAT_OUT_OF_DATE_PERIOD = 5000; // 5 seconds\n        const HEARTBEAT_KILL_OLD_PERIOD = 15000; // 15 seconds\n        // Keys that should not trigger the `shared_value_updated` event.\n        const PRIVATE_LOCAL_STORAGE_KEYS = [\"main\", \"heartbeat\"];\n\n        // PROPERTIES\n        let _isOnMainTab = false;\n        let lastHeartbeat = 0;\n        let heartbeatTimeout;\n        const sanitizedOrigin = location.origin.replace(/:\\/{0,2}/g, \"_\");\n        const localStoragePrefix = `${this.name}.${sanitizedOrigin}.`;\n        const now = new Date().getTime();\n        const tabId = `${this.name}${multiTabId++}:${now}`;\n\n        function generateLocalStorageKey(baseKey) {\n            return localStoragePrefix + baseKey;\n        }\n\n        function getItemFromStorage(key, defaultValue) {\n            const item = browser.localStorage.getItem(generateLocalStorageKey(key));\n            try {\n                return item ? JSON.parse(item) : defaultValue;\n            } catch {\n                return item;\n            }\n        }\n\n        function setItemInStorage(key, value) {\n            browser.localStorage.setItem(generateLocalStorageKey(key), JSON.stringify(value));\n        }\n\n        function startElection() {\n            if (_isOnMainTab) {\n                return;\n            }\n            // Check who's next.\n            const now = new Date().getTime();\n            const lastPresenceByTab = getItemFromStorage(\"lastPresenceByTab\", {});\n            const heartbeatKillOld = now - HEARTBEAT_KILL_OLD_PERIOD;\n            let newMain;\n            for (const [tab, lastPresence] of Object.entries(lastPresenceByTab)) {\n                // Check for dead tabs.\n                if (lastPresence < heartbeatKillOld) {\n                    continue;\n                }\n                newMain = tab;\n                break;\n            }\n            if (newMain === tabId) {\n                // We're next in queue. Electing as main.\n                lastHeartbeat = now;\n                setItemInStorage(\"heartbeat\", lastHeartbeat);\n                setItemInStorage(\"main\", true);\n                _isOnMainTab = true;\n                bus.trigger(\"become_main_tab\");\n                // Removing main peer from queue.\n                delete lastPresenceByTab[newMain];\n                setItemInStorage(\"lastPresenceByTab\", lastPresenceByTab);\n            }\n        }\n\n        function heartbeat() {\n            const now = new Date().getTime();\n            let heartbeatValue = getItemFromStorage(\"heartbeat\", 0);\n            const lastPresenceByTab = getItemFromStorage(\"lastPresenceByTab\", {});\n            if (heartbeatValue + HEARTBEAT_OUT_OF_DATE_PERIOD < now) {\n                // Heartbeat is out of date. Electing new main.\n                startElection();\n                heartbeatValue = getItemFromStorage(\"heartbeat\", 0);\n            }\n            if (_isOnMainTab) {\n                // Walk through all tabs and kill old ones.\n                const cleanedTabs = {};\n                for (const [tabId, lastPresence] of Object.entries(lastPresenceByTab)) {\n                    if (lastPresence + HEARTBEAT_KILL_OLD_PERIOD > now) {\n                        cleanedTabs[tabId] = lastPresence;\n                    }\n                }\n                if (heartbeatValue !== lastHeartbeat) {\n                    // Someone else is also main...\n                    // It should not happen, except in some race condition situation.\n                    _isOnMainTab = false;\n                    lastHeartbeat = 0;\n                    lastPresenceByTab[tabId] = now;\n                    setItemInStorage(\"lastPresenceByTab\", lastPresenceByTab);\n                    bus.trigger(\"no_longer_main_tab\");\n                } else {\n                    lastHeartbeat = now;\n                    setItemInStorage(\"heartbeat\", now);\n                    setItemInStorage(\"lastPresenceByTab\", cleanedTabs);\n                }\n            } else {\n                // Update own heartbeat.\n                lastPresenceByTab[tabId] = now;\n                setItemInStorage(\"lastPresenceByTab\", lastPresenceByTab);\n            }\n            const hbPeriod = _isOnMainTab ? MAIN_TAB_HEARTBEAT_PERIOD : TAB_HEARTBEAT_PERIOD;\n            heartbeatTimeout = browser.setTimeout(heartbeat, hbPeriod);\n        }\n\n        function onStorage({ key, newValue }) {\n            if (key === generateLocalStorageKey(\"main\") && !newValue) {\n                // Main was unloaded.\n                startElection();\n            }\n            if (PRIVATE_LOCAL_STORAGE_KEYS.includes(key)) {\n                return;\n            }\n            if (key && key.includes(localStoragePrefix)) {\n                // Only trigger the shared_value_updated event if the key is\n                // related to this service/origin.\n                const baseKey = key.replace(localStoragePrefix, \"\");\n                bus.trigger(\"shared_value_updated\", { key: baseKey, newValue });\n            }\n        }\n\n        /**\n         * Unregister this tab from the multi-tab service. It will no longer\n         * be able to become the main tab.\n         */\n        function unregister() {\n            clearTimeout(heartbeatTimeout);\n            const lastPresenceByTab = getItemFromStorage(\"lastPresenceByTab\", {});\n            delete lastPresenceByTab[tabId];\n            setItemInStorage(\"lastPresenceByTab\", lastPresenceByTab);\n\n            // Unload main.\n            if (_isOnMainTab) {\n                _isOnMainTab = false;\n                bus.trigger(\"no_longer_main_tab\");\n                browser.localStorage.removeItem(generateLocalStorageKey(\"main\"));\n            }\n        }\n\n        browser.addEventListener(\"pagehide\", unregister);\n        browser.addEventListener(\"storage\", onStorage);\n\n        // REGISTER THIS TAB\n        const lastPresenceByTab = getItemFromStorage(\"lastPresenceByTab\", {});\n        lastPresenceByTab[tabId] = now;\n        setItemInStorage(\"lastPresenceByTab\", lastPresenceByTab);\n\n        if (!getItemFromStorage(\"main\")) {\n            startElection();\n        }\n        heartbeat();\n\n        return {\n            bus,\n            get currentTabId() {\n                return tabId;\n            },\n            /**\n             * Determine whether or not this tab is the main one.\n             *\n             * @returns {boolean}\n             */\n            isOnMainTab() {\n                return _isOnMainTab;\n            },\n            /**\n             * Get value shared between all the tabs.\n             *\n             * @param {string} key\n             * @param {any} defaultValue Value to be returned if this\n             * key does not exist.\n             */\n            getSharedValue(key, defaultValue) {\n                return getItemFromStorage(key, defaultValue);\n            },\n            /**\n             * Set value shared between all the tabs.\n             *\n             * @param {string} key\n             * @param {any} value\n             */\n            setSharedValue(key, value) {\n                if (value === undefined) {\n                    return this.removeSharedValue(key);\n                }\n                setItemInStorage(key, value);\n            },\n            /**\n             * Remove value shared between all the tabs.\n             *\n             * @param {string} key\n             */\n            removeSharedValue(key) {\n                browser.localStorage.removeItem(generateLocalStorageKey(key));\n            },\n            /**\n             * Unregister this tab from the multi-tab service. It will no longer\n             * be able to become the main tab.\n             */\n            unregister: unregister,\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"multi_tab\", multiTabService);\n", "import { browser } from \"@web/core/browser/browser\";\nimport { deserializeDateTime } from \"@web/core/l10n/dates\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { registry } from \"@web/core/registry\";\n\nconst { DateTime } = luxon;\nexport class OutdatedPageWatcherService {\n    constructor(env, services) {\n        this.setup(env, services);\n    }\n\n    /**\n     * @param {import(\"@web/env\").OdooEnv}\n     * @param {Partial<import(\"services\").Services>} services\n     */\n    setup(env, { bus_service, multi_tab, notification }) {\n        this.notification = notification;\n        const vacuumInfo = multi_tab.getSharedValue(\"bus.autovacuum_info\");\n        this.lastAutovacuumDt = vacuumInfo ? deserializeDateTime(vacuumInfo.lastcall) : null;\n        this.nextAutovacuumDt = vacuumInfo ? deserializeDateTime(vacuumInfo.nextcall) : null;\n        this.lastDisconnectDt = null;\n        this.closeNotificationFn;\n        bus_service.addEventListener(\"disconnect\", () => (this.lastDisconnectDt = DateTime.now()));\n        bus_service.addEventListener(\"reconnect\", async () => {\n            if (!multi_tab.isOnMainTab() || !this.lastDisconnectDt) {\n                return;\n            }\n            if (!this.lastAutovacuumDt || DateTime.now() >= this.nextAutovacuumDt) {\n                const { lastcall, nextcall } = await rpc(\n                    \"/bus/get_autovacuum_info\",\n                    {},\n                    { silent: true }\n                );\n                this.lastAutovacuumDt = deserializeDateTime(lastcall);\n                this.nextAutovacuumDt = deserializeDateTime(nextcall);\n                multi_tab.setSharedValue(\"bus.autovacuum_info\", { lastcall, nextcall });\n            }\n            if (this.lastDisconnectDt <= this.lastAutovacuumDt) {\n                this.showOutdatedPageNotification();\n            }\n        });\n        multi_tab.bus.addEventListener(\"shared_value_updated\", ({ detail: { key, newValue } }) => {\n            if (key !== \"bus.autovacuum_info\") {\n                return;\n            }\n            const infos = JSON.parse(newValue);\n            this.lastAutovacuumDt = deserializeDateTime(infos.lastcall);\n            this.nextAutovacuumDt = deserializeDateTime(infos.nextcall);\n            if (this.lastDisconnectDt <= this.lastAutovacuumDt) {\n                this.showOutdatedPageNotification();\n            }\n        });\n    }\n\n    showOutdatedPageNotification() {\n        this.closeNotificationFn?.();\n        this.closeNotificationFn = this.notification.add(\n            _t(\"Save your work and refresh to get the latest updates and avoid potential issues.\"),\n            {\n                title: _t(\"The page is out of date\"),\n                type: \"warning\",\n                sticky: true,\n                buttons: [\n                    {\n                        name: _t(\"Refresh\"),\n                        primary: true,\n                        onClick: () => browser.location.reload(),\n                    },\n                ],\n            }\n        );\n    }\n}\n\nexport const outdatedPageWatcherService = {\n    dependencies: [\"bus_service\", \"multi_tab\", \"notification\"],\n    start(env, services) {\n        return new OutdatedPageWatcherService(env, services);\n    },\n};\n\nregistry.category(\"services\").add(\"bus.outdated_page_watcher\", outdatedPageWatcherService);\n", "import { WORKER_STATE } from \"@bus/workers/websocket_worker\";\nimport { reactive } from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\n\nexport const CONNECTION_LOST_WARNING_DELAY = 15000;\nexport const CONNECTION_STATUS = Object.freeze({\n    CONNECTED: 0,\n    CONNECTION_LOST: 1,\n    CONNECTION_LOST_LONG: 2,\n});\n\n/**\n * Detect lost connections to the bus. A connection is considered as lost if it\n * couldn't be established after a reconnect attempt.\n */\nexport class BusMonitoringService {\n    connectionStatus = CONNECTION_STATUS.CONNECTED;\n    timeout;\n\n    constructor(env, services) {\n        const reactiveThis = reactive(this);\n        reactiveThis.setup(env, services);\n        return reactiveThis;\n    }\n\n    /**\n     * @param {import(\"@web/env\").OdooEnv} env\n     * @param {Partial<import(\"services\").Services>} services\n     */\n    setup(env, { bus_service }) {\n        bus_service.addEventListener(\"worker_state_updated\", ({ detail }) =>\n            this.workerStateOnChange(detail)\n        );\n        browser.addEventListener(\"offline\", () => (this.isReconnecting = false));\n    }\n\n    /**\n     * Handle state changes for the WebSocket worker.\n     *\n     * @param {WORKER_STATE[keyof WORKER_STATE]} state\n     */\n    workerStateOnChange(state) {\n        if (!navigator.onLine) {\n            return;\n        }\n        switch (state) {\n            case WORKER_STATE.CONNECTING: {\n                this.isReconnecting = true;\n                break;\n            }\n            case WORKER_STATE.CONNECTED: {\n                this.isReconnecting = false;\n                this.connectionStatus = CONNECTION_STATUS.CONNECTED;\n                if (this.timeout) {\n                    clearTimeout(this.timeout);\n                    this.timeout = undefined;\n                }\n                break;\n            }\n            case WORKER_STATE.DISCONNECTED: {\n                if (this.isReconnecting) {\n                    this.isReconnecting = false;\n                }\n                if (!this.timeout) {\n                    this.connectionStatus = CONNECTION_STATUS.CONNECTION_LOST;\n                    this.timeout = browser.setTimeout(() => {\n                        this.connectionStatus = CONNECTION_STATUS.CONNECTION_LOST_LONG;\n                    }, CONNECTION_LOST_WARNING_DELAY);\n                }\n                break;\n            }\n        }\n    }\n}\n\nexport const busMonitoringservice = {\n    dependencies: [\"bus_service\"],\n    start(env, services) {\n        return new BusMonitoringService(env, services);\n    },\n};\n\nregistry.category(\"services\").add(\"bus.monitoring_service\", busMonitoringservice);\n", "import { browser } from \"@web/core/browser/browser\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { Deferred } from \"@web/core/utils/concurrency\";\nimport { registry } from \"@web/core/registry\";\nimport { session } from \"@web/session\";\nimport { isIosApp } from \"@web/core/browser/feature_detection\";\nimport { EventBus } from \"@odoo/owl\";\nimport { user } from \"@web/core/user\";\n\n// List of worker events that should not be broadcasted.\nconst INTERNAL_EVENTS = new Set([\"initialized\", \"outdated\", \"log_debug\", \"notification\"]);\n// Slightly delay the reconnection when coming back online as the network is not\n// ready yet and the exponential backoff would delay the reconnection by a lot.\nexport const BACK_ONLINE_RECONNECT_DELAY = 5000;\n/**\n * Communicate with a SharedWorker in order to provide a single websocket\n * connection shared across multiple tabs.\n *\n *  @emits connect\n *  @emits disconnect\n *  @emits reconnect\n *  @emits reconnecting\n *  @emits worker_state_updated\n */\nexport const busService = {\n    dependencies: [\"bus.parameters\", \"localization\", \"multi_tab\", \"notification\"],\n\n    start(env, { multi_tab: multiTab, notification, \"bus.parameters\": params }) {\n        const bus = new EventBus();\n        const notificationBus = new EventBus();\n        const subscribeFnToWrapper = new Map();\n        let worker;\n        /**\n         * @typedef {typeof import(\"@bus/workers/websocket_worker\").WORKER_STATE} WORKER_STATE\n         * @type {WORKER_STATE[keyof WORKER_STATE]}\n         */\n        let workerState;\n        let isActive = false;\n        let isInitialized = false;\n        let isUsingSharedWorker = browser.SharedWorker && !isIosApp();\n        let backOnlineTimeout;\n        const startedAt = luxon.DateTime.now().set({ milliseconds: 0 });\n        const connectionInitializedDeferred = new Deferred();\n\n        /**\n         * Send a message to the worker.\n         *\n         * @param {WorkerAction} action Action to be\n         * executed by the worker.\n         * @param {Object|undefined} data Data required for the action to be\n         * executed.\n         */\n        function send(action, data) {\n            if (!worker) {\n                return;\n            }\n            const message = { action, data };\n            if (isUsingSharedWorker) {\n                worker.port.postMessage(message);\n            } else {\n                worker.postMessage(message);\n            }\n        }\n\n        /**\n         * Handle messages received from the shared worker and fires an\n         * event according to the message type.\n         *\n         * @param {MessageEvent} messageEv\n         * @param {{type: WorkerEvent, data: any}[]}  messageEv.data\n         */\n        function handleMessage(messageEv) {\n            const { type, data } = messageEv.data;\n            switch (type) {\n                case \"notification\": {\n                    const notifications = data.map(({ id, message }) => ({ id, ...message }));\n                    multiTab.setSharedValue(\"last_notification_id\", notifications.at(-1).id);\n                    for (const { id, type, payload } of notifications) {\n                        notificationBus.trigger(type, { id, payload });\n                        busService._onMessage(id, type, payload);\n                    }\n                    break;\n                }\n                case \"initialized\": {\n                    isInitialized = true;\n                    connectionInitializedDeferred.resolve();\n                    break;\n                }\n                case \"worker_state_updated\":\n                    workerState = data;\n                    break;\n                case \"log_debug\":\n                    console.debug(...data);\n                    break;\n                case \"outdated\": {\n                    multiTab.unregister();\n                    notification.add(\n                        _t(\n                            \"Save your work and refresh to get the latest updates and avoid potential issues.\"\n                        ),\n                        {\n                            title: _t(\"The page is out of date\"),\n                            type: \"warning\",\n                            sticky: true,\n                            buttons: [\n                                {\n                                    name: _t(\"Refresh\"),\n                                    primary: true,\n                                    onClick: () => {\n                                        browser.location.reload();\n                                    },\n                                },\n                            ],\n                        }\n                    );\n                    break;\n                }\n            }\n            if (!INTERNAL_EVENTS.has(type)) {\n                bus.trigger(type, data);\n            }\n        }\n\n        /**\n         * Initialize the connection to the worker by sending it usefull\n         * initial informations (last notification id, debug mode,\n         * ...).\n         */\n        function initializeWorkerConnection() {\n            // User_id has different values according to its origin:\n            //     - user service : number or false (key: userId)\n            //     - guest page: array containing null or number\n            //     - public pages: undefined\n            // Let's format it in order to ease its usage:\n            //     - number if user is logged, false otherwise, keep\n            //       undefined to indicate session_info is not available.\n            let uid = Array.isArray(session.user_id) ? session.user_id[0] : user.userId;\n            if (!uid && uid !== undefined) {\n                uid = false;\n            }\n            send(\"initialize_connection\", {\n                websocketURL: `${params.serverURL.replace(\"http\", \"ws\")}/websocket?version=${\n                    session.websocket_worker_version\n                }`,\n                db: session.db,\n                debug: odoo.debug,\n                lastNotificationId: multiTab.getSharedValue(\"last_notification_id\", 0),\n                uid,\n                startTs: startedAt.valueOf(),\n            });\n        }\n\n        /**\n         * Start the \"bus_service\" worker.\n         */\n        function startWorker() {\n            let workerURL = `${params.serverURL}/bus/websocket_worker_bundle?v=${session.websocket_worker_version}`;\n            if (params.serverURL !== window.origin) {\n                // Bus service is loaded from a different origin than the bundle\n                // URL. The Worker expects an URL from this origin, give it a base64\n                // URL that will then load the bundle via \"importScripts\" which\n                // allows cross origin.\n                const source = `importScripts(\"${workerURL}\");`;\n                workerURL = \"data:application/javascript;base64,\" + window.btoa(source);\n            }\n            const workerClass = isUsingSharedWorker ? browser.SharedWorker : browser.Worker;\n            worker = new workerClass(workerURL, {\n                name: isUsingSharedWorker\n                    ? \"odoo:websocket_shared_worker\"\n                    : \"odoo:websocket_worker\",\n            });\n            worker.addEventListener(\"error\", (e) => {\n                if (!isInitialized && workerClass === browser.SharedWorker) {\n                    console.warn(\n                        'Error while loading \"bus_service\" SharedWorker, fallback on Worker.'\n                    );\n                    isUsingSharedWorker = false;\n                    startWorker();\n                } else if (!isInitialized) {\n                    isInitialized = true;\n                    connectionInitializedDeferred.resolve();\n                    console.warn(\"Bus service failed to initialized.\");\n                }\n            });\n            if (isUsingSharedWorker) {\n                worker.port.start();\n                worker.port.addEventListener(\"message\", handleMessage);\n            } else {\n                worker.addEventListener(\"message\", handleMessage);\n            }\n            initializeWorkerConnection();\n        }\n        browser.addEventListener(\"pagehide\", ({ persisted }) => {\n            if (!persisted) {\n                // Page is gonna be unloaded, disconnect this client\n                // from the worker.\n                send(\"leave\");\n            }\n        });\n        browser.addEventListener(\n            \"online\",\n            () => {\n                backOnlineTimeout = browser.setTimeout(() => {\n                    if (isActive) {\n                        send(\"start\");\n                    }\n                }, BACK_ONLINE_RECONNECT_DELAY);\n            },\n            { capture: true }\n        );\n        browser.addEventListener(\n            \"offline\",\n            () => {\n                clearTimeout(backOnlineTimeout);\n                send(\"stop\");\n            },\n            {\n                capture: true,\n            }\n        );\n\n        return {\n            addEventListener: bus.addEventListener.bind(bus),\n            addChannel: async (channel) => {\n                if (!worker) {\n                    startWorker();\n                }\n                await connectionInitializedDeferred;\n                send(\"add_channel\", channel);\n                send(\"start\");\n                isActive = true;\n            },\n            deleteChannel: (channel) => send(\"delete_channel\", channel),\n            forceUpdateChannels: () => send(\"force_update_channels\"),\n            trigger: bus.trigger.bind(bus),\n            removeEventListener: bus.removeEventListener.bind(bus),\n            send: (eventName, data) => send(\"send\", { event_name: eventName, data }),\n            start: async () => {\n                if (!worker) {\n                    startWorker();\n                }\n                await connectionInitializedDeferred;\n                send(\"start\");\n                isActive = true;\n            },\n            stop: () => {\n                send(\"leave\");\n                isActive = false;\n            },\n            get isActive() {\n                return isActive;\n            },\n            /**\n             * Subscribe to a single notification type.\n             *\n             * @param {string} notificationType\n             * @param {function} callback\n             */\n            subscribe(notificationType, callback) {\n                const wrapper = ({ detail }) => {\n                    const { id, payload } = detail;\n                    callback(payload, { id });\n                };\n                subscribeFnToWrapper.set(callback, wrapper);\n                notificationBus.addEventListener(notificationType, wrapper);\n            },\n            /**\n             * Unsubscribe from a single notification type.\n             *\n             * @param {string} notificationType\n             * @param {function} callback\n             */\n            unsubscribe(notificationType, callback) {\n                notificationBus.removeEventListener(\n                    notificationType,\n                    subscribeFnToWrapper.get(callback)\n                );\n                subscribeFnToWrapper.delete(callback);\n            },\n            startedAt,\n            get workerState() {\n                return workerState;\n            },\n        };\n    },\n    /** Overriden to provide logs in tests. Use subscribe() in production. */\n    _onMessage(id, type, payload) {},\n};\nregistry.category(\"services\").add(\"bus_service\", busService);\n", "/** @odoo-module **/\n\nimport { EventBus } from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { registry } from \"@web/core/registry\";\n\nexport const presenceService = {\n    start(env) {\n        const LOCAL_STORAGE_PREFIX = \"presence\";\n        const bus = new EventBus();\n        let isOdooFocused = true;\n        let lastPresenceTime =\n            browser.localStorage.getItem(`${LOCAL_STORAGE_PREFIX}.lastPresence`) ||\n            luxon.DateTime.now().ts;\n\n        function onPresence() {\n            lastPresenceTime = luxon.DateTime.now().ts;\n            browser.localStorage.setItem(`${LOCAL_STORAGE_PREFIX}.lastPresence`, lastPresenceTime);\n            bus.trigger(\"presence\");\n        }\n\n        function onFocusChange(isFocused) {\n            try {\n                isFocused = parent.document.hasFocus();\n            } catch {\n                // noop\n            }\n            isOdooFocused = isFocused;\n            browser.localStorage.setItem(`${LOCAL_STORAGE_PREFIX}.focus`, isOdooFocused);\n            if (isOdooFocused) {\n                lastPresenceTime = luxon.DateTime.now().ts;\n                env.bus.trigger(\"window_focus\", isOdooFocused);\n            }\n        }\n\n        function onStorage({ key, newValue }) {\n            if (key === `${LOCAL_STORAGE_PREFIX}.focus`) {\n                isOdooFocused = JSON.parse(newValue);\n                env.bus.trigger(\"window_focus\", newValue);\n            }\n            if (key === `${LOCAL_STORAGE_PREFIX}.lastPresence`) {\n                lastPresenceTime = JSON.parse(newValue);\n                bus.trigger(\"presence\");\n            }\n        }\n        browser.addEventListener(\"storage\", onStorage);\n        browser.addEventListener(\"focus\", () => onFocusChange(true));\n        browser.addEventListener(\"blur\", () => onFocusChange(false));\n        browser.addEventListener(\"pagehide\", () => onFocusChange(false));\n        browser.addEventListener(\"click\", onPresence);\n        browser.addEventListener(\"keydown\", onPresence);\n\n        return {\n            bus,\n            getLastPresence() {\n                return lastPresenceTime;\n            },\n            isOdooFocused() {\n                return isOdooFocused;\n            },\n            getInactivityPeriod() {\n                return luxon.DateTime.now().ts - this.getLastPresence();\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"presence\", presenceService);\n", "/** @odoo-module **/\n\nimport { debounce, Deferred } from \"@bus/workers/websocket_worker_utils\";\n\n/**\n * Type of events that can be sent from the worker to its clients.\n *\n * @typedef { 'connect' | 'reconnect' | 'disconnect' | 'reconnecting' | 'notification' | 'initialized' | 'outdated'| 'worker_state_updated' | 'log_debug' } WorkerEvent\n */\n\n/**\n * Type of action that can be sent from the client to the worker.\n *\n * @typedef {'add_channel' | 'delete_channel' | 'force_update_channels' | 'initialize_connection' | 'send' | 'leave' | 'stop' | 'start'} WorkerAction\n */\n\nexport const WEBSOCKET_CLOSE_CODES = Object.freeze({\n    CLEAN: 1000,\n    GOING_AWAY: 1001,\n    PROTOCOL_ERROR: 1002,\n    INCORRECT_DATA: 1003,\n    ABNORMAL_CLOSURE: 1006,\n    INCONSISTENT_DATA: 1007,\n    MESSAGE_VIOLATING_POLICY: 1008,\n    MESSAGE_TOO_BIG: 1009,\n    EXTENSION_NEGOTIATION_FAILED: 1010,\n    SERVER_ERROR: 1011,\n    RESTART: 1012,\n    TRY_LATER: 1013,\n    BAD_GATEWAY: 1014,\n    SESSION_EXPIRED: 4001,\n    KEEP_ALIVE_TIMEOUT: 4002,\n    RECONNECTING: 4003,\n});\nexport const WORKER_STATE = Object.freeze({\n    CONNECTED: \"CONNECTED\",\n    DISCONNECTED: \"DISCONNECTED\",\n    IDLE: \"IDLE\",\n    CONNECTING: \"CONNECTING\",\n});\nconst MAXIMUM_RECONNECT_DELAY = 60000;\n\n/**\n * This class regroups the logic necessary in order for the\n * SharedWorker/Worker to work. Indeed, Safari and some minor browsers\n * do not support SharedWorker. In order to solve this issue, a Worker\n * is used in this case. The logic is almost the same than the one used\n * for SharedWorker and this class implements it.\n */\nexport class WebsocketWorker {\n    INITIAL_RECONNECT_DELAY = 1000;\n    RECONNECT_JITTER = 1000;\n\n    constructor() {\n        // Timestamp of start of most recent bus service sender\n        this.newestStartTs = undefined;\n        this.websocketURL = \"\";\n        this.currentUID = null;\n        this.currentDB = null;\n        this.isWaitingForNewUID = true;\n        this.channelsByClient = new Map();\n        this.connectRetryDelay = this.INITIAL_RECONNECT_DELAY;\n        this.connectTimeout = null;\n        this.debugModeByClient = new Map();\n        this.isDebug = false;\n        this.active = true;\n        this.state = WORKER_STATE.IDLE;\n        this.isReconnecting = false;\n        this.lastChannelSubscription = null;\n        this.firstSubscribeDeferred = new Deferred();\n        this.lastNotificationId = 0;\n        this.messageWaitQueue = [];\n        this._forceUpdateChannels = debounce(this._forceUpdateChannels, 300);\n        this._debouncedUpdateChannels = debounce(this._updateChannels, 300);\n        this._debouncedSendToServer = debounce(this._sendToServer, 300);\n\n        this._onWebsocketClose = this._onWebsocketClose.bind(this);\n        this._onWebsocketError = this._onWebsocketError.bind(this);\n        this._onWebsocketMessage = this._onWebsocketMessage.bind(this);\n        this._onWebsocketOpen = this._onWebsocketOpen.bind(this);\n    }\n\n    //--------------------------------------------------------------------------\n    // Public\n    //--------------------------------------------------------------------------\n\n    /**\n     * Send the message to all the clients that are connected to the\n     * worker.\n     *\n     * @param {WorkerEvent} type Event to broadcast to connected\n     * clients.\n     * @param {Object} data\n     */\n    broadcast(type, data) {\n        this._logDebug(\"broadcast\", type, data);\n        for (const client of this.channelsByClient.keys()) {\n            client.postMessage({ type, data: data ? JSON.parse(JSON.stringify(data)) : undefined });\n        }\n    }\n\n    /**\n     * Register a client handled by this worker.\n     *\n     * @param {MessagePort} messagePort\n     */\n    registerClient(messagePort) {\n        messagePort.onmessage = (ev) => {\n            this._onClientMessage(messagePort, ev.data);\n        };\n        this.channelsByClient.set(messagePort, []);\n    }\n\n    /**\n     * Send message to the given client.\n     *\n     * @param {number} client\n     * @param {WorkerEvent} type\n     * @param {Object} data\n     */\n    sendToClient(client, type, data) {\n        this._logDebug(\"sendToClient\", type, data);\n        client.postMessage({ type, data: data ? JSON.parse(JSON.stringify(data)) : undefined });\n    }\n\n    //--------------------------------------------------------------------------\n    // PRIVATE\n    //--------------------------------------------------------------------------\n\n    /**\n     * Called when a message is posted to the worker by a client (i.e. a\n     * MessagePort connected to this worker).\n     *\n     * @param {MessagePort} client\n     * @param {Object} message\n     * @param {WorkerAction} [message.action]\n     * Action to execute.\n     * @param {Object|undefined} [message.data] Data required by the\n     * action.\n     */\n    _onClientMessage(client, { action, data }) {\n        this._logDebug(\"_onClientMessage\", action, data);\n        switch (action) {\n            case \"send\": {\n                if (data[\"event_name\"] === \"update_presence\") {\n                    this._debouncedSendToServer(data);\n                } else {\n                    this._sendToServer(data);\n                }\n                return;\n            }\n            case \"start\":\n                return this._start();\n            case \"stop\":\n                return this._stop();\n            case \"leave\":\n                return this._unregisterClient(client);\n            case \"add_channel\":\n                return this._addChannel(client, data);\n            case \"delete_channel\":\n                return this._deleteChannel(client, data);\n            case \"force_update_channels\":\n                return this._forceUpdateChannels();\n            case \"initialize_connection\":\n                return this._initializeConnection(client, data);\n        }\n    }\n\n    /**\n     * Add a channel for the given client. If this channel is not yet\n     * known, update the subscription on the server.\n     *\n     * @param {MessagePort} client\n     * @param {string} channel\n     */\n    _addChannel(client, channel) {\n        const clientChannels = this.channelsByClient.get(client);\n        if (!clientChannels.includes(channel)) {\n            clientChannels.push(channel);\n            this.channelsByClient.set(client, clientChannels);\n            this._debouncedUpdateChannels();\n        }\n    }\n\n    /**\n     * Remove a channel for the given client. If this channel is not\n     * used anymore, update the subscription on the server.\n     *\n     * @param {MessagePort} client\n     * @param {string} channel\n     */\n    _deleteChannel(client, channel) {\n        const clientChannels = this.channelsByClient.get(client);\n        if (!clientChannels) {\n            return;\n        }\n        const channelIndex = clientChannels.indexOf(channel);\n        if (channelIndex !== -1) {\n            clientChannels.splice(channelIndex, 1);\n            this._debouncedUpdateChannels();\n        }\n    }\n\n    /**\n     * Update the channels on the server side even if the channels on\n     * the client side are the same than the last time we subscribed.\n     */\n    _forceUpdateChannels() {\n        this._updateChannels({ force: true });\n    }\n\n    /**\n     * Remove the given client from this worker client list as well as\n     * its channels. If some of its channels are not used anymore,\n     * update the subscription on the server.\n     *\n     * @param {MessagePort} client\n     */\n    _unregisterClient(client) {\n        this.channelsByClient.delete(client);\n        this.debugModeByClient.delete(client);\n        this.isDebug = [...this.debugModeByClient.values()].some(Boolean);\n        this._debouncedUpdateChannels();\n    }\n\n    /**\n     * Initialize a client connection to this worker.\n     *\n     * @param {Object} param0\n     * @param {string} [param0.db] Database name.\n     * @param {String} [param0.debug] Current debugging mode for the\n     * given client.\n     * @param {Number} [param0.lastNotificationId] Last notification id\n     * known by the client.\n     * @param {String} [param0.websocketURL] URL of the websocket endpoint.\n     * @param {Number|false|undefined} [param0.uid] Current user id\n     *     - Number: user is logged whether on the frontend/backend.\n     *     - false: user is not logged.\n     *     - undefined: not available (e.g. livechat support page)\n     * @param {Number} param0.startTs Timestamp of start of bus service sender.\n     */\n    _initializeConnection(client, { db, debug, lastNotificationId, uid, websocketURL, startTs }) {\n        if (this.newestStartTs && this.newestStartTs > startTs) {\n            this.debugModeByClient.set(client, debug);\n            this.isDebug = [...this.debugModeByClient.values()].some(Boolean);\n            this.sendToClient(client, \"update_state\", this.state);\n            this.sendToClient(client, \"initialized\");\n            return;\n        }\n        this.newestStartTs = startTs;\n        this.websocketURL = websocketURL;\n        this.lastNotificationId = lastNotificationId;\n        this.debugModeByClient.set(client, debug);\n        this.isDebug = [...this.debugModeByClient.values()].some(Boolean);\n        const isCurrentUserKnown = uid !== undefined;\n        if (this.isWaitingForNewUID && isCurrentUserKnown) {\n            this.isWaitingForNewUID = false;\n            this.currentUID = uid;\n        }\n        if ((this.currentUID !== uid && isCurrentUserKnown) || this.currentDB !== db) {\n            this.currentUID = uid;\n            this.currentDB = db;\n            if (this.websocket) {\n                this.websocket.close(WEBSOCKET_CLOSE_CODES.CLEAN);\n            }\n            this.channelsByClient.forEach((_, key) => this.channelsByClient.set(key, []));\n        }\n        this.sendToClient(client, \"update_state\", this.state);\n        this.sendToClient(client, \"initialized\");\n        if (!this.active) {\n            this.sendToClient(client, \"outdated\");\n        }\n    }\n\n    /**\n     * Determine whether or not the websocket associated to this worker\n     * is connected.\n     *\n     * @returns {boolean}\n     */\n    _isWebsocketConnected() {\n        return this.websocket && this.websocket.readyState === 1;\n    }\n\n    /**\n     * Determine whether or not the websocket associated to this worker\n     * is connecting.\n     *\n     * @returns {boolean}\n     */\n    _isWebsocketConnecting() {\n        return this.websocket && this.websocket.readyState === 0;\n    }\n\n    /**\n     * Determine whether or not the websocket associated to this worker\n     * is in the closing state.\n     *\n     * @returns {boolean}\n     */\n    _isWebsocketClosing() {\n        return this.websocket && this.websocket.readyState === 2;\n    }\n\n    /**\n     * Triggered when a connection is closed. If closure was not clean ,\n     * try to reconnect after indicating to the clients that the\n     * connection was closed.\n     *\n     * @param {CloseEvent} ev\n     * @param {number} code  close code indicating why the connection\n     * was closed.\n     * @param {string} reason reason indicating why the connection was\n     * closed.\n     */\n    _onWebsocketClose({ code, reason }) {\n        this._logDebug(\"_onWebsocketClose\", code, reason);\n        this._updateState(WORKER_STATE.DISCONNECTED);\n        this.lastChannelSubscription = null;\n        this.firstSubscribeDeferred = new Deferred();\n        if (this.isReconnecting) {\n            // Connection was not established but the close event was\n            // triggered anyway. Let the onWebsocketError method handle\n            // this case.\n            return;\n        }\n        this.broadcast(\"disconnect\", { code, reason });\n        if (code === WEBSOCKET_CLOSE_CODES.CLEAN) {\n            if (reason === \"OUTDATED_VERSION\") {\n                console.warn(\"Worker deactivated due to an outdated version.\");\n                this.active = false;\n                this.broadcast(\"outdated\");\n            }\n            // WebSocket was closed on purpose, do not try to reconnect.\n            return;\n        }\n        // WebSocket was not closed cleanly, let's try to reconnect.\n        this.broadcast(\"reconnecting\", { closeCode: code });\n        this.isReconnecting = true;\n        if (code === WEBSOCKET_CLOSE_CODES.KEEP_ALIVE_TIMEOUT) {\n            // Don't wait to reconnect on keep alive timeout.\n            this.connectRetryDelay = 0;\n        }\n        if (code === WEBSOCKET_CLOSE_CODES.SESSION_EXPIRED) {\n            this.isWaitingForNewUID = true;\n        }\n        this._retryConnectionWithDelay();\n    }\n\n    /**\n     * Triggered when a connection failed or failed to established.\n     */\n    _onWebsocketError() {\n        this._logDebug(\"_onWebsocketError\");\n        this._retryConnectionWithDelay();\n    }\n\n    /**\n     * Handle data received from the bus.\n     *\n     * @param {MessageEvent} messageEv\n     */\n    _onWebsocketMessage(messageEv) {\n        const notifications = JSON.parse(messageEv.data);\n        this._logDebug(\"_onWebsocketMessage\", notifications);\n        this.lastNotificationId = notifications[notifications.length - 1].id;\n        this.broadcast(\"notification\", notifications);\n    }\n\n    _logDebug(title, ...args) {\n        const clientsInDebug = [...this.debugModeByClient.keys()].filter((client) =>\n            this.debugModeByClient.get(client)\n        );\n        for (const client of clientsInDebug) {\n            client.postMessage({\n                type: \"log_debug\",\n                data: [\n                    `%c${new Date().toLocaleString()} - [${title}]`,\n                    \"color: #c6e; font-weight: bold;\",\n                    ...args,\n                ],\n            });\n        }\n    }\n\n    /**\n     * Triggered on websocket open. Send message that were waiting for\n     * the connection to open.\n     */\n    _onWebsocketOpen() {\n        this._logDebug(\"_onWebsocketOpen\");\n        this._updateState(WORKER_STATE.CONNECTED);\n        this.broadcast(this.isReconnecting ? \"reconnect\" : \"connect\");\n        this._debouncedUpdateChannels();\n        this.connectRetryDelay = this.INITIAL_RECONNECT_DELAY;\n        this.connectTimeout = null;\n        this.isReconnecting = false;\n        this.firstSubscribeDeferred.then(() => {\n            this.messageWaitQueue.forEach((msg) => this.websocket.send(msg));\n            this.messageWaitQueue = [];\n        });\n    }\n\n    /**\n     * Try to reconnect to the server, an exponential back off is\n     * applied to the reconnect attempts.\n     */\n    _retryConnectionWithDelay() {\n        this.connectRetryDelay =\n            Math.min(this.connectRetryDelay * 1.5, MAXIMUM_RECONNECT_DELAY) +\n            this.RECONNECT_JITTER * Math.random();\n        this._logDebug(\"_retryConnectionWithDelay\", this.connectRetryDelay);\n        this.connectTimeout = setTimeout(this._start.bind(this), this.connectRetryDelay);\n    }\n\n    /**\n     * Send a message to the server through the websocket connection.\n     * If the websocket is not open, enqueue the message and send it\n     * upon the next reconnection.\n     *\n     * @param {{event_name: string, data: any }} message Message to send to the server.\n     */\n    _sendToServer(message) {\n        this._logDebug(\"_sendToServer\", message);\n        const payload = JSON.stringify(message);\n        if (!this._isWebsocketConnected()) {\n            if (message[\"event_name\"] === \"subscribe\") {\n                this.messageWaitQueue = this.messageWaitQueue.filter(\n                    (msg) => JSON.parse(msg).event_name !== \"subscribe\"\n                );\n                this.messageWaitQueue.unshift(payload);\n            } else {\n                this.messageWaitQueue.push(payload);\n            }\n        } else {\n            if (message[\"event_name\"] === \"subscribe\") {\n                this.websocket.send(payload);\n            } else {\n                this.firstSubscribeDeferred.then(() => this.websocket.send(payload));\n            }\n        }\n    }\n\n    _removeWebsocketListeners() {\n        this.websocket?.removeEventListener(\"open\", this._onWebsocketOpen);\n        this.websocket?.removeEventListener(\"message\", this._onWebsocketMessage);\n        this.websocket?.removeEventListener(\"error\", this._onWebsocketError);\n        this.websocket?.removeEventListener(\"close\", this._onWebsocketClose);\n    }\n\n    /**\n     * Start the worker by opening a websocket connection.\n     */\n    _start() {\n        this._logDebug(\"_start\");\n        if (!this.active || this._isWebsocketConnected() || this._isWebsocketConnecting()) {\n            return;\n        }\n        this._removeWebsocketListeners();\n        if (this._isWebsocketClosing()) {\n            // close event was not triggered and will never be, broadcast the\n            // disconnect event for consistency sake.\n            this.lastChannelSubscription = null;\n            this.broadcast(\"disconnect\", { code: WEBSOCKET_CLOSE_CODES.ABNORMAL_CLOSURE });\n        }\n        this._updateState(WORKER_STATE.CONNECTING);\n        this.websocket = new WebSocket(this.websocketURL);\n        this.websocket.addEventListener(\"open\", this._onWebsocketOpen);\n        this.websocket.addEventListener(\"error\", this._onWebsocketError);\n        this.websocket.addEventListener(\"message\", this._onWebsocketMessage);\n        this.websocket.addEventListener(\"close\", this._onWebsocketClose);\n    }\n\n    /**\n     * Stop the worker.\n     */\n    _stop() {\n        this._logDebug(\"_stop\");\n        clearTimeout(this.connectTimeout);\n        this.connectRetryDelay = this.INITIAL_RECONNECT_DELAY;\n        this.isReconnecting = false;\n        this.lastChannelSubscription = null;\n        this.websocket?.close();\n        this._removeWebsocketListeners();\n    }\n\n    /**\n     * Update the channel subscription on the server. Ignore if the channels\n     * did not change since the last subscription.\n     *\n     * @param {boolean} force Whether or not we should update the subscription\n     * event if the channels haven't change since last subscription.\n     */\n    _updateChannels({ force = false } = {}) {\n        const allTabsChannels = [\n            ...new Set([].concat.apply([], [...this.channelsByClient.values()])),\n        ].sort();\n        const allTabsChannelsString = JSON.stringify(allTabsChannels);\n        const shouldUpdateChannelSubscription =\n            allTabsChannelsString !== this.lastChannelSubscription;\n        if (force || shouldUpdateChannelSubscription) {\n            this.lastChannelSubscription = allTabsChannelsString;\n            this._sendToServer({\n                event_name: \"subscribe\",\n                data: { channels: allTabsChannels, last: this.lastNotificationId },\n            });\n            this.firstSubscribeDeferred.resolve();\n        }\n    }\n    /**\n     * Update the worker state and broadcast the new state to its clients.\n     *\n     * @param {WORKER_STATE[keyof WORKER_STATE]} newState\n     */\n    _updateState(newState) {\n        this.state = newState;\n        this.broadcast(\"worker_state_updated\", newState);\n    }\n}\n", "/** @odoo-module **/\n\n/**\n * Returns a function, that, as long as it continues to be invoked, will not\n * be triggered. The function will be called after it stops being called for\n * N milliseconds. If `immediate` is passed, trigger the function on the\n * leading edge, instead of the trailing.\n *\n * Inspired by https://davidwalsh.name/javascript-debounce-function\n */\nexport function debounce(func, wait, immediate) {\n    let timeout;\n    return function () {\n        const context = this;\n        const args = arguments;\n        function later() {\n            timeout = null;\n            if (!immediate) {\n                func.apply(context, args);\n            }\n        }\n        const callNow = immediate && !timeout;\n        clearTimeout(timeout);\n        timeout = setTimeout(later, wait);\n        if (callNow) {\n            func.apply(context, args);\n        }\n    };\n}\n\n/**\n * Deferred is basically a resolvable/rejectable extension of Promise.\n */\nexport class Deferred extends Promise {\n    constructor() {\n        let resolve;\n        let reject;\n        const prom = new Promise((res, rej) => {\n            resolve = res;\n            reject = rej;\n        });\n        return Object.assign(prom, { resolve, reject });\n    }\n}\n", "/** @odoo-module **/\n\nimport { Component, useEffect, useRef } from \"@odoo/owl\";\nimport { usePosition } from \"@web/core/position/position_hook\";\n\n/**\n * @typedef {import(\"../tour_service/tour_pointer_state\").TourPointerState} TourPointerState\n *\n * @typedef TourPointerProps\n * @property {TourPointerState} pointerState\n * @property {boolean} bounce\n */\n\n/** @extends {Component<TourPointerProps, any>} */\nexport class TourPointer extends Component {\n    static props = {\n        pointerState: {\n            type: Object,\n            shape: {\n                anchor: { type: HTMLElement, optional: true },\n                content: { type: String, optional: true },\n                isOpen: { type: Boolean, optional: true },\n                isVisible: { type: Boolean, optional: true },\n                isZone: { type: Boolean, optional: true },\n                onClick: { type: [Function, { value: null }], optional: true },\n                onMouseEnter: { type: [Function, { value: null }], optional: true },\n                onMouseLeave: { type: [Function, { value: null }], optional: true },\n                position: {\n                    type: [\n                        { value: \"left\" },\n                        { value: \"right\" },\n                        { value: \"top\" },\n                        { value: \"bottom\" },\n                    ],\n                    optional: true,\n                },\n                rev: { type: Number, optional: true },\n            },\n        },\n        bounce: { type: Boolean, optional: true },\n    };\n\n    static defaultProps = {\n        bounce: true,\n    };\n\n    static template = \"web_tour.TourPointer\";\n    static width = 28; // in pixels\n    static height = 28; // in pixels\n\n    setup() {\n        const positionOptions = {\n            margin: 6,\n            onPositioned: (pointer, position) => {\n                const popperRect = pointer.getBoundingClientRect();\n                const { top, left, direction } = position;\n                if (direction === \"top\") {\n                    // position from the bottom instead of the top as it is needed\n                    // to ensure the expand animation is properly done\n                    pointer.style.bottom = `${window.innerHeight - top - popperRect.height}px`;\n                    pointer.style.removeProperty(\"top\");\n                } else if (direction === \"left\") {\n                    // position from the right instead of the left as it is needed\n                    // to ensure the expand animation is properly done\n                    pointer.style.right = `${window.innerWidth - left - popperRect.width}px`;\n                    pointer.style.removeProperty(\"left\");\n                }\n            },\n        };\n        Object.defineProperty(positionOptions, \"position\", {\n            get: () => this.position,\n            enumerable: true,\n        });\n        const position = usePosition(\n            \"pointer\",\n            () => this.props.pointerState.anchor,\n            positionOptions\n        );\n        const rootRef = useRef(\"pointer\");\n        const zoneRef = useRef(\"zone\");\n        /** @type {DOMREct | null} */\n        let dimensions = null;\n        let lastMeasuredContent = null;\n        let lastOpenState = this.isOpen;\n        let lastAnchor;\n        let [anchorX, anchorY] = [0, 0];\n        useEffect(() => {\n            const { el: pointer } = rootRef;\n            const { el: zone } = zoneRef;\n            if (pointer) {\n                const hasContentChanged = lastMeasuredContent !== this.content;\n                const hasOpenStateChanged = lastOpenState !== this.isOpen;\n                lastOpenState = this.isOpen;\n\n                // Check is the pointed element is a zone\n                if (this.props.pointerState.isZone) {\n                    const { anchor } = this.props.pointerState;\n                    const { left, top, width, height } = anchor.getBoundingClientRect();\n                    zone.style.minWidth = width + \"px\";\n                    zone.style.minHeight = height + \"px\";\n                    zone.style.left = left + \"px\";\n                    zone.style.top = top + \"px\";\n                }\n\n                // Content changed: we must re-measure the dimensions of the text.\n                if (hasContentChanged) {\n                    lastMeasuredContent = this.content;\n                    pointer.style.removeProperty(\"width\");\n                    pointer.style.removeProperty(\"height\");\n                    dimensions = pointer.getBoundingClientRect();\n                }\n\n                // If the content or the \"is open\" state changed: we must apply\n                // new width and height properties\n                if (hasContentChanged || hasOpenStateChanged) {\n                    const [width, height] = this.isOpen\n                        ? [dimensions.width, dimensions.height]\n                        : [this.constructor.width, this.constructor.height];\n                    if (this.isOpen) {\n                        pointer.style.removeProperty(\"transition\");\n                    } else {\n                        // No transition if switching from open to closed\n                        pointer.style.setProperty(\"transition\", \"none\");\n                    }\n                    pointer.style.setProperty(\"width\", `${width}px`);\n                    pointer.style.setProperty(\"height\", `${height}px`);\n                }\n\n                if (!this.isOpen) {\n                    const { anchor } = this.props.pointerState;\n                    if (anchor === lastAnchor) {\n                        const { x, y, width } = anchor.getBoundingClientRect();\n                        const [lastAnchorX, lastAnchorY] = [anchorX, anchorY];\n                        [anchorX, anchorY] = [x, y];\n                        // Let's just say that the anchor is static if it moved less than 1px.\n                        const delta = Math.sqrt(\n                            Math.pow(x - lastAnchorX, 2) + Math.pow(y - lastAnchorY, 2)\n                        );\n                        if (delta < 1) {\n                            position.lock();\n                            return;\n                        }\n                        const wouldOverflow = window.innerWidth - x - width / 2 < dimensions?.width;\n                        pointer.classList.toggle(\"o_expand_left\", wouldOverflow);\n                    }\n                    lastAnchor = anchor;\n                    pointer.style.bottom = \"\";\n                    pointer.style.right = \"\";\n                    position.unlock();\n                }\n            } else {\n                lastMeasuredContent = null;\n                lastOpenState = false;\n                lastAnchor = null;\n                dimensions = null;\n            }\n        });\n    }\n\n    get content() {\n        return this.props.pointerState.content || \"\";\n    }\n\n    get isOpen() {\n        return this.props.pointerState.isOpen && this.content;\n    }\n\n    get position() {\n        return this.props.pointerState.position || \"top\";\n    }\n}\n", "import { tourState } from \"./tour_state\";\nimport { config as transitionConfig } from \"@web/core/transition\";\nimport { TourStepAutomatic } from \"./tour_step_automatic\";\nimport { Macro } from \"@web/core/macro\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { setupEventActions } from \"@web/../lib/hoot-dom/helpers/events\";\nimport * as hoot from \"@odoo/hoot-dom\";\nimport { patch } from \"@web/core/utils/patch\";\n\nexport class TourAutomatic {\n    mode = \"auto\";\n    pointer = null;\n    constructor(data) {\n        Object.assign(this, data);\n        this.steps = this.steps.map((step, index) => new TourStepAutomatic(step, this, index));\n        this.config = tourState.getCurrentConfig() || {};\n    }\n\n    get currentIndex() {\n        return tourState.getCurrentIndex();\n    }\n\n    get currentStep() {\n        return this.steps[this.currentIndex];\n    }\n\n    get debugMode() {\n        return this.config.debug !== false;\n    }\n\n    get checkForUndeterminisms() {\n        return this.config.delayToCheckUndeterminisms > 0;\n    }\n\n    start(pointer) {\n        setupEventActions(document.createElement(\"div\"));\n        const macroSteps = this.steps\n            .filter((step) => step.index >= this.currentIndex)\n            .flatMap((step) => {\n                return [\n                    {\n                        action: async () => {\n                            if (this.debugMode) {\n                                console.groupCollapsed(step.describeMe);\n                                console.log(step.stringify);\n                            } else {\n                                console.log(step.describeMe);\n                            }\n                            if (step.break && this.debugMode) {\n                                // eslint-disable-next-line no-debugger\n                                debugger;\n                            }\n                            // This delay is important for making the current set of tour tests pass.\n                            // IMPROVEMENT: Find a way to remove this delay.\n                            await new Promise((resolve) => requestAnimationFrame(resolve));\n                            if (this.config.stepDelay > 0) {\n                                await hoot.delay(this.config.stepDelay);\n                            }\n                        },\n                    },\n                    {\n                        initialDelay: () => {\n                            return this.previousStepIsJustACheck ? 0 : null;\n                        },\n                        trigger: step.trigger ? () => step.findTrigger() : null,\n                        timeout: (step.timeout || 10000) + this.config.stepDelay,\n                        action: async () => {\n                            if (this.checkForUndeterminisms) {\n                                await step.checkForUndeterminisms();\n                            }\n                            this.previousStepIsJustACheck = !this.currentStep.hasAction;\n                            if (this.debugMode) {\n                                if (!step.skipped && this.showPointerDuration > 0 && step.element) {\n                                    // Useful in watch mode.\n                                    pointer.pointTo(step.element, this);\n                                    await hoot.delay(this.showPointerDuration);\n                                    pointer.hide();\n                                }\n                                console.log(step.element);\n                                if (step.skipped) {\n                                    console.log(\"This step has been skipped\");\n                                } else {\n                                    console.log(\"This step has run successfully\");\n                                }\n                                console.groupEnd();\n                            }\n                            const result = await step.doAction();\n                            if (step.pause && this.debugMode) {\n                                await this.pause();\n                            }\n                            tourState.setCurrentIndex(step.index + 1);\n                            return result;\n                        },\n                    },\n                ];\n            });\n\n        const end = () => {\n            //Tour is finished, it's too late to console.\n            patch(console, {\n                error: () => {},\n                warn: () => {},\n            });\n            delete window.hoot;\n            transitionConfig.disabled = false;\n            tourState.clear();\n            pointer.stop();\n            //No need to catch error yet.\n            window.addEventListener(\"error\", (ev) => ev.preventDefault());\n            window.addEventListener(\"unhandledrejection\", (ev) => ev.preventDefault());\n        };\n\n        this.macro = new Macro({\n            name: this.name,\n            checkDelay: this.checkDelay || 200,\n            steps: macroSteps,\n            onError: (error) => {\n                this.throwError([error]);\n                end();\n            },\n            onComplete: () => {\n                browser.console.log(\"tour succeeded\");\n                // Used to see easily in the python console and to know which tour has been succeeded in suite tours case.\n                const succeeded = `\u2551 TOUR ${this.name} SUCCEEDED \u2551`;\n                const msg = [succeeded];\n                msg.unshift(\"\u2554\" + \"\u2550\".repeat(succeeded.length - 2) + \"\u2557\");\n                msg.push(\"\u255a\" + \"\u2550\".repeat(succeeded.length - 2) + \"\u255d\");\n                browser.console.log(`\\n\\n${msg.join(\"\\n\")}\\n`);\n                end();\n            },\n            onTimeout: (timeout) => {\n                this.throwError([\n                    ...this.currentStep.describeWhyIFailed,\n                    `TIMEOUT: The step failed to complete within ${timeout} ms.`,\n                ]);\n                end();\n            },\n        });\n        if (this.debugMode && this.currentIndex === 0) {\n            // Starts the tour with a debugger to allow you to choose devtools configuration.\n            // eslint-disable-next-line no-debugger\n            debugger;\n        }\n        transitionConfig.disabled = true;\n        window.hoot = hoot;\n        this.macro.start();\n    }\n\n    get describeWhereIFailed() {\n        const offset = 3;\n        const start = Math.max(this.currentIndex - offset, 0);\n        const end = Math.min(this.currentIndex + offset, this.steps.length - 1);\n        const result = [];\n        for (let i = start; i <= end; i++) {\n            const step = this.steps[i];\n            const stepString = step.stringify;\n            const text = [stepString];\n            if (i === this.currentIndex) {\n                const line = \"-\".repeat(10);\n                const failing_step = `${line} FAILED: ${step.describeMe} ${line}`;\n                text.unshift(failing_step);\n                text.push(\"-\".repeat(failing_step.length));\n            }\n            result.push(...text);\n        }\n        return result.join(\"\\n\");\n    }\n\n    /**\n     * @param {string} [error]\n     */\n    throwError(errors = []) {\n        console.groupEnd();\n        tourState.setCurrentTourOnError();\n        // console.error notifies the test runner that the tour failed.\n        browser.console.error([`FAILED: ${this.currentStep.describeMe}.`, ...errors].join(\"\\n\"));\n        // The logged text shows the relative position of the failed step.\n        // Useful for finding the failed step.\n        browser.console.dir(this.describeWhereIFailed);\n        if (this.debugMode) {\n            // eslint-disable-next-line no-debugger\n            debugger;\n        }\n    }\n\n    async pause() {\n        const styles = [\n            \"background: black; color: white; font-size: 14px\",\n            \"background: black; color: orange; font-size: 14px\",\n        ];\n        console.log(\n            `%cTour is paused. Use %cplay()%c to continue.`,\n            styles[0],\n            styles[1],\n            styles[0]\n        );\n        await new Promise((resolve) => {\n            window.play = () => {\n                resolve();\n                delete window.play;\n            };\n        });\n    }\n}\n", "import * as hoot from \"@odoo/hoot-dom\";\n\nexport class TourHelpers {\n    /**\n     * @typedef {string|Node} Selector\n     */\n\n    constructor(anchor) {\n        this.anchor = anchor;\n        this.delay = 20;\n    }\n\n    /**\n     * Ensures that the given {@link Selector} is checked.\n     * @description\n     * If it is not checked, a click is triggered on the input.\n     * If the input is still not checked after the click, an error is thrown.\n     *\n     * @param {string|Node} selector\n     * @example\n     *  run: \"check\", //Checks the action element\n     * @example\n     *  run: \"check input[type=checkbox]\", // Checks the selector\n     */\n    async check(selector) {\n        const element = this._get_action_element(selector);\n        await hoot.check(element);\n    }\n\n    /**\n     * Clears the **value** of the **{@link Selector}**.\n     * @description\n     * This is done using the following sequence:\n     * - pressing \"Control\" + \"A\" to select the whole value;\n     * - pressing \"Backspace\" to delete the value;\n     * - (optional) triggering a \"change\" event by pressing \"Enter\".\n     *\n     * @param {Selector} selector\n     * @example\n     *  run: \"clear\", // Clears the value of the action element\n     * @example\n     *  run: \"clear input#my_input\", // Clears the value of the selector\n     */\n    async clear(selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        await hoot.clear();\n    }\n\n    /**\n     * Performs a click sequence on the given **{@link Selector}**\n     * @description Let's see more informations about click sequence here: {@link hoot.click}\n     * @param {Selector} selector\n     * @example\n     *  run: \"click\", // Click on the action element\n     * @example\n     *  run: \"click .o_rows:first\", // Click on the selector\n     */\n    async click(selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n    }\n\n    /**\n     * Performs two click sequences on the given **{@link Selector}**.\n     * @description Let's see more informations about click sequence here: {@link hoot.dblclick}\n     * @param {Selector} selector\n     * @example\n     *  run: \"dblclick\", // Double click on the action element\n     * @example\n     *  run: \"dblclick .o_rows:first\", // Double click on the selector\n     */\n    async dblclick(selector) {\n        const element = this._get_action_element(selector);\n        await hoot.dblclick(element);\n    }\n\n    /**\n     * Starts a drag sequence on the active element (anchor) and drop it on the given **{@link Selector}**.\n     * @param {Selector} selector\n     * @param {hoot.PointerOptions} options\n     * @example\n     *  run: \"drag_and_drop .o_rows:first\", // Drag the active element and drop it in the selector\n     * @example\n     *  async run(helpers) {\n     *      await helpers.drag_and_drop(\".o_rows:first\", {\n     *          position: {\n     *              top: 40,\n     *              left: 5,\n     *          },\n     *          relative: true,\n     *      });\n     *  }\n     */\n    async drag_and_drop(selector, options) {\n        if (typeof options !== \"object\") {\n            options = { position: \"top\", relative: true };\n        }\n        const dragEffectDelay = async () => {\n            await new Promise((resolve) => requestAnimationFrame(resolve));\n            await new Promise((resolve) => setTimeout(resolve, this.delay));\n        };\n        const element = this.anchor;\n        const { drop, moveTo } = await hoot.drag(element);\n        await dragEffectDelay();\n        await hoot.hover(element, {\n            position: {\n                top: 20,\n                left: 20,\n            },\n            relative: true,\n        });\n        await dragEffectDelay();\n        const target = await hoot.waitFor(selector, {\n            visible: true,\n            timeout: 500,\n        });\n        await moveTo(target, options);\n        await dragEffectDelay();\n        await drop();\n        await dragEffectDelay();\n    }\n\n    /**\n     * Edit input or textarea given by **{@link selector}**\n     * @param {string} text\n     * @param {Selector} selector\n     * @example\n     *  run: \"edit Hello Mr. Doku\",\n     */\n    async edit(text, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        await hoot.edit(text);\n    }\n\n    /**\n     * Edit only editable wysiwyg element given by **{@link Selector}**\n     * @param {string} text\n     * @param {Selector} selector\n     */\n    async editor(text, selector) {\n        const element = this._get_action_element(selector);\n        const InEditor = Boolean(element.closest(\".odoo-editor-editable\"));\n        if (!InEditor) {\n            throw new Error(\"run 'editor' always on an element in an editor\");\n        }\n        await hoot.click(element);\n        this._set_range(element, \"start\");\n        await hoot.keyDown(\"_\");\n        element.textContent = text;\n        await hoot.manuallyDispatchProgrammaticEvent(element, \"input\");\n        this._set_range(element, \"stop\");\n        await hoot.keyUp(\"_\");\n        await hoot.manuallyDispatchProgrammaticEvent(element, \"change\");\n    }\n\n    /**\n     * Fills the **{@link Selector}** with the given `value`.\n     * @description This helper is intended for `<input>` and `<textarea>` elements,\n     * with the exception of `\"checkbox\"` and `\"radio\"` types, which should be\n     * selected using the {@link check} helper.\n     * In tour, it's mainly usefull for autocomplete components.\n     * @param {string} value\n     * @param {Selector} selector\n     */\n    async fill(value, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        await hoot.fill(value);\n    }\n\n    /**\n     * Performs a hover sequence on the given **{@link Selector}**.\n     * @param {Selector} selector\n     * @example\n     *  run: \"hover\",\n     */\n    async hover(selector) {\n        const element = this._get_action_element(selector);\n        await hoot.hover(element);\n    }\n\n    /**\n     * Only for input[type=\"range\"]\n     * @param {string|number} value\n     * @param {Selector} selector\n     */\n    async range(value, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        await hoot.setInputRange(element, value);\n    }\n\n    /**\n     * Performs a keyboard event sequence.\n     * @example\n     *  run : \"press Enter\",\n     */\n    press(...args) {\n        return hoot.press(args.flatMap((arg) => typeof arg === \"string\" && arg.split(\"+\")));\n    }\n\n    /**\n     * Performs a selection event sequence on **{@link Selector}**. This helper is intended\n     * for `<select>` elements only.\n     * @description Select the option by its value\n     * @param {string} value\n     * @param {Selector} selector\n     * @example\n     * run(helpers) => {\n     *  helpers.select(\"Kevin17\", \"select#mySelect\");\n     * },\n     * @example\n     * run: \"select Foden47\",\n     */\n    async select(value, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        await hoot.select(value, { target: element });\n    }\n\n    /**\n     * Performs a selection event sequence on **{@link Selector}**\n     * @description Select the option by its index\n     * @param {number} index starts at 0\n     * @param {Selector} selector\n     * @example\n     *  run: \"selectByIndex 2\", //Select the third option\n     */\n    async selectByIndex(index, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        const value = hoot.queryValue(`option:eq(${index})`, { root: element });\n        if (value) {\n            await hoot.select(value, { target: element });\n            await hoot.manuallyDispatchProgrammaticEvent(element, \"input\");\n        }\n    }\n\n    /**\n     * Performs a selection event sequence on **{@link Selector}**\n     * @description Select option(s) by there labels\n     * @param {string|RegExp} contains\n     * @param {Selector} selector\n     * @example\n     *  run: \"selectByLabel Jeremy Doku\", //Select all options where label contains Jeremy Doku\n     */\n    async selectByLabel(contains, selector) {\n        const element = this._get_action_element(selector);\n        await hoot.click(element);\n        const values = hoot.queryAllValues(`option:contains(${contains})`, { root: element });\n        await hoot.select(values, { target: element });\n    }\n\n    /**\n     * Ensures that the given {@link Selector} is unchecked.\n     * @description\n     * If it is checked, a click is triggered on the input.\n     * If the input is still checked after the click, an error is thrown.\n     *\n     * @param {string|Node} selector\n     * @example\n     *  run: \"uncheck\", // Unchecks the action element\n     * @example\n     *  run: \"uncheck input[type=checkbox]\", // Unchecks the selector\n     */\n    uncheck(selector) {\n        const element = this._get_action_element(selector);\n        hoot.uncheck(element);\n    }\n\n    /**\n     * Navigate to {@link url}.\n     *\n     * @param {string} url\n     * @example\n     *  run: \"goToUrl /shop\", // Go to /shop\n     */\n    goToUrl(url) {\n        const linkEl = document.createElement(\"a\");\n        linkEl.href = url;\n        linkEl.click();\n    }\n\n    /**\n     * Get Node for **{@link Selector}**\n     * @param {Selector} selector\n     * @returns {Node}\n     * @default this.anchor\n     */\n    _get_action_element(selector) {\n        if (typeof selector === \"string\" && selector.length) {\n            const nodes = hoot.queryAll(selector);\n            return nodes.find(hoot.isVisible) || nodes.at(0);\n        } else if (typeof selector === \"object\" && Boolean(selector?.nodeType)) {\n            return selector;\n        }\n        return this.anchor;\n    }\n\n    // Useful for wysiwyg editor.\n    _set_range(element, start_or_stop) {\n        function _node_length(node) {\n            if (node.nodeType === Node.TEXT_NODE) {\n                return node.nodeValue.length;\n            } else {\n                return node.childNodes.length;\n            }\n        }\n        const selection = element.ownerDocument.getSelection();\n        selection.removeAllRanges();\n        const range = new Range();\n        let node = element;\n        let length = 0;\n        if (start_or_stop === \"start\") {\n            while (node.firstChild) {\n                node = node.firstChild;\n            }\n        } else {\n            while (node.lastChild) {\n                node = node.lastChild;\n            }\n            length = _node_length(node);\n        }\n        range.setStart(node, length);\n        range.setEnd(node, length);\n        selection.addRange(range);\n    }\n}\n", "import { tourState } from \"@web_tour/tour_service/tour_state\";\nimport { debounce } from \"@web/core/utils/timing\";\nimport { getScrollParent } from \"@web_tour/tour_service/tour_utils\";\nimport * as hoot from \"@odoo/hoot-dom\";\nimport { utils } from \"@web/core/ui/ui_service\";\nimport { TourStep } from \"./tour_step\";\nimport { MacroMutationObserver } from \"@web/core/macro\";\n\n/**\n * @typedef ConsumeEvent\n * @property {string} name\n * @property {Element} target\n * @property {(ev: Event) => boolean} conditional\n */\n\nexport class TourInteractive {\n    mode = \"manual\";\n    currentAction;\n    currentActionIndex;\n    anchorEls = [];\n    removeListeners = () => {};\n\n    /**\n     * @param {Tour} data\n     */\n    constructor(data) {\n        Object.assign(this, data);\n        this.steps = this.steps.map((step) => new TourStep(step, this));\n        this.actions = this.steps.flatMap((s) => this.getSubActions(s));\n    }\n\n    /**\n     * @param {import(\"@web_tour/tour_pointer/tour_pointer\").TourPointer} pointer\n     * @param {Function} onTourEnd\n     */\n    start(pointer, onTourEnd) {\n        this.pointer = pointer;\n        this.debouncedToggleOpen = debounce(this.pointer.showContent, 50, true);\n        this.onTourEnd = onTourEnd;\n        this.observer = new MacroMutationObserver(() => this._onMutation());\n        this.observer.observe(document.body);\n        this.currentActionIndex = tourState.getCurrentIndex();\n        this.play();\n    }\n\n    backward() {\n        let tempIndex = this.currentActionIndex;\n        let tempAction,\n            tempAnchors = [];\n        while (!tempAnchors.length && tempIndex >= 0) {\n            tempIndex--;\n            tempAction = this.actions.at(tempIndex);\n            if (!tempAction.step.active) {\n                continue;\n            }\n            tempAnchors = tempAction && this.findTriggers(tempAction.anchor);\n        }\n\n        if (tempIndex >= 0) {\n            this.currentActionIndex = tempIndex;\n            this.play();\n        }\n    }\n\n    /**\n     * @returns {HTMLElement[]}\n     */\n    findTriggers(anchor) {\n        if (!anchor) {\n            anchor = this.currentAction.anchor;\n        }\n\n        return anchor\n            .split(/,\\s*(?![^(]*\\))/)\n            .map((part) => hoot.queryFirst(part, { visible: true }))\n            .filter((el) => !!el)\n            .map((el) => this.getAnchorEl(el, this.currentAction.event))\n            .filter((el) => !!el);\n    }\n\n    play() {\n        this.removeListeners();\n        if (this.currentActionIndex === this.actions.length) {\n            this.observer.disconnect();\n            this.onTourEnd();\n            return;\n        }\n\n        this.currentAction = this.actions.at(this.currentActionIndex);\n\n        if (!this.currentAction.step.active || this.currentAction.event === \"warn\") {\n            if (this.currentAction.event === \"warn\") {\n                console.warn(`Step '${this.currentAction.anchor}' ignored.`);\n            }\n            this.currentActionIndex++;\n            this.play();\n            return;\n        }\n\n        console.log(this.currentAction.event, this.currentAction.anchor);\n\n        tourState.setCurrentIndex(this.currentActionIndex);\n        this.anchorEls = this.findTriggers();\n        this.setActionListeners();\n        this.updatePointer();\n    }\n\n    updatePointer() {\n        if (this.anchorEls.length) {\n            this.pointer.pointTo(\n                this.anchorEls[0],\n                this.currentAction.pointerInfo,\n                this.currentAction.event === \"drop\"\n            );\n            this.pointer.setState({\n                onMouseEnter: () => this.debouncedToggleOpen(true),\n                onMouseLeave: () => this.debouncedToggleOpen(false),\n            });\n        }\n    }\n\n    setActionListeners() {\n        const cleanups = this.anchorEls.flatMap((anchorEl, index) => {\n            const toListen = {\n                anchorEl,\n                consumeEvents: this.getConsumeEventType(anchorEl, this.currentAction.event),\n                onConsume: () => {\n                    this.pointer.hide();\n                    this.currentActionIndex++;\n                    this.play();\n                },\n                onError: () => {\n                    if (this.currentAction.event === \"drop\") {\n                        this.pointer.hide();\n                        this.currentActionIndex--;\n                        this.play();\n                    }\n                },\n            };\n            if (index === 0) {\n                return this.setupListeners({\n                    ...toListen,\n                    onMouseEnter: () => this.pointer.showContent(true),\n                    onMouseLeave: () => this.pointer.showContent(false),\n                    onScroll: () => this.updatePointer(),\n                });\n            } else {\n                return this.setupListeners({\n                    ...toListen,\n                    onScroll: () => {},\n                });\n            }\n        });\n        this.removeListeners = () => {\n            this.anchorEls = [];\n            while (cleanups.length) {\n                cleanups.pop()();\n            }\n        };\n    }\n\n    /**\n     * @param {HTMLElement} params.anchorEl\n     * @param {import(\"./tour_utils\").ConsumeEvent[]} params.consumeEvents\n     * @param {() => void} params.onMouseEnter\n     * @param {() => void} params.onMouseLeave\n     * @param {(ev: Event) => any} params.onScroll\n     * @param {(ev: Event) => any} params.onConsume\n     * @param {() => any} params.onError\n     */\n    setupListeners({\n        anchorEl,\n        consumeEvents,\n        onMouseEnter,\n        onMouseLeave,\n        onScroll,\n        onConsume,\n        onError = () => {},\n    }) {\n        consumeEvents = consumeEvents.map((c) => ({\n            target: c.target,\n            type: c.name,\n            listener: function (ev) {\n                if (!c.conditional || c.conditional(ev)) {\n                    onConsume();\n                } else {\n                    onError();\n                }\n            },\n        }));\n\n        for (const consume of consumeEvents) {\n            consume.target.addEventListener(consume.type, consume.listener, true);\n        }\n        anchorEl.addEventListener(\"mouseenter\", onMouseEnter);\n        anchorEl.addEventListener(\"mouseleave\", onMouseLeave);\n\n        const cleanups = [\n            () => {\n                for (const consume of consumeEvents) {\n                    consume.target.removeEventListener(consume.type, consume.listener, true);\n                }\n                anchorEl.removeEventListener(\"mouseenter\", onMouseEnter);\n                anchorEl.removeEventListener(\"mouseleave\", onMouseLeave);\n            },\n        ];\n\n        const scrollEl = getScrollParent(anchorEl);\n        if (scrollEl) {\n            const debouncedOnScroll = debounce(onScroll, 50);\n            scrollEl.addEventListener(\"scroll\", debouncedOnScroll);\n            cleanups.push(() => scrollEl.removeEventListener(\"scroll\", debouncedOnScroll));\n        }\n\n        return cleanups;\n    }\n\n    /**\n     *\n     * @param {import(\"./tour_service\").TourStep} step\n     * @returns {{\n     *  event: string,\n     *  anchor: string,\n     *  pointerInfo: { tooltipPosition: string?, content: string? },\n     * }[]}\n     */\n    getSubActions(step) {\n        const actions = [];\n        if (!step.run || typeof step.run === \"function\") {\n            actions.push({\n                step,\n                event: \"warn\",\n                anchor: step.trigger,\n            });\n            return actions;\n        }\n\n        for (const todo of step.run.split(\"&&\")) {\n            const m = String(todo)\n                .trim()\n                .match(/^(?<action>\\w*) *\\(? *(?<arguments>.*?)\\)?$/);\n\n            let action = m.groups?.action;\n            const anchor = m.groups?.arguments || step.trigger;\n            const pointerInfo = {\n                content: step.content,\n                tooltipPosition: step.tooltipPosition,\n            };\n\n            if (action === \"drag_and_drop\") {\n                actions.push({\n                    step,\n                    event: \"drag\",\n                    anchor: step.trigger,\n                    pointerInfo,\n                });\n                action = \"drop\";\n            }\n\n            actions.push({\n                step,\n                event: action,\n                anchor: action === \"edit\" ? step.trigger : anchor,\n                pointerInfo,\n            });\n        }\n\n        return actions;\n    }\n\n    /**\n     * @param {HTMLElement} [element]\n     * @param {string} [runCommand]\n     * @returns {ConsumeEvent[]}\n     */\n    getConsumeEventType(element, runCommand) {\n        const consumeEvents = [];\n        if (runCommand === \"click\") {\n            consumeEvents.push({\n                name: \"click\",\n                target: element,\n            });\n\n            // Click on a field widget with an autocomplete should be also completed with a selection though Enter or Tab\n            // This case is for the steps that click on field_widget\n            if (element.querySelector(\".o-autocomplete--input\")) {\n                consumeEvents.push({\n                    name: \"keydown\",\n                    target: element.querySelector(\".o-autocomplete--input\"),\n                    conditional: (ev) =>\n                        [\"Tab\", \"Enter\"].includes(ev.key) &&\n                        ev.target.parentElement.querySelector(\n                            \".o-autocomplete--dropdown-item .ui-state-active\"\n                        ),\n                });\n            }\n\n            // Click on an element of a dropdown should be also completed with a selection though Enter or Tab\n            // This case is for the steps that click on a dropdown-item\n            if (element.closest(\".o-autocomplete--dropdown-menu\")) {\n                consumeEvents.push({\n                    name: \"keydown\",\n                    target: element.closest(\".o-autocomplete\").querySelector(\"input\"),\n                    conditional: (ev) => [\"Tab\", \"Enter\"].includes(ev.key),\n                });\n            }\n\n            // Press enter on a button do the same as a click\n            if (element.tagName === \"BUTTON\") {\n                consumeEvents.push({\n                    name: \"keydown\",\n                    target: element,\n                    conditional: (ev) => ev.key === \"Enter\",\n                });\n\n                // Pressing enter in the input group does the same as clicking on the button\n                if (element.closest(\".input-group\")) {\n                    for (const inputEl of element.parentElement.querySelectorAll(\"input\")) {\n                        consumeEvents.push({\n                            name: \"keydown\",\n                            target: inputEl,\n                            conditional: (ev) => ev.key === \"Enter\",\n                        });\n                    }\n                }\n            }\n        }\n\n        if ([\"fill\", \"edit\"].includes(runCommand)) {\n            if (\n                utils.isSmall() &&\n                element.closest(\".o_field_widget\")?.matches(\".o_field_many2one, .o_field_many2many\")\n            ) {\n                consumeEvents.push({\n                    name: \"click\",\n                    target: element,\n                });\n            } else {\n                consumeEvents.push({\n                    name: \"input\",\n                    target: element,\n                });\n            }\n        }\n\n        // Drag & drop run command\n        if (runCommand === \"drag\") {\n            consumeEvents.push({\n                name: \"pointerdown\",\n                target: element,\n            });\n        }\n\n        if (runCommand === \"drop\") {\n            consumeEvents.push({\n                name: \"pointerup\",\n                target: document,\n                conditional: (ev) =>\n                    element.ownerDocument\n                        .elementsFromPoint(ev.clientX, ev.clientY)\n                        .includes(element),\n            });\n        }\n\n        return consumeEvents;\n    }\n\n    /**\n     * Returns the element that will be used in listening to the `consumeEvent`.\n     * @param {HTMLElement} el\n     * @param {string} consumeEvent\n     */\n    getAnchorEl(el, consumeEvent) {\n        if (consumeEvent === \"drag\") {\n            // jQuery-ui draggable triggers 'drag' events on the .ui-draggable element,\n            // but the tip is attached to the .ui-draggable-handle element which may\n            // be one of its children (or the element itself)\n            return el.closest(\".ui-draggable, .o_draggable, .o_we_draggable, .o-draggable\");\n        }\n\n        if (consumeEvent === \"input\" && ![\"textarea\", \"input\"].includes(el.tagName.toLowerCase())) {\n            return el.closest(\"[contenteditable='true']\");\n        }\n        if (consumeEvent === \"sort\") {\n            // when an element is dragged inside a sortable container (with classname\n            // 'ui-sortable'), jQuery triggers the 'sort' event on the container\n            return el.closest(\".ui-sortable, .o_sortable\");\n        }\n        return el;\n    }\n\n    _onMutation() {\n        if (this.currentAction) {\n            const tempAnchors = this.findTriggers();\n            if (\n                tempAnchors.length &&\n                (tempAnchors.some((a) => !this.anchorEls.includes(a)) ||\n                    this.anchorEls.some((a) => !tempAnchors.includes(a)))\n            ) {\n                this.removeListeners();\n                this.anchorEls = tempAnchors;\n                this.setActionListeners();\n            } else if (!tempAnchors.length && this.anchorEls.length) {\n                this.pointer.hide();\n                if (!hoot.queryFirst(\".o_home_menu\", { visible: true })) {\n                    this.backward();\n                }\n                return;\n            }\n            this.updatePointer();\n        }\n    }\n}\n", "/** @odoo-module **/\n\nimport { reactive } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { TourPointer } from \"@web_tour/tour_pointer/tour_pointer\";\nimport { getScrollParent } from \"./tour_utils\";\n\n/**\n * @typedef {import(\"@web/core/position/position_hook\").Direction} Direction\n *\n * @typedef {\"in\" | \"out-below\" | \"out-above\" | \"unknown\"} IntersectionPosition\n *\n * @typedef {ReturnType<createPointerState>[\"methods\"]} TourPointerMethods\n *\n * @typedef TourPointerState\n * @property {HTMLElement} [anchor]\n * @property {string} [content]\n * @property {boolean} [isOpen]\n * @property {() => {}} [onClick]\n * @property {() => {}} [onMouseEnter]\n * @property {() => {}} [onMouseLeave]\n * @property {boolean} isVisible\n * @property {boolean} isZone\n * @property {Direction} position\n * @property {number} rev\n *\n * @typedef {import(\"./tour_service\").TourStep} TourStep\n */\n\nclass Intersection {\n    constructor() {\n        /** @type {Element | null} */\n        this.currentTarget = null;\n        this.rootBounds = null;\n        /** @type {IntersectionPosition} */\n        this._targetPosition = \"unknown\";\n        this._observer = new IntersectionObserver((observations) =>\n            this._handleObservations(observations)\n        );\n    }\n\n    /** @type {IntersectionObserverCallback} */\n    _handleObservations(observations) {\n        if (observations.length < 1) {\n            return;\n        }\n        const observation = observations[observations.length - 1];\n        this.rootBounds = observation.rootBounds;\n        if (this.rootBounds && this.currentTarget) {\n            if (observation.isIntersecting) {\n                this._targetPosition = \"in\";\n            } else {\n                const targetBounds = this.currentTarget.getBoundingClientRect();\n                if (targetBounds.bottom < this.rootBounds.height / 2) {\n                    this._targetPosition = \"out-above\";\n                } else if (targetBounds.top > this.rootBounds.height / 2) {\n                    this._targetPosition = \"out-below\";\n                }\n            }\n        } else {\n            this._targetPosition = \"unknown\";\n        }\n    }\n\n    get targetPosition() {\n        if (!this.rootBounds) {\n            return this.currentTarget ? \"in\" : \"unknown\";\n        } else {\n            return this._targetPosition;\n        }\n    }\n\n    /**\n     * @param {Element} newTarget\n     */\n    setTarget(newTarget) {\n        if (this.currentTarget !== newTarget) {\n            if (this.currentTarget) {\n                this._observer.unobserve(this.currentTarget);\n            }\n            if (newTarget) {\n                this._observer.observe(newTarget);\n            }\n            this.currentTarget = newTarget;\n        }\n    }\n\n    stop() {\n        this._observer.disconnect();\n    }\n}\n\nexport function createPointerState() {\n    /**\n     * @param {Partial<TourPointerState>} newState\n     */\n    const setState = (newState) => {\n        Object.assign(state, newState);\n    };\n\n    /**\n     * @param {TourStep} step\n     * @param {HTMLElement} [anchor]\n     * @param {boolean} [isZone] will border de zone. e.g.: a dropzone\n     */\n    const pointTo = (anchor, step, isZone) => {\n        intersection.setTarget(anchor);\n        if (anchor) {\n            let { tooltipPosition, content } = step;\n            switch (intersection.targetPosition) {\n                case \"unknown\": {\n                    // Do nothing for unknown target position.\n                    break;\n                }\n                case \"in\": {\n                    if (document.body.contains(floatingAnchor)) {\n                        floatingAnchor.remove();\n                    }\n                    setState({\n                        anchor,\n                        content,\n                        isZone,\n                        onClick: null,\n                        position: tooltipPosition,\n                        isVisible: true,\n                    });\n                    break;\n                }\n                default: {\n                    const onClick = () => {\n                        anchor.scrollIntoView({ behavior: \"smooth\", block: \"nearest\" });\n                        hide();\n                    };\n\n                    const scrollParent = getScrollParent(anchor);\n                    if (!scrollParent) {\n                        setState({\n                            anchor,\n                            content,\n                            isZone,\n                            onClick: null,\n                            position: tooltipPosition,\n                            isVisible: true,\n                        });\n                        return;\n                    }\n                    let { x, y, width, height } = scrollParent.getBoundingClientRect();\n\n                    // If the scrolling element is within an iframe the offsets\n                    // must be computed taking into account the iframe.\n                    const iframeEl = scrollParent.ownerDocument.defaultView.frameElement;\n                    if (iframeEl) {\n                        const iframeOffset = iframeEl.getBoundingClientRect();\n                        x += iframeOffset.x;\n                        y += iframeOffset.y;\n                    }\n                    floatingAnchor.style.left = `${x + width / 2}px`;\n                    if (intersection.targetPosition === \"out-below\") {\n                        tooltipPosition = \"top\";\n                        content = _t(\"Scroll down to reach the next step.\");\n                        floatingAnchor.style.top = `${y + height - TourPointer.height}px`;\n                    } else if (intersection.targetPosition === \"out-above\") {\n                        tooltipPosition = \"bottom\";\n                        content = _t(\"Scroll up to reach the next step.\");\n                        floatingAnchor.style.top = `${y + TourPointer.height}px`;\n                    }\n                    if (!document.contains(floatingAnchor)) {\n                        document.body.appendChild(floatingAnchor);\n                    }\n                    setState({\n                        anchor: floatingAnchor,\n                        content,\n                        onClick,\n                        position: tooltipPosition,\n                        isZone,\n                        isVisible: true,\n                    });\n                }\n            }\n        } else {\n            hide();\n        }\n    };\n\n    function hide() {\n        setState({ content: \"\", isVisible: false, isOpen: false });\n    }\n\n    function showContent(isOpen) {\n        setState({ isOpen });\n    }\n\n    function destroy() {\n        intersection.stop();\n        if (document.body.contains(floatingAnchor)) {\n            floatingAnchor.remove();\n        }\n    }\n\n    /** @type {TourPointerState} */\n    const state = reactive({});\n    const intersection = new Intersection();\n    const floatingAnchor = document.createElement(\"div\");\n    floatingAnchor.className = \"position-fixed\";\n\n    return { state, setState, showContent, pointTo, hide, destroy };\n}\n", "import { useService } from \"@web/core/utils/hooks\";\nimport { Dropdown } from \"@web/core/dropdown/dropdown\";\nimport { DropdownItem } from \"@web/core/dropdown/dropdown_item\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { queryAll, queryFirst, queryOne } from \"@odoo/hoot-dom\";\nimport { Component, useState, useExternalListener } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { x2ManyCommands } from \"@web/core/orm_service\";\nimport { tourRecorderState } from \"./tour_recorder_state\";\n\nexport const TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY = \"tour_recorder_active\";\nconst PRECISE_IDENTIFIERS = [\"data-menu-xmlid\", \"name\", \"contenteditable\"];\nconst ODOO_CLASS_REGEX = /^oe?(-|_)[\\w-]+$/;\nconst VALIDATING_KEYS = [\"Enter\", \"Tab\"];\n\n/**\n * @param {EventTarget[]} paths composedPath of an click event\n * @returns {string}\n */\nconst getShortestSelector = (paths) => {\n    paths.reverse();\n    let filteredPath = [];\n    let hasOdooClass = false;\n    for (\n        let currentElem = paths.pop();\n        (currentElem && queryAll(filteredPath.join(\" > \")).length !== 1) || !hasOdooClass;\n        currentElem = paths.pop()\n    ) {\n        if (currentElem.parentElement.contentEditable === \"true\") {\n            continue;\n        }\n\n        let currentPredicate = currentElem.tagName.toLowerCase();\n        const odooClass = [...currentElem.classList].find((c) => c.match(ODOO_CLASS_REGEX));\n        if (odooClass) {\n            currentPredicate = `.${odooClass}`;\n            hasOdooClass = true;\n        }\n\n        // If we are inside a link or button the previous elements, like <i></i>, <span></span>, etc., can be removed\n        if ([\"BUTTON\", \"A\"].includes(currentElem.tagName)) {\n            filteredPath = [];\n        }\n\n        for (const identifier of PRECISE_IDENTIFIERS) {\n            const identifierValue = currentElem.getAttribute(identifier);\n            if (identifierValue) {\n                currentPredicate += `[${identifier}='${CSS.escape(identifierValue)}']`;\n            }\n        }\n\n        const siblingNodes = queryAll(\":scope > \" + currentPredicate, {\n            root: currentElem.parentElement,\n        });\n        if (siblingNodes.length > 1) {\n            currentPredicate += `:nth-child(${\n                [...currentElem.parentElement.children].indexOf(currentElem) + 1\n            })`;\n        }\n\n        filteredPath.unshift(currentPredicate);\n    }\n\n    if (filteredPath.length > 2) {\n        return reducePath(filteredPath);\n    }\n\n    return filteredPath.join(\" > \");\n};\n\n/**\n * @param {string[]} paths\n * @returns {string}\n */\nconst reducePath = (paths) => {\n    const numberOfElement = paths.length - 2;\n    let currentElement = \"\";\n    let hasReduced = false;\n    let path = paths.shift();\n    for (let i = 0; i < numberOfElement; i++) {\n        currentElement = paths.shift();\n        if (queryAll(`${path} ${paths.join(\" > \")}`).length === 1) {\n            hasReduced = true;\n        } else {\n            path += `${hasReduced ? \" \" : \" > \"}${currentElement}`;\n            hasReduced = false;\n        }\n    }\n    path += `${hasReduced ? \" \" : \" > \"}${paths.shift()}`;\n    return path;\n};\n\nexport class TourRecorder extends Component {\n    static template = \"web_tour.TourRecorder\";\n    static components = { Dropdown, DropdownItem };\n    static props = {\n        onClose: { type: Function },\n    };\n    static defaultState = {\n        recording: false,\n        url: \"\",\n        editedElement: undefined,\n        tourName: \"\",\n    };\n\n    setup() {\n        this.originClickEvent = false;\n        this.notification = useService(\"notification\");\n        this.orm = useService(\"orm\");\n        this.state = useState({\n            ...TourRecorder.defaultState,\n            steps: [],\n        });\n\n        this.state.steps = tourRecorderState.getCurrentTourRecorder();\n        this.state.recording = tourRecorderState.isRecording() === \"1\";\n        useExternalListener(document, \"pointerdown\", this.setStartingEvent, { capture: true });\n        useExternalListener(document, \"pointerup\", this.recordClickEvent, { capture: true });\n        useExternalListener(document, \"keydown\", this.recordConfirmationKeyboardEvent, {\n            capture: true,\n        });\n        useExternalListener(document, \"keyup\", this.recordKeyboardEvent, { capture: true });\n    }\n\n    /**\n     * @param {PointerEvent} ev\n     */\n    setStartingEvent(ev) {\n        if (!this.state.recording || ev.target.closest(\".o_tour_recorder\")) {\n            return;\n        }\n        this.originClickEvent = ev.composedPath().filter((p) => p instanceof Element);\n    }\n\n    /**\n     * @param {PointerEvent} ev\n     */\n    recordClickEvent(ev) {\n        if (!this.state.recording || ev.target.closest(\".o_tour_recorder\")) {\n            return;\n        }\n        const pathElements = ev.composedPath().filter((p) => p instanceof Element);\n        this.addTourStep([...pathElements]);\n\n        const lastStepInput = this.state.steps.at(-1);\n        // Check that pointerdown and pointerup paths are different to know if it's a drag&drop or a click\n        if (\n            JSON.stringify(pathElements.map((e) => e.tagName)) !==\n            JSON.stringify(this.originClickEvent.map((e) => e.tagName))\n        ) {\n            lastStepInput.run = `drag_and_drop ${lastStepInput.trigger}`;\n            lastStepInput.trigger = getShortestSelector(this.originClickEvent);\n        } else {\n            const lastStepInput = this.state.steps.at(-1);\n            lastStepInput.run = \"click\";\n        }\n\n        tourRecorderState.setCurrentTourRecorder(this.state.steps);\n    }\n\n    /**\n     * @param {KeyboardEvent} ev\n     */\n    recordConfirmationKeyboardEvent(ev) {\n        if (\n            !this.state.recording ||\n            !this.state.editedElement ||\n            ev.target.closest(\".o_tour_recorder\")\n        ) {\n            return;\n        }\n\n        if (\n            [...this.state.editedElement.classList].includes(\"o-autocomplete--input\") &&\n            VALIDATING_KEYS.includes(ev.key)\n        ) {\n            const selectedRow = queryFirst(\".ui-state-active\", {\n                root: this.state.editedElement.parentElement,\n            });\n            this.state.steps.push({\n                trigger: `.o-autocomplete--dropdown-item > a:contains('${selectedRow.textContent}'), .fa-circle-o-notch`,\n                run: \"click\",\n            });\n            this.state.editedElement = undefined;\n        }\n        tourRecorderState.setCurrentTourRecorder(this.state.steps);\n    }\n\n    /**\n     * @param {KeyboardEvent} ev\n     */\n    recordKeyboardEvent(ev) {\n        if (\n            !this.state.recording ||\n            VALIDATING_KEYS.includes(ev.key) ||\n            ev.target.closest(\".o_tour_recorder\")\n        ) {\n            return;\n        }\n\n        if (!this.state.editedElement) {\n            if (\n                ev.target.matches(\n                    \"input:not(:disabled), textarea:not(:disabled), [contenteditable=true]\"\n                )\n            ) {\n                this.state.editedElement = ev.target;\n                this.state.steps.push({\n                    trigger: getShortestSelector(ev.composedPath()),\n                });\n            } else {\n                return;\n            }\n        }\n\n        if (!this.state.editedElement) {\n            return;\n        }\n\n        const lastStep = this.state.steps.at(-1);\n        if (this.state.editedElement.contentEditable === \"true\") {\n            lastStep.run = `editor ${this.state.editedElement.textContent}`;\n        } else {\n            lastStep.run = `edit ${this.state.editedElement.value}`;\n        }\n        tourRecorderState.setCurrentTourRecorder(this.state.steps);\n    }\n\n    toggleRecording() {\n        this.state.recording = !this.state.recording;\n        tourRecorderState.setIsRecording(this.state.recording);\n        this.state.editedElement = undefined;\n        if (this.state.recording && !this.state.url) {\n            this.state.url = browser.location.pathname + browser.location.search;\n        }\n    }\n\n    async saveTour() {\n        const newTour = {\n            name: this.state.tourName.replaceAll(\" \", \"_\"),\n            url: this.state.url,\n            step_ids: this.state.steps.map((s) => x2ManyCommands.create(undefined, s)),\n            custom: true,\n        };\n\n        const result = await this.orm.create(\"web_tour.tour\", [newTour]);\n        if (result) {\n            this.notification.add(_t(\"Custom tour '%s' has been added.\", newTour.name), {\n                type: \"success\",\n            });\n            this.resetTourRecorderState();\n        } else {\n            this.notification.add(_t(\"Custom tour '%s' couldn't be saved!\", newTour.name), {\n                type: \"danger\",\n            });\n        }\n    }\n\n    resetTourRecorderState() {\n        Object.assign(this.state, { ...TourRecorder.defaultState, steps: [] });\n        tourRecorderState.clear();\n    }\n\n    /**\n     * @param {Element[]} path\n     */\n    addTourStep(path) {\n        const shortestPath = getShortestSelector(path);\n        const target = queryOne(shortestPath);\n        this.state.editedElement =\n            target.matches(\n                \"input:not(:disabled), textarea:not(:disabled), [contenteditable=true]\"\n            ) && target;\n        this.state.steps.push({\n            trigger: shortestPath,\n        });\n    }\n}\n", "import { browser } from \"@web/core/browser/browser\";\n\nconst CURRENT_TOUR_RECORDER_LOCAL_STORAGE = \"current_tour_recorder\";\nconst CURRENT_TOUR_RECORDER_RECORD_LOCAL_STORAGE = \"current_tour_recorder.record\";\n\n/**\n * Wrapper around localStorage for persistence of the current recording.\n * Useful for resuming recording when the page refreshed.\n */\nexport const tourRecorderState = {\n    isRecording() {\n        return browser.localStorage.getItem(CURRENT_TOUR_RECORDER_RECORD_LOCAL_STORAGE) || \"0\";\n    },\n    setIsRecording(isRecording) {\n        browser.localStorage.setItem(\n            CURRENT_TOUR_RECORDER_RECORD_LOCAL_STORAGE,\n            isRecording ? \"1\" : \"0\"\n        );\n    },\n    setCurrentTourRecorder(tour) {\n        tour = JSON.stringify(tour);\n        browser.localStorage.setItem(CURRENT_TOUR_RECORDER_LOCAL_STORAGE, tour);\n    },\n    getCurrentTourRecorder() {\n        const tour = browser.localStorage.getItem(CURRENT_TOUR_RECORDER_LOCAL_STORAGE) || \"[]\";\n        return JSON.parse(tour);\n    },\n    clear() {\n        browser.localStorage.removeItem(CURRENT_TOUR_RECORDER_LOCAL_STORAGE);\n        browser.localStorage.removeItem(CURRENT_TOUR_RECORDER_RECORD_LOCAL_STORAGE);\n    },\n};\n", "/** @odoo-module **/\n\nimport { markup, whenReady, validate } from \"@odoo/owl\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { registry } from \"@web/core/registry\";\nimport { session } from \"@web/session\";\nimport { TourPointer } from \"../tour_pointer/tour_pointer\";\nimport { createPointerState } from \"./tour_pointer_state\";\nimport { tourState } from \"./tour_state\";\nimport { TourInteractive } from \"./tour_interactive\";\nimport { TourAutomatic } from \"./tour_automatic\";\nimport { callWithUnloadCheck } from \"./tour_utils\";\nimport {\n    TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY,\n    TourRecorder,\n} from \"@web_tour/tour_service/tour_recorder/tour_recorder\";\nimport { redirect } from \"@web/core/utils/urls\";\nimport { tourRecorderState } from \"@web_tour/tour_service/tour_recorder/tour_recorder_state\";\n\nconst StepSchema = {\n    id: { type: [String], optional: true },\n    content: { type: [String, Object], optional: true }, //allow object(_t && markup)\n    debugHelp: { type: String, optional: true },\n    isActive: { type: Array, element: String, optional: true },\n    run: { type: [String, Function, Boolean], optional: true },\n    timeout: {\n        optional: true,\n        validate(value) {\n            return value >= 0 && value <= 60000;\n        },\n    },\n    tooltipPosition: {\n        optional: true,\n        validate(value) {\n            return [\"top\", \"bottom\", \"left\", \"right\"].includes(value);\n        },\n    },\n    trigger: { type: String },\n    //ONLY IN DEBUG MODE\n    pause: { type: Boolean, optional: true },\n    break: { type: Boolean, optional: true },\n};\n\nconst TourSchema = {\n    checkDelay: { type: Number, optional: true },\n    name: { type: String, optional: true },\n    steps: Function,\n    url: { type: String, optional: true },\n    wait_for: { type: [Function, Object], optional: true },\n};\n\nregistry.category(\"web_tour.tours\").addValidation(TourSchema);\nconst userMenuRegistry = registry.category(\"user_menuitems\");\n\nexport const tourService = {\n    // localization dependency to make sure translations used by tours are loaded\n    dependencies: [\"orm\", \"effect\", \"overlay\", \"localization\"],\n    start: async (_env, { orm, effect, overlay }) => {\n        await whenReady();\n        let toursEnabled = session?.tour_enabled;\n        const tourRegistry = registry.category(\"web_tour.tours\");\n        const pointer = createPointerState();\n        pointer.stop = () => {};\n\n        userMenuRegistry.add(\"web_tour.tour_enabled\", () => ({\n            type: \"switch\",\n            id: \"web_tour.tour_enabled\",\n            description: _t(\"Onboarding\"),\n            callback: async () => {\n                tourState.clear();\n                toursEnabled = await orm.call(\"res.users\", \"switch_tour_enabled\", [!toursEnabled]);\n                browser.location.reload();\n            },\n            isChecked: toursEnabled,\n            sequence: 30,\n        }));\n\n        function getTourFromRegistry(tourName) {\n            if (!tourRegistry.contains(tourName)) {\n                return;\n            }\n            const tour = tourRegistry.get(tourName);\n            return {\n                ...tour,\n                steps: tour.steps(),\n                name: tourName,\n                wait_for: tour.wait_for || Promise.resolve(),\n            };\n        }\n\n        async function getTourFromDB(tourName) {\n            const tour = await orm.call(\"web_tour.tour\", \"get_tour_json_by_name\", [tourName]);\n            if (!tour) {\n                throw new Error(`Tour '${tourName}' is not found in the database.`);\n            }\n\n            if (!tour.steps.length && tourRegistry.contains(tour.name)) {\n                tour.steps = tourRegistry.get(tour.name).steps();\n            }\n\n            return tour;\n        }\n\n        function validateStep(step) {\n            try {\n                validate(step, StepSchema);\n            } catch (error) {\n                console.error(\n                    `Error in schema for TourStep ${JSON.stringify(step, null, 4)}\\n${\n                        error.message\n                    }`\n                );\n            }\n        }\n\n        async function startTour(tourName, options = {}) {\n            pointer.stop();\n            const tourFromRegistry = getTourFromRegistry(tourName);\n\n            if (!tourFromRegistry && !options.fromDB) {\n                // Sometime tours are not loaded depending on the modules.\n                // For example, point_of_sale do not load all tours assets.\n                return;\n            }\n\n            const tour = options.fromDB ? { name: tourName, url: options.url } : tourFromRegistry;\n            if (!session.is_public && !toursEnabled && options.mode === \"manual\") {\n                toursEnabled = await orm.call(\"res.users\", \"switch_tour_enabled\", [!toursEnabled]);\n            }\n\n            let tourConfig = {\n                delayToCheckUndeterminisms: 0,\n                stepDelay: 0,\n                keepWatchBrowser: false,\n                mode: \"auto\",\n                showPointerDuration: 0,\n                debug: false,\n                redirect: true,\n            };\n\n            tourConfig = Object.assign(tourConfig, options);\n            tourState.setCurrentConfig(tourConfig);\n            tourState.setCurrentTour(tour.name);\n            tourState.setCurrentIndex(0);\n\n            const willUnload = callWithUnloadCheck(() => {\n                if (tour.url && tourConfig.startUrl != tour.url && tourConfig.redirect) {\n                    redirect(tour.url);\n                }\n            });\n            if (!willUnload) {\n                resumeTour();\n            }\n        }\n\n        async function resumeTour() {\n            const tourName = tourState.getCurrentTour();\n            const tourConfig = tourState.getCurrentConfig();\n\n            let tour = getTourFromRegistry(tourName);\n            if (tourConfig.fromDB) {\n                tour = await getTourFromDB(tourName);\n            }\n            if (!tour) {\n                return;\n            }\n\n            tour.steps.forEach((step) => validateStep(step));\n            pointer.stop = overlay.add(\n                TourPointer,\n                {\n                    pointerState: pointer.state,\n                    bounce: !(tourConfig.mode === \"auto\" && tourConfig.keepWatchBrowser),\n                },\n                {\n                    sequence: 1100, // sequence based on bootstrap z-index values.\n                }\n            );\n\n            if (tourConfig.mode === \"auto\") {\n                new TourAutomatic(tour).start(pointer);\n            } else {\n                new TourInteractive(tour).start(pointer, async () => {\n                    pointer.stop();\n                    tourState.clear();\n                    browser.console.log(\"tour succeeded\");\n                    let message = tourConfig.rainbowManMessage || tour.rainbowManMessage;\n                    if (message) {\n                        message = window.DOMPurify.sanitize(tourConfig.rainbowManMessage);\n                        effect.add({\n                            type: \"rainbow_man\",\n                            message: markup(message),\n                        });\n                    }\n\n                    const nextTour = await orm.call(\"web_tour.tour\", \"consume\", [tour.name]);\n                    if (nextTour) {\n                        startTour(nextTour.name, {\n                            mode: \"manual\",\n                            redirect: false,\n                            rainbowManMessage: nextTour.rainbowManMessage,\n                        });\n                    }\n                });\n            }\n        }\n\n        function startTourRecorder() {\n            if (!browser.localStorage.getItem(TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY)) {\n                const remove = overlay.add(\n                    TourRecorder,\n                    {\n                        onClose: () => {\n                            remove();\n                            browser.localStorage.removeItem(TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY);\n                            tourRecorderState.clear();\n                        },\n                    },\n                    { sequence: 99999 }\n                );\n            }\n            browser.localStorage.setItem(TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY, \"1\");\n        }\n\n        if (!window.frameElement) {\n            const paramsTourName = new URLSearchParams(browser.location.search).get(\"tour\");\n            if (paramsTourName) {\n                startTour(paramsTourName, { mode: \"manual\", fromDB: true });\n            }\n\n            if (tourState.getCurrentTour()) {\n                if (tourState.getCurrentConfig().mode === \"auto\" || toursEnabled) {\n                    resumeTour();\n                } else {\n                    tourState.clear();\n                }\n            } else if (session.current_tour) {\n                startTour(session.current_tour.name, {\n                    mode: \"manual\",\n                    redirect: false,\n                    rainbowManMessage: session.current_tour.rainbowManMessage,\n                });\n            }\n\n            if (\n                browser.localStorage.getItem(TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY) &&\n                !session.is_public\n            ) {\n                const remove = overlay.add(\n                    TourRecorder,\n                    {\n                        onClose: () => {\n                            remove();\n                            browser.localStorage.removeItem(TOUR_RECORDER_ACTIVE_LOCAL_STORAGE_KEY);\n                            tourRecorderState.clear();\n                        },\n                    },\n                    { sequence: 99999 }\n                );\n            }\n        }\n\n        odoo.startTour = startTour;\n        odoo.isTourReady = (tourName) => getTourFromRegistry(tourName).wait_for.then(() => true);\n\n        return {\n            startTour,\n            startTourRecorder,\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"tour_service\", tourService);\n", "/** @odoo-module **/\n\nimport { browser } from \"@web/core/browser/browser\";\n\nconst CURRENT_TOUR_LOCAL_STORAGE = \"current_tour\";\nconst CURRENT_TOUR_CONFIG_LOCAL_STORAGE = \"current_tour.config\";\nconst CURRENT_TOUR_INDEX_LOCAL_STORAGE = \"current_tour.index\";\nconst CURRENT_TOUR_ON_ERROR_LOCAL_STORAGE = \"current_tour.on_error\";\n\n/**\n * Wrapper around localStorage for persistence of the running tours.\n * Useful for resuming running tours when the page refreshed.\n */\nexport const tourState = {\n    getCurrentTour() {\n        return browser.localStorage.getItem(CURRENT_TOUR_LOCAL_STORAGE);\n    },\n    setCurrentTour(tourName) {\n        browser.localStorage.setItem(CURRENT_TOUR_LOCAL_STORAGE, tourName);\n    },\n    getCurrentIndex() {\n        const index = browser.localStorage.getItem(CURRENT_TOUR_INDEX_LOCAL_STORAGE, \"0\");\n        return parseInt(index, 10);\n    },\n    setCurrentIndex(index) {\n        browser.localStorage.setItem(CURRENT_TOUR_INDEX_LOCAL_STORAGE, index.toString());\n    },\n    getCurrentConfig() {\n        const config = browser.localStorage.getItem(CURRENT_TOUR_CONFIG_LOCAL_STORAGE, \"{}\");\n        return JSON.parse(config);\n    },\n    setCurrentConfig(config) {\n        config = JSON.stringify(config);\n        browser.localStorage.setItem(CURRENT_TOUR_CONFIG_LOCAL_STORAGE, config);\n    },\n    getCurrentTourOnError() {\n        return browser.localStorage.getItem(CURRENT_TOUR_ON_ERROR_LOCAL_STORAGE);\n    },\n    setCurrentTourOnError() {\n        browser.localStorage.setItem(CURRENT_TOUR_ON_ERROR_LOCAL_STORAGE, \"1\");\n    },\n    clear() {\n        browser.localStorage.removeItem(CURRENT_TOUR_ON_ERROR_LOCAL_STORAGE);\n        browser.localStorage.removeItem(CURRENT_TOUR_CONFIG_LOCAL_STORAGE);\n        browser.localStorage.removeItem(CURRENT_TOUR_INDEX_LOCAL_STORAGE);\n        browser.localStorage.removeItem(CURRENT_TOUR_LOCAL_STORAGE);\n    },\n};\n", "import { session } from \"@web/session\";\nimport { utils } from \"@web/core/ui/ui_service\";\nimport * as hoot from \"@odoo/hoot-dom\";\nimport { pick } from \"@web/core/utils/objects\";\n\n/**\n * @typedef TourStep\n * @property {\"enterprise\"|\"community\"|\"mobile\"|\"desktop\"|HootSelector[][]} isActive Active the step following {@link isActiveStep} filter\n * @property {string} [id]\n * @property {HootSelector} trigger The node on which the action will be executed.\n * @property {string} [content] Description of the step.\n * @property {\"top\" | \"botton\" | \"left\" | \"right\"} [position] The position where the UI helper is shown.\n * @property {RunCommand} [run] The action to perform when trigger conditions are verified.\n * @property {number} [timeout] By default, when the trigger node isn't found after 10000 milliseconds, it throws an error.\n * You can change this value to lengthen or shorten the time before the error occurs [ms].\n */\nexport class TourStep {\n    constructor(data, tour) {\n        Object.assign(this, data);\n        this.tour = tour;\n    }\n\n    /**\n     * Check if a step is active dependant on step.isActive property\n     * Note that when step.isActive is not defined, the step is active by default.\n     * When a step is not active, it's just skipped and the tour continues to the next step.\n     */\n    get active() {\n        this.checkHasTour();\n        const mode = this.tour.mode;\n        const isSmall = utils.isSmall();\n        const standardKeyWords = [\"enterprise\", \"community\", \"mobile\", \"desktop\", \"auto\", \"manual\"];\n        const isActiveArray = Array.isArray(this.isActive) ? this.isActive : [];\n        if (isActiveArray.length === 0) {\n            return true;\n        }\n        const selectors = isActiveArray.filter((key) => !standardKeyWords.includes(key));\n        if (selectors.length) {\n            // if one of selectors is not found, step is skipped\n            for (const selector of selectors) {\n                const el = hoot.queryFirst(selector);\n                if (!el) {\n                    return false;\n                }\n            }\n        }\n        const checkMode =\n            isActiveArray.includes(mode) ||\n            (!isActiveArray.includes(\"manual\") && !isActiveArray.includes(\"auto\"));\n        const edition =\n            (session.server_version_info || \"\").at(-1) === \"e\" ? \"enterprise\" : \"community\";\n        const checkEdition =\n            isActiveArray.includes(edition) ||\n            (!isActiveArray.includes(\"enterprise\") && !isActiveArray.includes(\"community\"));\n        const onlyForMobile = isActiveArray.includes(\"mobile\") && isSmall;\n        const onlyForDesktop = isActiveArray.includes(\"desktop\") && !isSmall;\n        const checkDevice =\n            onlyForMobile ||\n            onlyForDesktop ||\n            (!isActiveArray.includes(\"mobile\") && !isActiveArray.includes(\"desktop\"));\n        return checkEdition && checkDevice && checkMode;\n    }\n\n    checkHasTour() {\n        if (!this.tour) {\n            throw new Error(`TourStep instance must have a tour`);\n        }\n    }\n\n    get describeMe() {\n        this.checkHasTour();\n        return (\n            `[${this.index + 1}/${this.tour.steps.length}] Tour ${this.tour.name} \u2192 Step ` +\n            (this.content ? `${this.content} (trigger: ${this.trigger})` : this.trigger)\n        );\n    }\n\n    get stringify() {\n        return (\n            JSON.stringify(\n                pick(this, \"isActive\", \"content\", \"trigger\", \"run\", \"tooltipPosition\", \"timeout\"),\n                (_key, value) => {\n                    if (typeof value === \"function\") {\n                        return \"[function]\";\n                    } else {\n                        return value;\n                    }\n                },\n                2\n            ) + \",\"\n        );\n    }\n}\n", "import { tourState } from \"./tour_state\";\nimport * as hoot from \"@odoo/hoot-dom\";\nimport { callWithUnloadCheck } from \"./tour_utils\";\nimport { TourHelpers } from \"./tour_helpers\";\nimport { TourStep } from \"./tour_step\";\nimport { getTag } from \"@web/core/utils/xml\";\nimport { browser } from \"@web/core/browser/browser\";\n\nexport class TourStepAutomatic extends TourStep {\n    skipped = false;\n    error = \"\";\n    constructor(data, tour, index) {\n        super(data, tour);\n        this.index = index;\n        this.tourConfig = tourState.getCurrentConfig();\n    }\n\n    async checkForUndeterminisms() {\n        const delay = this.tourConfig.delayToCheckUndeterminisms;\n        if (delay > 0 && this.element) {\n            const snapshot = this.element.cloneNode(true);\n            return new Promise((resolve, reject) => {\n                browser.setTimeout(() => {\n                    if (this.element.isEqualNode(snapshot)) {\n                        resolve();\n                    } else {\n                        reject(\n                            new Error(\n                                [\n                                    ...this.describeWhyIFailed,\n                                    `UNDETERMINISM: two differents elements have been found in ${delay}ms for trigger ${this.trigger}`,\n                                ].join(\"\\n\")\n                            )\n                        );\n                    }\n                }, delay);\n            });\n        }\n    }\n\n    get describeWhyIFailed() {\n        const errors = [];\n        if (this.element) {\n            errors.push(`Element has been found.`);\n            if (this.isUIBlocked) {\n                errors.push(\"BUT: DOM is blocked by UI.\");\n            }\n            if (!this.elementIsInModal) {\n                errors.push(\n                    `BUT: It is not allowed to do action on an element that's below a modal.`\n                );\n            }\n            if (!this.elementIsEnabled) {\n                errors.push(\n                    `BUT: Element is not enabled. TIP: You can use :enable to wait the element is enabled before doing action on it.`\n                );\n            }\n        } else {\n            const checkElement = hoot.queryFirst(this.trigger);\n            if (checkElement) {\n                errors.push(`Element has been found.`);\n                errors.push(\n                    `BUT: Element is not visible. TIP: You can use :not(:visible) to force the search for an invisible element.`\n                );\n            } else {\n                errors.push(`Element (${this.trigger}) has not been found.`);\n            }\n        }\n        return errors;\n    }\n\n    /**\n     * When return true, macro stops.\n     * @returns {Boolean}\n     */\n    async doAction() {\n        let result = false;\n        if (!this.skipped) {\n            // TODO: Delegate the following routine to the `ACTION_HELPERS` in the macro module.\n            const actionHelper = new TourHelpers(this.element);\n\n            if (typeof this.run === \"function\") {\n                const willUnload = await callWithUnloadCheck(async () => {\n                    await this.run.call({ anchor: this.element }, actionHelper);\n                });\n                result = willUnload && \"will unload\";\n            } else if (typeof this.run === \"string\") {\n                for (const todo of this.run.split(\"&&\")) {\n                    const m = String(todo)\n                        .trim()\n                        .match(/^(?<action>\\w*) *\\(? *(?<arguments>.*?)\\)?$/);\n                    await actionHelper[m.groups?.action](m.groups?.arguments);\n                }\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Each time it returns false, tour engine wait for a mutation\n     * to retry to find the trigger.\n     * @returns {(HTMLElement|Boolean)}\n     */\n    findTrigger() {\n        if (!this.active) {\n            this.skipped = true;\n            return true;\n        }\n        const visible = !this.trigger.includes(\":visible\");\n        this.element = hoot.queryFirst(this.trigger, { visible });\n        return !this.isUIBlocked && this.elementIsEnabled && this.elementIsInModal\n            ? this.element\n            : false;\n    }\n\n    get isUIBlocked() {\n        return (\n            document.body.classList.contains(\"o_ui_blocked\") || document.querySelector(\".o_blockUI\")\n        );\n    }\n\n    get elementIsInModal() {\n        if (!this.element) {\n            return false;\n        }\n        if (this.hasAction) {\n            const overlays = hoot.queryFirst(\".popover, .o-we-command, .o_notification\");\n            const modal = hoot.queryFirst(\".modal:visible:not(.o_inactive_modal):last\");\n            if (modal && !overlays && !this.trigger.startsWith(\"body\")) {\n                return (\n                    modal.contains(hoot.getParentFrame(this.element)) ||\n                    modal.contains(this.element)\n                );\n            }\n        }\n        return true;\n    }\n\n    get elementIsEnabled() {\n        const isTag = (array) => array.includes(getTag(this.element, true));\n        if (!this.element) {\n            return false;\n        }\n        if (this.hasAction) {\n            if (isTag([\"input\", \"textarea\"])) {\n                return hoot.isEditable(this.element);\n            } else if (isTag([\"button\", \"select\"])) {\n                return !this.element.disabled;\n            }\n        }\n        return true;\n    }\n\n    get hasAction() {\n        return [\"string\", \"function\"].includes(typeof this.run) && !this.skipped;\n    }\n}\n", "/** @odoo-module **/\nimport * as hoot from \"@odoo/hoot-dom\";\nimport { _t } from \"@web/core/l10n/translation\";\n\n/**\n * Calls the given `func` then returns/resolves to `true`\n * if it will result to unloading of the page.\n * @param {(...args: any[]) => void} func\n * @param  {any[]} args\n * @returns {boolean | Promise<boolean>}\n */\nexport function callWithUnloadCheck(func, ...args) {\n    let willUnload = false;\n    const beforeunload = () => (willUnload = true);\n    window.addEventListener(\"beforeunload\", beforeunload);\n    const result = func(...args);\n    if (result instanceof Promise) {\n        return result.then(() => {\n            window.removeEventListener(\"beforeunload\", beforeunload);\n            return willUnload;\n        });\n    } else {\n        window.removeEventListener(\"beforeunload\", beforeunload);\n        return willUnload;\n    }\n}\n\n/**\n * @param {HTMLElement} element\n * @returns {HTMLElement | null}\n */\nexport function getScrollParent(element) {\n    if (!element) {\n        return null;\n    }\n    // We cannot only rely on the fact that the element\u2019s scrollHeight is\n    // greater than its clientHeight. This might not be the case when a step\n    // starts, and the scrollbar could appear later. For example, when clicking\n    // on a \"building block\" in the \"building block previews modal\" during a\n    // tour (in website edit mode). When the modal opens, not all \"building\n    // blocks\" are loaded yet, and the scrollbar is not present initially.\n    const overflowY = window.getComputedStyle(element).overflowY;\n    const isScrollable =\n        overflowY === \"auto\" ||\n        overflowY === \"scroll\" ||\n        (overflowY === \"visible\" && element === element.ownerDocument.scrollingElement);\n    if (isScrollable) {\n        return element;\n    } else {\n        return getScrollParent(element.parentNode);\n    }\n}\n\nexport const stepUtils = {\n    _getHelpMessage(functionName, ...args) {\n        return `Generated by function tour utils ${functionName}(${args.join(\", \")})`;\n    },\n\n    addDebugHelp(helpMessage, step) {\n        if (typeof step.debugHelp === \"string\") {\n            step.debugHelp = step.debugHelp + \"\\n\" + helpMessage;\n        } else {\n            step.debugHelp = helpMessage;\n        }\n        return step;\n    },\n\n    showAppsMenuItem() {\n        return {\n            isActive: [\"auto\", \"community\", \"desktop\"],\n            trigger: \".o_navbar_apps_menu button:enabled\",\n            tooltipPosition: \"bottom\",\n            run: \"click\",\n        };\n    },\n\n    toggleHomeMenu() {\n        return [\n            {\n                isActive: [\".o_main_navbar .o_menu_toggle\"],\n                trigger: \".o_main_navbar .o_menu_toggle\",\n                content: _t(\"Click the top left corner to navigate across apps.\"),\n                tooltipPosition: \"bottom\",\n                run: \"click\",\n            },\n            {\n                isActive: [\"mobile\"],\n                trigger: \".o_sidebar_topbar a.btn-primary\",\n                tooltipPosition: \"right\",\n                run: \"click\",\n            },\n        ];\n    },\n\n    autoExpandMoreButtons(isActiveMobile = false) {\n        const isActive = [\"auto\"];\n        if (isActiveMobile) {\n            isActive.push(\"mobile\");\n        }\n        return {\n            isActive,\n            content: `autoExpandMoreButtons`,\n            trigger: \".o-form-buttonbox\",\n            run() {\n                const more = hoot.queryFirst(\".o-form-buttonbox .o_button_more\");\n                if (more) {\n                    hoot.click(more);\n                }\n            },\n        };\n    },\n\n    goToAppSteps(dataMenuXmlid, description) {\n        return [\n            this.showAppsMenuItem(),\n            {\n                isActive: [\"community\"],\n                trigger: `.o_app[data-menu-xmlid=\"${dataMenuXmlid}\"]`,\n                content: description,\n                tooltipPosition: \"right\",\n                run: \"click\",\n            },\n            {\n                isActive: [\"enterprise\"],\n                trigger: `.o_app[data-menu-xmlid=\"${dataMenuXmlid}\"]`,\n                content: description,\n                tooltipPosition: \"bottom\",\n                run: \"click\",\n            },\n        ].map((step) =>\n            this.addDebugHelp(this._getHelpMessage(\"goToApp\", dataMenuXmlid, description), step)\n        );\n    },\n\n    statusbarButtonsSteps(innerTextButton, description, trigger) {\n        const steps = [];\n        if (trigger) {\n            steps.push({\n                isActive: [\"auto\", \"mobile\"],\n                trigger,\n            });\n        }\n        steps.push(\n            {\n                isActive: [\"auto\", \"mobile\"],\n                trigger: \".o_cp_action_menus\",\n                run: (actions) => {\n                    const node = hoot.queryFirst(\".o_cp_action_menus .fa-cog\");\n                    if (node) {\n                        hoot.click(node);\n                    }\n                },\n            },\n            {\n                trigger: `.o_statusbar_buttons button:enabled:contains('${innerTextButton}'), .dropdown-item button:enabled:contains('${innerTextButton}')`,\n                content: description,\n                tooltipPosition: \"bottom\",\n                run: \"click\",\n            }\n        );\n        return steps.map((step) =>\n            this.addDebugHelp(\n                this._getHelpMessage(\"statusbarButtonsSteps\", innerTextButton, description),\n                step\n            )\n        );\n    },\n\n    mobileKanbanSearchMany2X(modalTitle, valueSearched) {\n        return [\n            {\n                isActive: [\"mobile\"],\n                trigger: `.modal:not(.o_inactive_modal) .o_control_panel_navigation .btn .fa-search`,\n                tooltipPosition: \"bottom\",\n                run: \"click\",\n            },\n            {\n                isActive: [\"mobile\"],\n                trigger: \".o_searchview_input\",\n                tooltipPosition: \"bottom\",\n                run: `edit ${valueSearched}`,\n            },\n            {\n                isActive: [\"mobile\"],\n                trigger: \".dropdown-menu.o_searchview_autocomplete\",\n            },\n            {\n                isActive: [\"mobile\"],\n                trigger: \".o_searchview_input\",\n                tooltipPosition: \"bottom\",\n                run: \"press Enter\",\n            },\n            {\n                isActive: [\"mobile\"],\n                trigger: `.o_kanban_record:contains('${valueSearched}')`,\n                tooltipPosition: \"bottom\",\n                run: \"click\",\n            },\n        ].map((step) =>\n            this.addDebugHelp(\n                this._getHelpMessage(\"mobileKanbanSearchMany2X\", modalTitle, valueSearched),\n                step\n            )\n        );\n    },\n    /**\n     * Utility steps to save a form and wait for the save to complete\n     */\n    saveForm() {\n        return [\n            {\n                isActive: [\"auto\"],\n                content: \"save form\",\n                trigger: \".o_form_button_save:enabled\",\n                run: \"click\",\n            },\n            {\n                isActive: [\"auto\"],\n                content: \"wait for save completion\",\n                trigger: \".o_form_readonly, .o_form_saved\",\n            },\n        ];\n    },\n    /**\n     * Utility steps to cancel a form creation or edition.\n     *\n     * Supports creation/edition from either a form or a list view (so checks\n     * for both states).\n     */\n    discardForm() {\n        return [\n            {\n                isActive: [\"auto\"],\n                content: \"discard the form\",\n                trigger: \".o_form_button_cancel\",\n                run: \"click\",\n            },\n            {\n                isActive: [\"auto\"],\n                content: \"wait for cancellation to complete\",\n                trigger:\n                    \".o_view_controller.o_list_view, .o_form_view > div > div > .o_form_readonly, .o_form_view > div > div > .o_form_saved\",\n            },\n        ];\n    },\n\n    waitIframeIsReady() {\n        return {\n            content: \"Wait until the iframe is ready\",\n            trigger: `iframe[is-ready=true]:iframe html`,\n        };\n    },\n\n    goToUrl(url) {\n        return {\n            isActive: [\"auto\"],\n            content: `Navigate to ${url}`,\n            trigger: \"body\",\n            run: `goToUrl ${url}`,\n        };\n    },\n};\n", "/** @odoo-module */\n\nimport { HootDomError, getTag, isFirefox, isIterable, parseRegExp } from \"../hoot_dom_utils\";\nimport { waitUntil } from \"./time\";\n\n/**\n * @typedef {number | [number, number] | {\n *  w?: number;\n *  h?: number;\n *  width?: number;\n *  height?: number;\n * }} Dimensions\n *\n * @typedef {{\n *  root?: Target;\n *  tabbable?: boolean;\n * }} FocusableOptions\n *\n * @typedef {{\n *  keepInlineTextNodes?: boolean;\n *  tabSize?: number;\n *  type?: \"html\" | \"xml\";\n * }} FormatXmlOptions\n *\n * @typedef {{\n *  inline: boolean;\n *  level: number;\n *  value: MarkupLayerValue;\n * }} MarkupLayer\n *\n * @typedef {{\n *  close?: string;\n *  open?: string;\n *  textContent?: string;\n * }} MarkupLayerValue\n *\n * @typedef {(node: Node, selector: string) => Node[]} NodeGetter\n *\n * @typedef {string | string[] | number | boolean | File[]} NodeValue\n *\n * @typedef {number | [number, number] | {\n *  x?: number;\n *  y?: number;\n *  left?: number;\n *  top?: number,\n *  clientX?: number;\n *  clientY?: number;\n *  pageX?: number;\n *  pageY?: number;\n *  screenX?: number;\n *  screenY?: number;\n * }} Position\n *\n * @typedef {(content: string) => (node: Node, index: number, nodes: Node[]) => boolean | Node} PseudoClassPredicateBuilder\n *\n * @typedef {{\n *  displayed?: boolean;\n *  exact?: number;\n *  root?: HTMLElement;\n *  viewPort?: boolean;\n *  visible?: boolean;\n * }} QueryOptions\n *\n * @typedef {{\n *  trimPadding?: boolean;\n * }} QueryRectOptions\n *\n * @typedef {{\n *  raw?: boolean;\n * }} QueryTextOptions\n *\n * @typedef {import(\"./time\").WaitOptions} WaitOptions\n */\n\n/**\n * @template T\n * @typedef {T | Iterable<T>} MaybeIterable\n */\n\n/**\n * @template [T=Node]\n * @typedef {MaybeIterable<T> | string | null | undefined | false} Target\n */\n\n//-----------------------------------------------------------------------------\n// Global\n//-----------------------------------------------------------------------------\n\nconst {\n    Boolean,\n    document,\n    DOMParser,\n    innerWidth,\n    innerHeight,\n    Map,\n    MutationObserver,\n    Number: { isInteger: $isInteger, isNaN: $isNaN, parseInt: $parseInt, parseFloat: $parseFloat },\n    Object: { keys: $keys, values: $values },\n    RegExp,\n    Set,\n} = globalThis;\n\n//-----------------------------------------------------------------------------\n// Internal\n//-----------------------------------------------------------------------------\n\n/**\n * @param  {string[]} values\n */\nconst and = (values) => {\n    const last = values.pop();\n    if (values.length) {\n        return [values.join(\", \"), last].join(\" and \");\n    } else {\n        return last;\n    }\n};\n\nconst compilePseudoClassRegex = () => {\n    const customKeys = [...customPseudoClasses.keys()].filter((k) => k !== \"has\" && k !== \"not\");\n    return new RegExp(`:(${customKeys.join(\"|\")})`);\n};\n\n/**\n * @param {Element[]} elements\n * @param {string} selector\n */\nconst elementsMatch = (elements, selector) => {\n    if (!elements.length) {\n        return false;\n    }\n    return parseSelector(selector).some((selectorParts) => {\n        const [baseSelector, ...filters] = selectorParts.at(-1);\n        for (let i = 0; i < elements.length; i++) {\n            if (baseSelector && !elements[i].matches(baseSelector)) {\n                return false;\n            }\n            if (!filters.every((filter) => matchFilter(filter, elements, i))) {\n                return false;\n            }\n        }\n        return true;\n    });\n};\n\n/**\n * @param {Node} node\n * @returns {Element | null}\n */\nconst ensureElement = (node) => {\n    if (node) {\n        if (isDocument(node)) {\n            return node.documentElement;\n        }\n        if (isWindow(node)) {\n            return node.document.documentElement;\n        }\n        if (isElement(node)) {\n            return node;\n        }\n    }\n    return null;\n};\n\n/**\n * @param {Iterable<Node>} nodes\n * @param {number} level\n * @param {boolean} [keepInlineTextNodes]\n */\nconst extractLayers = (nodes, level, keepInlineTextNodes) => {\n    /** @type {MarkupLayer[]} */\n    const layers = [];\n    for (const node of nodes) {\n        if (node.nodeType === Node.COMMENT_NODE) {\n            continue;\n        }\n        if (node.nodeType === Node.TEXT_NODE) {\n            const textContent = node.nodeValue.replaceAll(/\\n/g, \"\");\n            const trimmedTextContent = textContent.trim();\n            if (trimmedTextContent) {\n                const inline = textContent === trimmedTextContent;\n                layers.push({ inline, level, value: { textContent: trimmedTextContent } });\n            }\n            continue;\n        }\n        const [open, close] = node.outerHTML.replace(`>${node.innerHTML}<`, \">\\n<\").split(\"\\n\");\n        const layer = { inline: false, level, value: { open, close } };\n        layers.push(layer);\n        const childLayers = extractLayers(node.childNodes, level + 1, false);\n        if (keepInlineTextNodes && childLayers.length === 1 && childLayers[0].inline) {\n            layer.value.textContent = childLayers[0].value.textContent;\n        } else {\n            layers.push(...childLayers);\n        }\n    }\n    return layers;\n};\n\n/**\n * @param {Iterable<Node>} nodesToFilter\n */\nconst filterUniqueNodes = (nodesToFilter) => {\n    /** @type {Node[]} */\n    const nodes = [];\n    for (const node of nodesToFilter) {\n        if (isQueryableNode(node) && !nodes.includes(node)) {\n            nodes.push(node);\n        }\n    }\n    return nodes;\n};\n\n/**\n * @param {MarkupLayer[]} layers\n * @param {number} tabSize\n */\nconst generateStringFromLayers = (layers, tabSize) => {\n    const result = [];\n    let layerIndex = 0;\n    while (layers.length > 0) {\n        const layer = layers[layerIndex];\n        const { level, value } = layer;\n        const pad = \" \".repeat(tabSize * level);\n        let nextLayerIndex = layerIndex + 1;\n        if (value.open) {\n            if (value.textContent) {\n                // node with inline textContent (no wrapping white-spaces)\n                result.push(`${pad}${value.open}${value.textContent}${value.close}`);\n                layers.splice(layerIndex, 1);\n                nextLayerIndex--;\n            } else {\n                result.push(`${pad}${value.open}`);\n                delete value.open;\n            }\n        } else {\n            if (value.close) {\n                result.push(`${pad}${value.close}`);\n            } else if (value.textContent) {\n                result.push(`${pad}${value.textContent}`);\n            }\n            layers.splice(layerIndex, 1);\n            nextLayerIndex--;\n        }\n        if (nextLayerIndex >= layers.length) {\n            layerIndex = nextLayerIndex - 1;\n            continue;\n        }\n        const nextLayer = layers[nextLayerIndex];\n        if (nextLayerIndex === 0 || nextLayer.level > layers[nextLayerIndex - 1].level) {\n            layerIndex = nextLayerIndex;\n        } else {\n            layerIndex = nextLayerIndex - 1;\n        }\n    }\n    return result.join(\"\\n\");\n};\n\n/**\n * @param {Node} node\n * @returns {NodeValue}\n */\nconst getNodeContent = (node) => {\n    switch (getTag(node)) {\n        case \"input\":\n        case \"option\":\n        case \"textarea\":\n            return getNodeValue(node);\n        case \"select\":\n            return [...node.selectedOptions].map(getNodeValue).join(\",\");\n    }\n    return getNodeText(node);\n};\n\n/**\n * @param {string} string\n */\nconst getStringContent = (string) => string.match(R_QUOTE_CONTENT)?.[2] || string;\n\n/**\n * @param {string} [char]\n */\nconst isChar = (char) => Boolean(char) && R_CHAR.test(char);\n\n/**\n * @template T\n * @param {T} object\n * @returns {T extends Document ? true : false}\n */\nconst isDocument = (object) => object?.nodeType === Node.DOCUMENT_NODE;\n\n/**\n * @template T\n * @param {T} object\n * @returns {T extends Element ? true: false}\n */\nconst isElement = (object) => object?.nodeType === Node.ELEMENT_NODE;\n\n/**\n * @param {Node} node\n */\nconst isQueryableNode = (node) => QUERYABLE_NODE_TYPES.includes(node.nodeType);\n\n/**\n * @param {Element} [el]\n */\nconst isRootElement = (el) => el && R_ROOT_ELEMENT.test(el.nodeName || \"\");\n\n/**\n * @param {Element} el\n */\nconst isShadowRoot = (el) => el.nodeType === Node.DOCUMENT_FRAGMENT_NODE && Boolean(el.host);\n\n/**\n * @template T\n * @param {T} object\n * @returns {T extends Window ? true : false}\n */\nconst isWindow = (object) => object?.window === object && object.constructor.name === \"Window\";\n\n/**\n * @param {string} [char]\n */\nconst isWhiteSpace = (char) => Boolean(char) && R_HORIZONTAL_WHITESPACE.test(char);\n\n/**\n * @param {string} pseudoClass\n * @param {(node: Node) => NodeValue} getContent\n */\nconst makePatternBasedPseudoClass = (pseudoClass, getContent) => {\n    return (content) => {\n        let regex;\n        try {\n            regex = parseRegExp(content);\n        } catch (err) {\n            throw selectorError(pseudoClass, err.message);\n        }\n        if (regex instanceof RegExp) {\n            return function containsRegExp(node) {\n                return regex.test(String(getContent(node)));\n            };\n        } else {\n            const lowerContent = content.toLowerCase();\n            return function containsString(node) {\n                return getStringContent(String(getContent(node)))\n                    .toLowerCase()\n                    .includes(lowerContent);\n            };\n        }\n    };\n};\n\n/**\n *\n * @param {string | (node: Node, index: number, nodes: Node[]) => boolean} filter\n * @param {Node} node\n * @param {number} index\n * @param {Node[]} allNodes\n * @returns\n */\nconst matchFilter = (filter, nodes, index) => {\n    const node = nodes[index];\n    if (typeof filter === \"function\") {\n        return filter(node, index, nodes);\n    } else {\n        return node.matches?.(String(filter));\n    }\n};\n\n/**\n * @param {string} query\n * @param {number} width\n * @param {number} height\n */\nconst matchesQuery = (query, width, height) =>\n    query\n        .toLowerCase()\n        .split(/\\s*,\\s*/)\n        .some((orPart) =>\n            orPart\n                .split(/\\s*\\band\\b\\s*/)\n                .every((andPart) => matchesQueryPart(andPart, width, height))\n        );\n\n/**\n * @param {string} query\n * @param {number} width\n * @param {number} height\n */\nconst matchesQueryPart = (query, width, height) => {\n    const [, key, value] = query.match(/\\(\\s*([\\w-]+)\\s*:\\s*(.+)\\s*\\)/) || [];\n    let result = false;\n    if (key) {\n        switch (key) {\n            case \"display-mode\": {\n                result = value === mockedMatchMedia.DISPLAY_MODE;\n                break;\n            }\n            case \"max-height\": {\n                result = height <= $parseFloat(value);\n                break;\n            }\n            case \"max-width\": {\n                result = width <= $parseFloat(value);\n                break;\n            }\n            case \"min-height\": {\n                result = height >= $parseFloat(value);\n                break;\n            }\n            case \"min-width\": {\n                result = width >= $parseFloat(value);\n                break;\n            }\n            case \"orientation\": {\n                result = value === \"landscape\" ? width > height : width < height;\n                break;\n            }\n            case \"pointer\": {\n                switch (value) {\n                    case \"coarse\": {\n                        result = globalThis.ontouchstart !== undefined;\n                        break;\n                    }\n                    case \"fine\": {\n                        result = globalThis.ontouchstart === undefined;\n                        break;\n                    }\n                }\n                break;\n            }\n            case \"prefers-color-scheme\": {\n                result = value === mockedMatchMedia.COLOR_SCHEME;\n                break;\n            }\n            case \"prefers-reduced-motion\": {\n                result = value === mockedMatchMedia.REDUCED_MOTION;\n                break;\n            }\n        }\n    }\n\n    return query.startsWith(\"not\") ? !result : result;\n};\n\n/**\n * @template T\n * @param {T} value\n * @param {(keyof T)[]} propsA\n * @param {(keyof T)[]} propsB\n * @returns {[number, number]}\n */\nconst parseNumberTuple = (value, propsA, propsB) => {\n    let result = [];\n    if (value && typeof value === \"object\") {\n        if (isIterable(value)) {\n            [result[0], result[1]] = [...value];\n        } else {\n            for (const prop of propsA) {\n                result[0] ??= value[prop];\n            }\n            for (const prop of propsB) {\n                result[1] ??= value[prop];\n            }\n        }\n    } else {\n        result = [value, value];\n    }\n    return result.map($parseFloat);\n};\n\n/**\n * Parses a given selector string into a list of selector groups.\n *\n * - the return value is a list of selector `group` objects (representing comma-separated\n *  selectors);\n * - a `group` is composed of one or more `part` objects (representing space-separated\n *  selector parts inside of a group);\n * - a `part` is composed of a base selector (string) and zero or more 'filters' (predicates).\n *\n * @param {string} selector\n */\nconst parseSelector = (selector) => {\n    /**\n     * @param {string} selector\n     */\n    const addToSelector = (selector) => {\n        registerChar = false;\n        const index = currentPart.length - 1;\n        if (typeof currentPart[index] === \"string\") {\n            currentPart[index] += selector;\n        } else {\n            currentPart.push(selector);\n        }\n    };\n\n    /** @type {(string | ReturnType<PseudoClassPredicateBuilder>)[]} */\n    const firstPart = [\"\"];\n    const firstGroup = [firstPart];\n    const groups = [firstGroup];\n    const parens = [0, 0];\n\n    let currentGroup = groups.at(-1);\n    let currentPart = currentGroup.at(-1);\n    let currentPseudo = null;\n    let currentQuote = null;\n    let registerChar = true;\n\n    for (let i = 0; i < selector.length; i++) {\n        const char = selector[i];\n        registerChar = true;\n        switch (char) {\n            // Group separator (comma)\n            case \",\": {\n                if (!currentQuote && !currentPseudo) {\n                    groups.push([[\"\"]]);\n                    currentGroup = groups.at(-1);\n                    currentPart = currentGroup.at(-1);\n                    registerChar = false;\n                }\n                break;\n            }\n            // Part separator (white space)\n            case \" \":\n            case \"\\t\":\n            case \"\\n\":\n            case \"\\r\":\n            case \"\\f\":\n            case \"\\v\": {\n                if (!currentQuote && !currentPseudo) {\n                    if (currentPart[0] || currentPart.length > 1) {\n                        // Only push new part if the current one is not empty\n                        // (has at least 1 character OR 1 pseudo-class filter)\n                        currentGroup.push([\"\"]);\n                        currentPart = currentGroup.at(-1);\n                    }\n                    registerChar = false;\n                }\n                break;\n            }\n            // Quote delimiters\n            case `'`:\n            case `\"`: {\n                if (char === currentQuote) {\n                    currentQuote = null;\n                } else if (!currentQuote) {\n                    currentQuote = char;\n                }\n                break;\n            }\n            // Combinators\n            case \">\":\n            case \"+\":\n            case \"~\": {\n                if (!currentQuote && !currentPseudo) {\n                    while (isWhiteSpace(selector[i + 1])) {\n                        i++;\n                    }\n                    addToSelector(char);\n                }\n                break;\n            }\n            // Pseudo classes\n            case \":\": {\n                if (!currentQuote && !currentPseudo) {\n                    let pseudo = \"\";\n                    while (isChar(selector[i + 1])) {\n                        pseudo += selector[++i];\n                    }\n                    if (customPseudoClasses.has(pseudo)) {\n                        if (selector[i + 1] === \"(\") {\n                            parens[0]++;\n                            i++;\n                            registerChar = false;\n                        }\n                        currentPseudo = [pseudo, \"\"];\n                    } else {\n                        addToSelector(char + pseudo);\n                    }\n                }\n                break;\n            }\n            // Parentheses\n            case \"(\": {\n                if (!currentQuote) {\n                    parens[0]++;\n                }\n                break;\n            }\n            case \")\": {\n                if (!currentQuote) {\n                    parens[1]++;\n                }\n                break;\n            }\n        }\n\n        if (currentPseudo) {\n            if (parens[0] === parens[1]) {\n                const [pseudo, content] = currentPseudo;\n                const makeFilter = customPseudoClasses.get(pseudo);\n                if (pseudo === \"iframe\" && !currentPart[0].startsWith(\"iframe\")) {\n                    // Special case: to optimise the \":iframe\" pseudo class, we\n                    // always select actual `iframe` elements.\n                    // Note that this may create \"impossible\" tag names (like \"iframediv\")\n                    // but this pseudo won't work on non-iframe elements anyway.\n                    currentPart[0] = `iframe${currentPart[0]}`;\n                }\n                currentPart.push(makeFilter(getStringContent(content)));\n                currentPseudo = null;\n            } else if (registerChar) {\n                currentPseudo[1] += selector[i];\n            }\n        } else if (registerChar) {\n            addToSelector(selector[i]);\n        }\n    }\n\n    return groups;\n};\n\n/**\n * @param {string} xmlString\n * @param {\"html\" | \"xml\"} type\n */\nconst parseXml = (xmlString, type) => {\n    const wrapperTag = type === \"html\" ? \"body\" : \"templates\";\n    const document = parser.parseFromString(\n        `<${wrapperTag}>${xmlString}</${wrapperTag}>`,\n        `text/${type}`\n    );\n    if (document.getElementsByTagName(\"parsererror\").length) {\n        const trimmed = xmlString.length > 80 ? xmlString.slice(0, 80) + \"\u2026\" : xmlString;\n        throw new HootDomError(\n            `error while parsing ${trimmed}: ${getNodeText(\n                document.getElementsByTagName(\"parsererror\")[0]\n            )}`\n        );\n    }\n    return document.getElementsByTagName(wrapperTag)[0].childNodes;\n};\n\n/**\n * Converts a CSS pixel value to a number, removing the 'px' part.\n *\n * @param {string} val\n */\nconst pixelValueToNumber = (val) => $parseFloat(val.endsWith(\"px\") ? val.slice(0, -2) : val);\n\n/**\n * @param {Node[]} nodes\n * @param {string} selector\n */\nconst queryWithCustomSelector = (nodes, selector) => {\n    const selectorGroups = parseSelector(selector);\n    const foundNodes = [];\n    for (const selectorParts of selectorGroups) {\n        let groupNodes = nodes;\n        for (const [partSelector, ...filters] of selectorParts) {\n            let baseSelector = partSelector;\n            let nodeGetter;\n            switch (baseSelector[0]) {\n                case \"+\": {\n                    nodeGetter = NEXT_SIBLING;\n                    break;\n                }\n                case \">\": {\n                    nodeGetter = DIRECT_CHILDREN;\n                    break;\n                }\n                case \"~\": {\n                    nodeGetter = NEXT_SIBLINGS;\n                    break;\n                }\n            }\n\n            // Slices modifier (if any)\n            if (nodeGetter) {\n                baseSelector = baseSelector.slice(1);\n            }\n\n            // Retrieve matching nodes and apply filters\n            const getNodes = nodeGetter || DESCENDANTS;\n            let currentGroupNodes = groupNodes.flatMap((node) => getNodes(node, baseSelector));\n\n            // Filter/replace nodes based on custom pseudo-classes\n            const pseudosReturningNode = new Set();\n            for (const filter of filters) {\n                const filteredGroupNodes = [];\n                for (let i = 0; i < currentGroupNodes.length; i++) {\n                    const result = matchFilter(filter, currentGroupNodes, i);\n                    if (result === true) {\n                        filteredGroupNodes.push(currentGroupNodes[i]);\n                    } else if (result) {\n                        filteredGroupNodes.push(result);\n                        pseudosReturningNode.add(filter.name);\n                    }\n                }\n\n                if (pseudosReturningNode.size > 1) {\n                    const pseudoList = [...pseudosReturningNode];\n                    throw selectorError(\n                        pseudoList[0],\n                        `cannot use multiple pseudo-classes returning nodes (${and(pseudoList)})`\n                    );\n                }\n\n                currentGroupNodes = filteredGroupNodes;\n            }\n\n            groupNodes = currentGroupNodes;\n        }\n\n        foundNodes.push(...groupNodes);\n    }\n\n    return filterUniqueNodes(foundNodes);\n};\n\n/**\n * @param {string} pseudoClass\n * @param {string} message\n */\nconst selectorError = (pseudoClass, message) =>\n    new HootDomError(`invalid selector \\`:${pseudoClass}\\`: ${message}`);\n\n// Regexes\nconst R_CHAR = /[\\w-]/;\nconst R_QUOTE_CONTENT = /^\\s*(['\"])?([^]*?)\\1\\s*$/;\nconst R_ROOT_ELEMENT = /^(HTML|HEAD|BODY)$/;\n/**\n * \\s without \\n and \\v\n */\nconst R_HORIZONTAL_WHITESPACE =\n    /[\\r\\t\\f \\u00a0\\u1680\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]+/g;\n\nconst QUERYABLE_NODE_TYPES = [Node.ELEMENT_NODE, Node.DOCUMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE];\n\nconst parser = new DOMParser();\n\n// Node getters\n\n/** @type {NodeGetter} */\nconst DIRECT_CHILDREN = (node, selector) => {\n    const children = [];\n    for (const childNode of node.childNodes) {\n        if (childNode.matches?.(selector)) {\n            children.push(childNode);\n        }\n    }\n    return children;\n};\n\n/** @type {NodeGetter} */\nconst DESCENDANTS = (node, selector) => [...(node.querySelectorAll?.(selector || \"*\") || [])];\n\n/** @type {NodeGetter} */\nconst NEXT_SIBLING = (node, selector) => {\n    const sibling = node.nextElementSibling;\n    return sibling?.matches?.(selector) ? [sibling] : [];\n};\n\n/** @type {NodeGetter} */\nconst NEXT_SIBLINGS = (node, selector) => {\n    const siblings = [];\n    while ((node = node.nextElementSibling)) {\n        if (node.matches?.(selector)) {\n            siblings.push(node);\n        }\n    }\n    return siblings;\n};\n\n/** @type {Map<HTMLElement, { callbacks: Set<MutationCallback>, observer: MutationObserver }>} */\nconst observers = new Map();\nconst currentDimensions = {\n    width: innerWidth,\n    height: innerHeight,\n};\nlet getDefaultRoot = () => document;\n\n//-----------------------------------------------------------------------------\n// Pseudo classes\n//-----------------------------------------------------------------------------\n\n/** @type {Map<string, PseudoClassPredicateBuilder>} */\nconst customPseudoClasses = new Map();\n\ncustomPseudoClasses\n    .set(\"contains\", makePatternBasedPseudoClass(\"contains\", getNodeText))\n    .set(\"displayed\", () => {\n        return function displayed(node) {\n            return isNodeDisplayed(node);\n        };\n    })\n    .set(\"empty\", () => {\n        return function empty(node) {\n            return isEmpty(node);\n        };\n    })\n    .set(\"eq\", (content) => {\n        const index = $parseInt(content);\n        if (!$isInteger(index)) {\n            throw selectorError(\"eq\", `expected index to be an integer (got ${content})`);\n        }\n        return function eq(node, i, nodes) {\n            return index < 0 ? i === nodes.length + index : i === index;\n        };\n    })\n    .set(\"first\", () => {\n        return function first(node, i) {\n            return i === 0;\n        };\n    })\n    .set(\"focusable\", () => {\n        return function focusable(node) {\n            return isNodeFocusable(node);\n        };\n    })\n    .set(\"has\", (content) => {\n        return function has(node) {\n            return Boolean(queryAll(content, { root: node }).length);\n        };\n    })\n    .set(\"hidden\", () => {\n        return function hidden(node) {\n            return !isNodeVisible(node);\n        };\n    })\n    .set(\"iframe\", () => {\n        return function iframe(node) {\n            // Note: should only apply on `iframe` elements\n            /** @see parseSelector */\n            const doc = node.contentDocument;\n            return doc && doc.readyState !== \"loading\" ? doc : false;\n        };\n    })\n    .set(\"last\", () => {\n        return function last(node, i, nodes) {\n            return i === nodes.length - 1;\n        };\n    })\n    .set(\"not\", (content) => {\n        return function not(node) {\n            return !matches(node, content);\n        };\n    })\n    .set(\"only\", () => {\n        return function only(node, i, nodes) {\n            return nodes.length === 1;\n        };\n    })\n    .set(\"scrollable\", () => {\n        return function scrollable(node) {\n            return isNodeScrollable(node);\n        };\n    })\n    .set(\"selected\", () => {\n        return function selected(node) {\n            return Boolean(node.selected);\n        };\n    })\n    .set(\"shadow\", () => {\n        return function shadow(node) {\n            return node.shadowRoot || false;\n        };\n    })\n    .set(\"value\", makePatternBasedPseudoClass(\"value\", getNodeValue))\n    .set(\"visible\", () => {\n        return function visible(node) {\n            return isNodeVisible(node);\n        };\n    });\n\nconst rCustomPseudoClass = compilePseudoClassRegex();\n\n//-----------------------------------------------------------------------------\n// Internal exports (inside Hoot/Hoot-DOM)\n//-----------------------------------------------------------------------------\n\nexport function cleanupDOM() {\n    // Dimensions\n    currentDimensions.width = innerWidth;\n    currentDimensions.height = innerHeight;\n\n    // Observers\n    const remainingObservers = observers.size;\n    if (remainingObservers) {\n        for (const { observer } of observers.values()) {\n            observer.disconnect();\n        }\n        observers.clear();\n    }\n}\n\n/**\n * @param {Node | () => Node} node\n */\nexport function defineRootNode(node) {\n    if (typeof node === \"function\") {\n        getDefaultRoot = node;\n    } else if (node) {\n        getDefaultRoot = () => node;\n    } else {\n        getDefaultRoot = () => document;\n    }\n}\n\nexport function getCurrentDimensions() {\n    return currentDimensions;\n}\n\nexport function getDefaultRootNode() {\n    return getDefaultRoot();\n}\n\n/**\n * @param {Node} [node]\n * @returns {Document}\n */\nexport function getDocument(node) {\n    node ||= getDefaultRoot();\n    return isDocument(node) ? node : node.ownerDocument || document;\n}\n\n/**\n * @param {Node} node\n * @param {string} attribute\n * @returns {string | null}\n */\nexport function getNodeAttribute(node, attribute) {\n    return node.getAttribute?.(attribute) ?? null;\n}\n\n/**\n * @param {Node} node\n * @returns {NodeValue}\n */\nexport function getNodeValue(node) {\n    switch (node.type) {\n        case \"checkbox\":\n        case \"radio\":\n            return node.checked;\n        case \"file\":\n            return [...node.files];\n        case \"number\":\n        case \"range\":\n            return node.valueAsNumber;\n        case \"date\":\n        case \"datetime-local\":\n        case \"month\":\n        case \"time\":\n        case \"week\":\n            return node.valueAsDate.toISOString();\n    }\n    return node.value;\n}\n\n/**\n * @param {Node} node\n * @param {QueryRectOptions} [options]\n */\nexport function getNodeRect(node, options) {\n    if (!isElement(node)) {\n        return new DOMRect();\n    }\n\n    /** @type {DOMRect} */\n    const rect = node.getBoundingClientRect();\n    const parentFrame = getParentFrame(node);\n    if (parentFrame) {\n        const parentRect = getNodeRect(parentFrame);\n        rect.x -= parentRect.x;\n        rect.y -= parentRect.y;\n    }\n\n    if (!options?.trimPadding) {\n        return rect;\n    }\n\n    const style = getStyle(node);\n    const { x, y, width, height } = rect;\n    const [pl, pr, pt, pb] = [\"left\", \"right\", \"top\", \"bottom\"].map((side) =>\n        pixelValueToNumber(style.getPropertyValue(`padding-${side}`))\n    );\n\n    return new DOMRect(x + pl, y + pt, width - (pl + pr), height - (pt + pb));\n}\n\n/**\n * @param {Node} node\n * @param {QueryTextOptions} [options]\n * @returns {string}\n */\nexport function getNodeText(node, options) {\n    let content;\n    if (typeof node.innerText === \"string\") {\n        content = node.innerText;\n    } else {\n        content = node.textContent;\n    }\n    if (options?.raw) {\n        return content;\n    }\n    return content.replace(R_HORIZONTAL_WHITESPACE, \" \").trim();\n}\n\n/**\n * @template {Node} T\n * @param {T} node\n * @returns {T extends Element ? CSSStyleDeclaration : null}\n */\nexport function getStyle(node) {\n    return isElement(node) ? getComputedStyle(node) : null;\n}\n\n/**\n * @param {Node} [node]\n * @returns {Window}\n */\nexport function getWindow(node) {\n    return getDocument(node).defaultView;\n}\n\n/**\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isCheckable(node) {\n    switch (getTag(node)) {\n        case \"input\":\n            return node.type === \"checkbox\" || node.type === \"radio\";\n        case \"label\":\n            return isCheckable(node.control);\n        default:\n            return false;\n    }\n}\n\n/**\n * @param {unknown} value\n * @returns {boolean}\n */\nexport function isEmpty(value) {\n    if (!value) {\n        return true;\n    }\n    if (typeof value === \"object\") {\n        if (isNode(value)) {\n            return isEmpty(getNodeContent(value));\n        }\n        if (!isIterable(value)) {\n            value = $keys(value);\n        }\n        return [...value].length === 0;\n    }\n    return false;\n}\n\n/**\n * Returns whether the given object is an {@link EventTarget}.\n *\n * @template T\n * @param {T} object\n * @returns {T extends EventTarget ? true : false}\n * @example\n *  isEventTarget(window); // true\n * @example\n *  isEventTarget(new App()); // false\n */\nexport function isEventTarget(object) {\n    return object && typeof object.addEventListener === \"function\";\n}\n\n/**\n * Returns whether the given object is a {@link Node} object.\n * Note that it is independant from the {@link Node} class itself to support\n * cross-window checks.\n *\n * @template T\n * @param {T} object\n * @returns {T extends Node ? true : false}\n */\nexport function isNode(object) {\n    return object && typeof object.nodeType === \"number\" && typeof object.nodeName === \"string\";\n}\n\n/**\n * @param {Node} node\n */\nexport function isNodeCssVisible(node) {\n    const element = ensureElement(node);\n    if (element === getDefaultRoot() || isRootElement(element)) {\n        return true;\n    }\n    const style = getStyle(element);\n    if (style?.visibility === \"hidden\" || style?.opacity === \"0\") {\n        return false;\n    }\n    const parent = element.parentNode;\n    return !parent || isNodeCssVisible(isShadowRoot(parent) ? parent.host : parent);\n}\n\n/**\n * @param {Window | Node} node\n */\nexport function isNodeDisplayed(node) {\n    const element = ensureElement(node);\n    if (!isInDOM(element)) {\n        return false;\n    }\n    if (isRootElement(element) || element.offsetParent || element.closest(\"svg\")) {\n        return true;\n    }\n    // `position=fixed` elements in Chrome do not have an `offsetParent`\n    return !isFirefox() && getStyle(element)?.position === \"fixed\";\n}\n\n/**\n * @param {Node} node\n * @param {FocusableOptions} node\n */\nexport function isNodeFocusable(node, options) {\n    return (\n        isNodeDisplayed(node) &&\n        node.matches?.(FOCUSABLE_SELECTOR) &&\n        (!options?.tabbable || node.tabIndex >= 0)\n    );\n}\n\n/**\n * @param {Window | Node} node\n */\nexport function isNodeInViewPort(node) {\n    const element = ensureElement(node);\n    const { x, y } = getNodeRect(element);\n\n    return y > 0 && y < currentDimensions.height && x > 0 && x < currentDimensions.width;\n}\n\n/**\n * @param {Window | Node} node\n * @param {\"x\" | \"y\"} [axis]\n */\nexport function isNodeScrollable(node, axis) {\n    if (!isElement(node)) {\n        return false;\n    }\n    const [scrollProp, sizeProp] =\n        axis === \"x\" ? [\"scrollWidth\", \"clientWidth\"] : [\"scrollHeight\", \"clientHeight\"];\n    if (node[scrollProp] > node[sizeProp]) {\n        const overflow = getStyle(node).getPropertyValue(\"overflow\");\n        if (/\\bauto\\b|\\bscroll\\b/.test(overflow)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/**\n * @param {Window | Node} node\n */\nexport function isNodeVisible(node) {\n    const element = ensureElement(node);\n\n    // Must be displayed and not hidden by CSS\n    if (!isNodeDisplayed(element) || !isNodeCssVisible(element)) {\n        return false;\n    }\n\n    let visible = false;\n\n    // Check size (width & height)\n    const { width, height } = getNodeRect(element);\n    visible = width > 0 && height > 0;\n\n    // Check content (if display=contents)\n    if (!visible && getStyle(element)?.display === \"contents\") {\n        for (const child of element.childNodes) {\n            if (isNodeVisible(child)) {\n                return true;\n            }\n        }\n    }\n\n    return visible;\n}\n\n/**\n * @type {typeof matchMedia}\n */\nexport function mockedMatchMedia(query) {\n    let onchange = null;\n    return {\n        addEventListener: (type, callback) => window.addEventListener(\"resize\", callback),\n        get matches() {\n            return matchesQuery(query, window.innerWidth, window.innerHeight);\n        },\n        media: query,\n        get onchange() {\n            return onchange;\n        },\n        set onchange(value) {\n            value ||= null;\n            if (value) {\n                window.addEventListener(\"resize\", value);\n            } else {\n                window.removeEventListener(\"resize\", onchange);\n            }\n            onchange = value;\n        },\n        removeEventListener: (type, callback) => window.removeEventListener(\"resize\", callback),\n    };\n}\n\nmockedMatchMedia.COLOR_SCHEME = \"light\";\nmockedMatchMedia.DISPLAY_MODE = \"browser\";\nmockedMatchMedia.REDUCED_MOTION = \"reduce\";\n\n/**\n * @param {Dimensions} dimensions\n * @returns {[number, number]}\n */\nexport function parseDimensions(dimensions) {\n    return parseNumberTuple(dimensions, [\"width\", \"w\"], [\"height\", \"h\"]);\n}\n\n/**\n * @param {Position} position\n * @returns {[number, number]}\n */\nexport function parsePosition(position) {\n    return parseNumberTuple(\n        position,\n        [\"x\", \"left\", \"clientX\", \"pageX\", \"screenX\"],\n        [\"y\", \"top\", \"clientY\", \"pageY\", \"screenY\"]\n    );\n}\n\n/**\n * @param {number} width\n * @param {number} height\n */\nexport function setDimensions(width, height) {\n    const defaultRoot = getDefaultRoot();\n    if (!$isNaN(width)) {\n        currentDimensions.width = width;\n        defaultRoot.style?.setProperty(\"width\", `${width}px`, \"important\");\n    }\n    if (!$isNaN(height)) {\n        currentDimensions.height = height;\n        defaultRoot.style?.setProperty(\"height\", `${height}px`, \"important\");\n    }\n}\n\n/**\n * @param {Node} node\n * @param {{ object?: boolean }} [options]\n * @returns {string | string[]}\n */\nexport function toSelector(node, options) {\n    const parts = {\n        tag: node.nodeName.toLowerCase(),\n    };\n    if (node.id) {\n        parts.id = `#${node.id}`;\n    }\n    if (node.classList?.length) {\n        parts.class = `.${[...node.classList].join(\".\")}`;\n    }\n    return options?.object ? parts : $values(parts).join(\"\");\n}\n\n// Following selector is based on this spec:\n// https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex\nexport const FOCUSABLE_SELECTOR = [\n    \"a[href]\",\n    \"area[href]\",\n    \"button:enabled\",\n    \"details > summary:first-of-type\",\n    \"iframe\",\n    \"input:enabled\",\n    \"select:enabled\",\n    \"textarea:enabled\",\n    \"[tabindex]\",\n    \"[contenteditable=true]\",\n].join(\",\");\n\n//-----------------------------------------------------------------------------\n// Exports\n//-----------------------------------------------------------------------------\n\n/**\n * Returns a standardized representation of the given `string` value as a human-readable\n * XML string template (or HTML if the `type` option is `\"html\"`).\n *\n * @param {string} value\n * @param {FormatXmlOptions} [options]\n * @returns {string}\n */\nexport function formatXml(value, options) {\n    const nodes = parseXml(value, options?.type || \"xml\");\n    const layers = extractLayers(nodes, 0, options?.keepInlineTextNodes ?? false);\n    return generateStringFromLayers(layers, options?.tabSize ?? 4);\n}\n\n/**\n * Returns the active element in the given document (or in the owner document of\n * the given node).\n *\n * @param {Node} [node]\n */\nexport function getActiveElement(node) {\n    const { activeElement } = getDocument(node);\n    if (activeElement.contentDocument) {\n        return getActiveElement(activeElement.contentDocument);\n    }\n    if (activeElement.shadowRoot) {\n        return activeElement.shadowRoot.activeElement;\n    }\n    return activeElement;\n}\n\n/**\n * Returns the list of focusable elements in the given parent, sorted by their `tabIndex`\n * property.\n *\n * @see {@link isFocusable} for more information\n * @param {FocusableOptions} [options]\n * @returns {Element[]}\n * @example\n *  getFocusableElements();\n */\nexport function getFocusableElements(options) {\n    const parent = queryOne(options?.root || getDefaultRoot());\n    if (typeof parent.querySelectorAll !== \"function\") {\n        return [];\n    }\n    const byTabIndex = {};\n    for (const element of parent.querySelectorAll(FOCUSABLE_SELECTOR)) {\n        const { tabIndex } = element;\n        if ((options?.tabbable && tabIndex < 0) || !isNodeDisplayed(element)) {\n            continue;\n        }\n        if (!byTabIndex[tabIndex]) {\n            byTabIndex[tabIndex] = [];\n        }\n        byTabIndex[tabIndex].push(element);\n    }\n    const withTabIndexZero = byTabIndex[0] || [];\n    delete byTabIndex[0];\n    return [...$values(byTabIndex).flat(), ...withTabIndexZero];\n}\n\n/**\n * Returns the next focusable element after the current active element if it is\n * contained in the given parent.\n *\n * @see {@link getFocusableElements}\n * @param {FocusableOptions} [options]\n * @returns {Element | null}\n * @example\n *  getPreviousFocusableElement();\n */\nexport function getNextFocusableElement(options) {\n    const parent = queryOne(options?.root || getDefaultRoot());\n    const focusableEls = getFocusableElements({ ...options, parent });\n    const index = focusableEls.indexOf(getActiveElement(parent));\n    return focusableEls[index + 1] || null;\n}\n\n/**\n * Returns the parent `<iframe>` of a given node (if any).\n *\n * @param {Node} node\n * @returns {HTMLIFrameElement | null}\n */\nexport function getParentFrame(node) {\n    const nodeDocument = node.ownerDocument;\n    const view = nodeDocument.defaultView;\n    if (view !== view.parent) {\n        for (const iframe of view.parent.document.getElementsByTagName(\"iframe\")) {\n            if (iframe.contentDocument === nodeDocument) {\n                return iframe;\n            }\n        }\n    }\n    return null;\n}\n\n/**\n * Returns the previous focusable element before the current active element if it is\n * contained in the given parent.\n *\n * @see {@link getFocusableElements}\n * @param {FocusableOptions} [options]\n * @returns {Element | null}\n * @example\n *  getPreviousFocusableElement();\n */\nexport function getPreviousFocusableElement(options) {\n    const parent = queryOne(options?.root || getDefaultRoot());\n    const focusableEls = getFocusableElements({ ...options, parent });\n    const index = focusableEls.indexOf(getActiveElement(parent));\n    return index < 0 ? focusableEls.at(-1) : focusableEls[index - 1] || null;\n}\n\n/**\n * Checks whether a target is displayed, meaning that it has an offset parent and\n * is contained in the current document.\n *\n * Note that it does not mean that the target is \"visible\" (it can still be hidden\n * by CSS properties such as `width`, `opacity`, `visiblity`, etc.).\n *\n * @param {Target} target\n * @returns {boolean}\n */\nexport function isDisplayed(target) {\n    return queryAll(target, { displayed: true }).length > 0;\n}\n\n/**\n * Returns whether the given node is editable, meaning that it is an `\":enabled\"`\n * `<input>` or `<textarea>` {@link Element};\n *\n * Note: this does **NOT** support elements with `contenteditable=\"true\"`.\n *\n * @param {Node} node\n * @returns {boolean}\n * @example\n *  isEditable(document.querySelector(\"input\")); // true\n * @example\n *  isEditable(document.body); // false\n */\nexport function isEditable(node) {\n    return (\n        isElement(node) &&\n        !node.matches?.(\":disabled\") &&\n        [\"input\", \"textarea\"].includes(getTag(node))\n    );\n}\n\n/**\n * Returns whether an element is focusable. Focusable elements are either:\n * - `<a>` or `<area>` elements with an `href` attribute;\n * - *enabled* `<button>`, `<input>`, `<select>` and `<textarea>` elements;\n * - `<iframe>` elements;\n * - any element with its `contenteditable` attribute set to `\"true\"`.\n *\n * A focusable element must also not have a `tabIndex` property set to less than 0.\n *\n * @see {@link FOCUSABLE_SELECTOR}\n * @param {Target} target\n * @param {FocusableOptions} [options]\n * @returns {boolean}\n */\nexport function isFocusable(target, options) {\n    const nodes = queryAll(...arguments);\n    return nodes.length && nodes.every((node) => isNodeFocusable(node, options));\n}\n\n/**\n * Returns whether the given target is contained in the current root document.\n *\n * @param {Window | Node} target\n * @returns {boolean}\n * @example\n *  isInDOM(queryFirst(\"div\")); // true\n * @example\n *  isInDOM(document.createElement(\"div\")); // Not attached -> false\n */\nexport function isInDOM(target) {\n    return ensureElement(target)?.isConnected;\n}\n\n/**\n * Checks whether a target is *at least partially* visible in the current viewport.\n *\n * @param {Target} target\n * @returns {boolean}\n */\nexport function isInViewPort(target) {\n    return queryAll(target, { viewPort: true }).length > 0;\n}\n\n/**\n * Returns whether an element is scrollable.\n *\n * @param {Target} target\n * @param {\"x\" | \"y\"} [axis]\n * @returns {boolean}\n */\nexport function isScrollable(target, axis) {\n    const nodes = queryAll(target);\n    return nodes.length && nodes.every((node) => isNodeScrollable(node, axis));\n}\n\n/**\n * Checks whether a target is visible, meaning that it is \"displayed\" (see {@link isDisplayed}),\n * has a non-zero width and height, and is not hidden by \"opacity\" or \"visibility\"\n * CSS properties.\n *\n * Note that it does not account for:\n *  - the position of the target in the viewport (e.g. negative x/y coordinates)\n *  - the color of the target (e.g. transparent text with no background).\n *\n * @param {Target} target\n * @returns {boolean}\n */\nexport function isVisible(target) {\n    return queryAll(target, { visible: true }).length > 0;\n}\n\n/**\n * Equivalent to the native `node.matches(selector)`, with a few differences:\n * - it can take any {@link Target} (strings, nodes and iterable of nodes);\n * - it supports custom pseudo-classes, such as \":contains\" or \":visible\".\n *\n * @param {Target} target\n * @param {string} selector\n * @returns {boolean}\n * @example\n *  matches(\"input[name=surname]\", \":value(John)\");\n * @example\n *  matches(buttonEl, \":contains(Submit)\");\n */\nexport function matches(target, selector) {\n    return elementsMatch(queryAll(target), selector);\n}\n\n/**\n * Listens for DOM mutations on a given target.\n *\n * This helper has 2 main advantages over directly calling the native MutationObserver:\n * - it ensures a single observer is created for a given target, even if multiple\n *  callbacks are registered;\n * - it keeps track of these observers, which allows to check whether an observer\n *  is still running while it should not, and to disconnect all running observers\n *  at once.\n *\n * @param {HTMLElement} target\n * @param {MutationCallback} callback\n */\nexport function observe(target, callback) {\n    if (observers.has(target)) {\n        observers.get(target).callbacks.add(callback);\n    } else {\n        const callbacks = new Set([callback]);\n        const observer = new MutationObserver((mutations, observer) => {\n            for (const callback of callbacks) {\n                callback(mutations, observer);\n            }\n        });\n        observer.observe(target, {\n            attributes: true,\n            characterData: true,\n            childList: true,\n            subtree: true,\n        });\n        observers.set(target, { callbacks, observer });\n    }\n\n    return function disconnect() {\n        if (!observers.has(target)) {\n            return;\n        }\n        const { callbacks, observer } = observers.get(target);\n        callbacks.delete(callback);\n        if (!callbacks.size) {\n            observer.disconnect();\n            observers.delete(target);\n        }\n    };\n}\n\n/**\n * Returns a list of nodes matching the given {@link Target}.\n * This function can either be used as a **template literal tag** (only supports\n * string selector without options) or invoked the usual way.\n *\n * The target can be:\n * - a {@link Node} (or an iterable of nodes), or {@link Window} object;\n * - a {@link Document} object (which will be converted to its body);\n * - a string representing a *custom selector* (which will be queried in the `root` option);\n *\n * This function allows all string selectors supported by the native {@link Element.querySelector}\n * along with some additional custom pseudo-classes:\n *\n * - `:contains(text)`: matches nodes whose *content* matches the given *text*;\n *      * given *text* supports regular expression syntax (e.g. `:contains(/^foo.+/)`)\n *          and is case-insensitive;\n *      * given *text* will be matched against:\n *          - an `<input>`, `<textarea>` or `<select>` element's **value**;\n *          - or any other element's **inner text**.\n * - `:displayed`: matches nodes that are \"displayed\" (see {@link isDisplayed});\n * - `:empty`: matches nodes that have an empty *content* (**value** or **inner text**);\n * - `:eq(n)`: matches the *nth* node (0-based index);\n * - `:first`: matches the first node matching the selector (regardless of its actual\n *  DOM siblings);\n * - `:focusable`: matches nodes that can be focused (see {@link isFocusable});\n * - `:hidden`: matches nodes that are **not** \"visible\" (see {@link isVisible});\n * - `:iframe`: matches nodes that are `<iframe>` elements, and returns their `body`\n *  if it is ready;\n * - `:last`: matches the last node matching the selector (regardless of its actual\n *  DOM siblings);\n * - `:selected`: matches nodes that are selected (e.g. `<option>` elements);\n * - `:shadow`: matches nodes that have shadow roots, and returns their shadow root;\n * - `:scrollable`: matches nodes that are scrollable (see {@link isScrollable});\n * - `:visible`: matches nodes that are \"visible\" (see {@link isVisible});\n *\n * An `options` object can be specified to filter[1] the results:\n * - `displayed`: whether the nodes must be \"displayed\" (see {@link isDisplayed});\n * - `exact`: the exact number of nodes to match (throws an error if the number of\n *  nodes doesn't match);\n * - `focusable`: whether the nodes must be \"focusable\" (see {@link isFocusable});\n * - `root`: the root node to query the selector in (defaults to the current fixture);\n * - `viewPort`: whether the nodes must be partially visible in the current viewport\n *  (see {@link isInViewPort});\n * - `visible`: whether the nodes must be \"visible\" (see {@link isVisible}).\n *      * This option implies `displayed`\n *\n * [1] these filters (except for `exact` and `root`) achieve the same result as\n *  using their homonym pseudo-classes on the final group of the given selector\n *  string (e.g. ```queryAll`ul > li:visible`;``` = ```queryAll(\"ul > li\", { visible: true })```).\n *\n * @param {Target} target\n * @param {QueryOptions} [options]\n * @returns {Element[]}\n * @example\n *  // regular selectors\n *  queryAll`window`; // -> []\n *  queryAll`input#name`; // -> [input]\n *  queryAll`div`; // -> [div, div, ...]\n *  queryAll`ul > li`; // -> [li, li, ...]\n * @example\n *  // custom selectors\n *  queryAll`div:visible:contains(Lorem ipsum)`; // -> [div, div, ...]\n *  queryAll`div:visible:contains(${/^L\\w+\\si.*m$/})`; // -> [div, div, ...]\n *  queryAll`:focusable`; // -> [a, button, input, ...]\n *  queryAll`.o_iframe:iframe p`; // -> [p, p, ...] (inside iframe)\n *  queryAll`#editor:shadow div`; // -> [div, div, ...] (inside shadow DOM)\n * @example\n *  // with options\n *  queryAll(`div:first`, { exact: 1 }); // -> [div]\n *  queryAll(`div`, { root: queryOne`iframe` }); // -> [div, div, ...]\n *  // redundant, but possible\n *  queryAll(`button:visible`, { visible: true }); // -> [button, button, ...]\n */\nexport function queryAll(target, options) {\n    if (!target) {\n        return [];\n    }\n    if (target.raw) {\n        return queryAll(String.raw(...arguments));\n    }\n\n    const { exact, displayed, root, viewPort, visible } = options || {};\n\n    /** @type {Node[]} */\n    let nodes = [];\n    let selector;\n\n    if (typeof target === \"string\") {\n        nodes = root ? queryAll(root) : [getDefaultRoot()];\n        selector = target.trim();\n        // HTMLSelectElement is iterable \u00af\\_(\u30c4)_/\u00af\n    } else if (isIterable(target) && !isNode(target)) {\n        nodes = filterUniqueNodes(target);\n    } else {\n        nodes = filterUniqueNodes([target]);\n    }\n\n    if (selector && nodes.length) {\n        if (rCustomPseudoClass.test(selector)) {\n            nodes = queryWithCustomSelector(nodes, selector);\n        } else {\n            nodes = filterUniqueNodes(nodes.flatMap((node) => DESCENDANTS(node, selector)));\n        }\n    }\n\n    /** @type {string} */\n    let prefix, suffix;\n    if (visible + displayed > 1) {\n        throw new HootDomError(\n            `cannot use more than one visibility modifier ('visible' implies 'displayed')`\n        );\n    }\n    if (viewPort) {\n        nodes = nodes.filter(isNodeInViewPort);\n        suffix = \"in viewport\";\n    } else if (visible) {\n        nodes = nodes.filter(isNodeVisible);\n        prefix = \"visible\";\n    } else if (displayed) {\n        nodes = nodes.filter(isNodeDisplayed);\n        prefix = \"displayed\";\n    }\n\n    const count = nodes.length;\n    if ($isInteger(exact) && count !== exact) {\n        const s = count === 1 ? \"\" : \"s\";\n        const strPrefix = prefix ? `${prefix} ` : \"\";\n        const strSuffix = suffix ? ` ${suffix}` : \"\";\n        const strSelector = typeof target === \"string\" ? `(selector: \"${target}\")` : \"\";\n        throw new HootDomError(\n            `found ${count} ${strPrefix}node${s}${strSuffix} instead of ${exact} ${strSelector}`\n        );\n    }\n\n    return nodes;\n}\n\n/**\n * Performs a {@link queryOne} with the given arguments and returns the value of\n * the given *attribute* of the matching node.\n *\n * @param {Target} target\n * @param {string} attribute\n * @param {QueryOptions} [options]\n * @returns {string | null}\n */\nexport function queryAttribute(target, attribute, options) {\n    return getNodeAttribute(queryOne(target, options), attribute);\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns a list of the\n * *attribute values* of the matching nodes.\n *\n * @param {Target} target\n * @param {string} attribute\n * @param {QueryOptions} [options]\n * @returns {string[]}\n */\nexport function queryAllAttributes(target, attribute, options) {\n    return queryAll(target, options).map((node) => getNodeAttribute(node, attribute));\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns a list of the\n * *properties* of the matching nodes.\n *\n * @param {Target} target\n * @param {string} property\n * @param {QueryOptions} [options]\n * @returns {any[]}\n */\nexport function queryAllProperties(target, property, options) {\n    return queryAll(target, options).map((node) => node[property]);\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns a list of the\n * {@link DOMRect} of the matching nodes.\n *\n * There are a few differences with the native {@link Element.getBoundingClientRect}:\n * - rects take their positions relative to the top window element (instead of their\n *  parent `<iframe>` if any);\n * - they can be trimmed to remove padding with the `trimPadding` option.\n *\n * @param {Target} target\n * @param {QueryOptions & QueryRectOptions} [options]\n * @returns {DOMRect[]}\n */\nexport function queryAllRects(target, options) {\n    return queryAll(...arguments).map(getNodeRect);\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns a list of the\n * *texts* of the matching nodes.\n *\n * @param {Target} target\n * @param {QueryOptions & QueryTextOptions} [options]\n * @returns {string[]}\n */\nexport function queryAllTexts(target, options) {\n    return queryAll(...arguments).map((node) => getNodeText(node, options));\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns a list of the\n * *values* of the matching nodes.\n *\n * @param {Target} target\n * @param {QueryOptions} [options]\n * @returns {NodeValue[]}\n */\nexport function queryAllValues(target, options) {\n    return queryAll(...arguments).map(getNodeValue);\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments and returns the first result\n * or `null`.\n *\n * @param {Target} target\n * @param {QueryOptions} options\n * @returns {Element | null}\n */\nexport function queryFirst(target, options) {\n    return queryAll(...arguments)[0] || null;\n}\n\n/**\n * Performs a {@link queryAll} with the given arguments, along with a forced `exact: 1`\n * option to ensure only one node matches the given {@link Target}.\n *\n * The returned value is a single node instead of a list of nodes.\n *\n * @param {Target} target\n * @param {Omit<QueryOptions, \"exact\">} [options]\n * @returns {Element}\n */\nexport function queryOne(target, options) {\n    if (target.raw) {\n        return queryOne(String.raw(...arguments));\n    }\n    if ($isInteger(options?.exact)) {\n        throw new HootDomError(\n            `cannot call \\`queryOne\\` with 'exact'=${options.exact}: did you mean to use \\`queryAll\\`?`\n        );\n    }\n    return queryAll(target, { ...options, exact: 1 })[0];\n}\n\n/**\n * Performs a {@link queryOne} with the given arguments and returns the {@link DOMRect}\n * of the matching node.\n *\n * There are a few differences with the native {@link Element.getBoundingClientRect}:\n * - rects take their positions relative to the top window element (instead of their\n *  parent `<iframe>` if any);\n * - they can be trimmed to remove padding with the `trimPadding` option.\n *\n * @param {Target} target\n * @param {QueryOptions & QueryRectOptions} [options]\n * @returns {DOMRect}\n */\nexport function queryRect(target, options) {\n    return getNodeRect(queryOne(...arguments), options);\n}\n\n/**\n * Performs a {@link queryOne} with the given arguments and returns the *text* of\n * the matching node.\n *\n * @param {Target} target\n * @param {QueryOptions & QueryTextOptions} [options]\n * @returns {string}\n */\nexport function queryText(target, options) {\n    return getNodeText(queryOne(...arguments), options);\n}\n\n/**\n * Performs a {@link queryOne} with the given arguments and returns the *value* of\n * the matching node.\n *\n * @param {Target} target\n * @param {QueryOptions} [options]\n * @returns {NodeValue}\n */\nexport function queryValue(target, options) {\n    return getNodeValue(queryOne(...arguments));\n}\n\n/**\n * Combination of {@link queryAll} and {@link waitUntil}: waits for a given target\n * to match elements in the DOM and returns the first matching node when it appears\n * (or immediately if it is already present).\n *\n * @see {@link queryAll}\n * @see {@link waitUntil}\n * @param {Target} target\n * @param {QueryOptions & WaitOptions} [options]\n * @returns {Promise<Element>}\n * @example\n *  const button = await waitFor(`button`);\n *  button.click();\n */\nexport function waitFor(target, options) {\n    return waitUntil(() => queryFirst(...arguments), {\n        message: `Could not find elements matching \"${target}\" within %timeout% milliseconds`,\n        ...options,\n    });\n}\n\n/**\n * Opposite of {@link waitFor}: waits for a given target to disappear from the DOM\n * (resolves instantly if the selector is already missing).\n *\n * @see {@link waitFor}\n * @param {Target} target\n * @param {QueryOptions & WaitOptions} [options]\n * @returns {Promise<number>}\n * @example\n *  await waitForNone(`button`);\n */\nexport function waitForNone(target, options) {\n    let count = 0;\n    return waitUntil(\n        () => {\n            count = queryAll(...arguments).length;\n            return !count;\n        },\n        {\n            message: () =>\n                `Could still find ${count} elements matching \"${target}\" after %timeout% milliseconds`,\n            ...options,\n        }\n    );\n}\n", "/** @odoo-module */\n\nimport { HootDomError, getTag, isFirefox, isIterable } from \"../hoot_dom_utils\";\nimport {\n    getActiveElement,\n    getDocument,\n    getNextFocusableElement,\n    getNodeRect,\n    getNodeValue,\n    getPreviousFocusableElement,\n    getWindow,\n    isCheckable,\n    isEditable,\n    isEventTarget,\n    isNode,\n    isNodeFocusable,\n    isNodeVisible,\n    parseDimensions,\n    parsePosition,\n    queryAll,\n    queryFirst,\n    setDimensions,\n    toSelector,\n} from \"./dom\";\n\n/**\n * @typedef {Target | Promise<Target>} AsyncTarget\n *\n * @typedef {\"auto\" | \"blur\" | \"enter\" | \"tab\" | false} ConfirmAction\n *\n * @typedef {{\n *  cancel: (options?: EventOptions) => Promise<EventList>;\n *  drop: (to?: AsyncTarget, options?: PointerOptions) => Promise<EventList>;\n *  moveTo: (to?: AsyncTarget, options?: PointerOptions) => Promise<DragHelpers>;\n * }} DragHelpers\n *\n * @typedef {import(\"./dom\").Position} Position\n *\n * @typedef {import(\"./dom\").Dimensions} Dimensions\n *\n * @typedef {((ev: Event) => boolean) | EventType} EventListPredicate\n *\n * @typedef {{}} EventOptions generic event options\n *\n * @typedef {{\n *  clientX: number;\n *  clientY: number;\n *  pageX: number;\n *  pageY: number;\n *  screenX: number;\n *  screenY: number;\n * }} EventPosition\n *\n * @typedef {keyof HTMLElementEventMap | keyof WindowEventMap} EventType\n *\n * @typedef {EventOptions & {\n *  confirm?: ConfirmAction;\n *  composition?: boolean;\n *  instantly?: boolean;\n * }} FillOptions\n *\n * @typedef {string | number | MaybeIterable<File>} InputValue\n *\n * @typedef {EventOptions & KeyboardEventInit} KeyboardOptions\n *\n * @typedef {string | string[]} KeyStrokes\n *\n * @typedef {EventOptions & QueryOptions & {\n *  button?: number,\n *  position?: Side | `${Side}-${Side}` | Position;\n *  relative?: boolean;\n * }} PointerOptions\n *\n * @typedef {import(\"./dom\").QueryOptions} QueryOptions\n *\n * @typedef {EventOptions & {\n *  target: AsyncTarget;\n * }} SelectOptions\n *\n * @typedef {\"bottom\" | \"left\" | \"right\" | \"top\"} Side\n */\n\n/**\n * @template [T=EventInit]\n * @typedef {T & {\n *  target: EventTarget;\n *  type: EventType;\n * }} FullEventInit\n */\n\n/**\n * @template T\n * @typedef {T | Iterable<T>} MaybeIterable\n */\n\n/**\n * @template [T=Node]\n * @typedef {import(\"./dom\").Target<T>} Target\n */\n\n//-----------------------------------------------------------------------------\n// Global\n//-----------------------------------------------------------------------------\n\nconst {\n    AnimationEvent,\n    ClipboardEvent,\n    CompositionEvent,\n    console: { dir: $dir, groupCollapsed: $groupCollapsed, groupEnd: $groupEnd, log: $log },\n    DataTransfer,\n    document,\n    DragEvent,\n    ErrorEvent,\n    Event,\n    FocusEvent,\n    KeyboardEvent,\n    Math: { ceil: $ceil, max: $max, min: $min },\n    MouseEvent,\n    Number: { isInteger: $isInteger, isNaN: $isNaN, parseFloat: $parseFloat },\n    Object: { assign: $assign, values: $values },\n    PointerEvent,\n    PromiseRejectionEvent,\n    String,\n    SubmitEvent,\n    Touch,\n    TouchEvent,\n    TypeError,\n    WheelEvent,\n} = globalThis;\n/** @type {Document[\"createRange\"]} */\nconst $createRange = document.createRange.bind(document);\n/** @type {Document[\"hasFocus\"]} */\nconst $hasFocus = document.hasFocus.bind(document);\n\n//-----------------------------------------------------------------------------\n// Internal\n//-----------------------------------------------------------------------------\n\n/**\n * @param {EventTarget} target\n * @param {EventType} type\n */\nconst catchNextEvent = (target, type) =>\n    new Promise((resolve) => {\n        target.addEventListener(\n            type,\n            (event) => {\n                getCurrentEvents().push(event);\n                resolve(event);\n            },\n            { once: true }\n        );\n    });\n\n/**\n * @param {HTMLInputElement | HTMLTextAreaElement} target\n */\nconst deleteSelection = (target) => {\n    const { selectionStart, selectionEnd, value } = target;\n    return value.slice(0, selectionStart) + value.slice(selectionEnd);\n};\n\n/**\n *\n * @param {EventTarget} target\n * @param {EventType} eventType\n * @param {PointerEventInit} eventInit\n * @param {{\n *  mouse?: [EventType, MouseEventInit];\n *  touch?: [EventType, TouchEventInit];\n * }} additionalEvents\n */\nconst dispatchPointerEvent = async (target, eventType, eventInit, { mouse, touch }) => {\n    const pointerEvent = await dispatch(target, eventType, eventInit);\n    let prevented = isPrevented(pointerEvent);\n    if (hasTouch()) {\n        if (touch && runTime.pointerDownTarget) {\n            const [touchEventType, touchEventInit] = touch;\n            await dispatch(runTime.pointerDownTarget, touchEventType, touchEventInit || eventInit);\n        }\n    } else {\n        if (mouse && !prevented) {\n            const [mouseEventType, mouseEventInit] = mouse;\n            const mouseEvent = await dispatch(target, mouseEventType, mouseEventInit || eventInit);\n            prevented = isPrevented(mouseEvent);\n        }\n    }\n    return prevented;\n};\n\n/**\n * @param {Iterable<Event>} events\n * @param {EventType} eventType\n * @param {EventInit} eventInit\n */\nconst dispatchRelatedEvents = async (events, eventType, eventInit) => {\n    for (const event of events) {\n        if (!event.target || isPrevented(event)) {\n            break;\n        }\n        await dispatch(event.target, eventType, eventInit);\n    }\n};\n\n/**\n * @template T\n * @param {MaybeIterable<T>} value\n * @returns {T[]}\n */\nconst ensureArray = (value) => (isIterable(value) ? [...value] : [value]);\n\nconst getCurrentEvents = () => {\n    const eventType = currentEventTypes.at(-1);\n    if (!eventType) {\n        return [];\n    }\n    currentEvents[eventType] ||= [];\n    return currentEvents[eventType];\n};\n\nconst getDefaultRunTimeValue = () => ({\n    // Composition\n    isComposing: false,\n\n    // Drag & drop\n    canStartDrag: false,\n    isDragging: false,\n    lastDragOverCancelled: false,\n\n    // Pointer\n    clickCount: 0,\n    key: null,\n    pointerDownTarget: null,\n    pointerDownTimeout: 0,\n    pointerTarget: null,\n    /** @type {EventPosition | {}} */\n    position: {},\n    previousPointerDownTarget: null,\n    previousPointerTarget: null,\n    /** @type {EventPosition | {}} */\n    touchStartPosition: {},\n\n    // File\n    fileInput: null,\n\n    // Buttons\n    buttons: 0,\n\n    // Modifier keys\n    modifierKeys: {\n        altKey: false,\n        ctrlKey: false,\n        metaKey: false,\n        shiftKey: false,\n    },\n});\n\n/**\n * Returns the list of nodes containing n2 (included) that do not contain n1.\n *\n * @param {Element} [el1]\n * @param {Element} [el2]\n */\nconst getDifferentParents = (el1, el2) => {\n    if (!el1 && !el2) {\n        // No given elements => no parents\n        return [];\n    } else if (!el1 && el2) {\n        // No first element => only parents of second element\n        [el1, el2] = [el2, el1];\n    }\n    const parents = [el2 || el1];\n    while (parents[0].parentElement) {\n        const parent = parents[0].parentElement;\n        if (el2 && parent.contains(el1)) {\n            break;\n        }\n        parents.unshift(parent);\n    }\n    return parents;\n};\n\n/**\n * @template {typeof Event} T\n * @param {EventType} eventType\n * @returns {[T, ((attrs: FullEventInit) => EventInit), number]}\n */\nconst getEventConstructor = (eventType) => {\n    switch (eventType) {\n        // Mouse events\n        case \"dblclick\":\n        case \"mousedown\":\n        case \"mouseup\":\n        case \"mousemove\":\n        case \"mouseover\":\n        case \"mouseout\":\n            return [MouseEvent, mapMouseEvent, BUBBLES | CANCELABLE];\n        case \"mouseenter\":\n        case \"mouseleave\":\n            return [MouseEvent, mapMouseEvent];\n\n        // Pointer events\n        case \"auxclick\":\n        case \"click\":\n        case \"contextmenu\":\n        case \"pointerdown\":\n        case \"pointerup\":\n        case \"pointermove\":\n        case \"pointerover\":\n        case \"pointerout\":\n            return [PointerEvent, mapPointerEvent, BUBBLES | CANCELABLE];\n        case \"pointerenter\":\n        case \"pointerleave\":\n        case \"pointercancel\":\n            return [PointerEvent, mapPointerEvent];\n\n        // Focus events\n        case \"blur\":\n        case \"focus\":\n            return [FocusEvent, mapEvent];\n        case \"focusin\":\n        case \"focusout\":\n            return [FocusEvent, mapEvent, BUBBLES];\n\n        // Clipboard events\n        case \"cut\":\n        case \"copy\":\n        case \"paste\":\n            return [ClipboardEvent, mapEvent, BUBBLES];\n\n        // Keyboard events\n        case \"keydown\":\n        case \"keyup\":\n            return [KeyboardEvent, mapKeyboardEvent, BUBBLES | CANCELABLE];\n\n        // Drag events\n        case \"drag\":\n        case \"dragend\":\n        case \"dragenter\":\n        case \"dragstart\":\n        case \"dragleave\":\n        case \"dragover\":\n        case \"drop\":\n            return [DragEvent, mapEvent, BUBBLES];\n\n        // Input events\n        case \"beforeinput\":\n            return [InputEvent, mapInputEvent, BUBBLES | CANCELABLE];\n        case \"input\":\n            return [InputEvent, mapInputEvent, BUBBLES];\n\n        // Composition events\n        case \"compositionstart\":\n        case \"compositionend\":\n            return [CompositionEvent, mapEvent, BUBBLES];\n\n        // Selection events\n        case \"select\":\n        case \"selectionchange\":\n            return [Event, mapEvent, BUBBLES];\n\n        // Touch events\n        case \"touchstart\":\n        case \"touchend\":\n        case \"touchmove\":\n            return [TouchEvent, mapTouchEvent, BUBBLES | CANCELABLE];\n        case \"touchcancel\":\n            return [TouchEvent, mapTouchEvent, BUBBLES];\n\n        // Resize events\n        case \"resize\":\n            return [Event, mapEvent];\n\n        // Submit events\n        case \"submit\":\n            return [SubmitEvent, mapEvent, BUBBLES | CANCELABLE];\n\n        // Wheel events\n        case \"wheel\":\n            return [WheelEvent, mapWheelEvent, BUBBLES];\n\n        // Animation events\n        case \"animationcancel\":\n        case \"animationend\":\n        case \"animationiteration\":\n        case \"animationstart\": {\n            return [AnimationEvent, mapEvent, BUBBLES | CANCELABLE];\n        }\n\n        // Error events\n        case \"error\":\n            return [ErrorEvent, mapEvent];\n        case \"unhandledrejection\":\n            return [PromiseRejectionEvent, mapEvent, CANCELABLE];\n\n        // Unload events (BeforeUnloadEvent cannot be constructed)\n        case \"beforeunload\":\n            return [Event, mapEvent, CANCELABLE];\n        case \"unload\":\n            return [Event, mapEvent];\n\n        // Default: base Event constructor\n        default:\n            return [Event, mapEvent, BUBBLES];\n    }\n};\n\n/**\n * @param {Node} [a]\n * @param {Node} [b]\n */\nconst getFirstCommonParent = (a, b) => {\n    if (!a || !b || a.ownerDocument !== b.ownerDocument) {\n        return null;\n    }\n\n    const range = document.createRange();\n    range.setStart(a, 0);\n    range.setEnd(b, 0);\n\n    if (range.collapsed) {\n        // Re-arranges range if the first node comes after the second\n        range.setStart(b, 0);\n        range.setEnd(a, 0);\n    }\n\n    return range.commonAncestorContainer;\n};\n\n/**\n * @param {HTMLElement} element\n * @param {PointerOptions} [options]\n */\nconst getPosition = (element, options) => {\n    const { position, relative } = options || {};\n    const isString = typeof position === \"string\";\n    const [posX, posY] = parsePosition(position);\n\n    if (!isString && !relative && !$isNaN(posX) && !$isNaN(posY)) {\n        // Absolute position\n        return toEventPosition(posX, posY, position);\n    }\n\n    const { x, y, width, height } = getNodeRect(element);\n    let clientX = x;\n    let clientY = y;\n\n    if (isString) {\n        const positions = position.split(\"-\");\n\n        // X position\n        if (positions.includes(\"left\")) {\n            clientX -= 1;\n        } else if (positions.includes(\"right\")) {\n            clientX += $ceil(width) + 1;\n        } else {\n            clientX += width / 2;\n        }\n\n        // Y position\n        if (positions.includes(\"top\")) {\n            clientY -= 1;\n        } else if (positions.includes(\"bottom\")) {\n            clientY += $ceil(height) + 1;\n        } else {\n            clientY += height / 2;\n        }\n    } else {\n        // X position\n        if ($isNaN(posX)) {\n            clientX += width / 2;\n        } else {\n            if (relative) {\n                clientX += posX || 0;\n            } else {\n                clientX = posX || 0;\n            }\n        }\n\n        // Y position\n        if ($isNaN(posY)) {\n            clientY += height / 2;\n        } else {\n            if (relative) {\n                clientY += posY || 0;\n            } else {\n                clientY = posY || 0;\n            }\n        }\n    }\n\n    return toEventPosition(clientX, clientY, position);\n};\n\n/**\n * @param {Node} target\n */\nconst getStringSelection = (target) =>\n    $isInteger(target.selectionStart) &&\n    $isInteger(target.selectionEnd) &&\n    [target.selectionStart, target.selectionEnd].join(\",\");\n\n/**\n * @param {Node} node\n * @param  {...string} tagNames\n */\nconst hasTagName = (node, ...tagNames) => tagNames.includes(getTag(node));\n\nconst hasTouch = () =>\n    globalThis.ontouchstart !== undefined || globalThis.matchMedia(\"(pointer:coarse)\").matches;\n\n/**\n * @param {EventTarget | EventPosition} target\n * @param {PointerOptions} [options]\n */\nconst isDifferentPosition = (target, options) => {\n    const previous = runTime.position;\n    const next = isNode(target) ? getPosition(target, options) : target;\n    for (const key in next) {\n        if (previous[key] !== next[key]) {\n            return true;\n        }\n    }\n    return false;\n};\n\n/**\n * @param {unknown} value\n */\nconst isNil = (value) => value === null || value === undefined;\n\n/**\n * @param {Event} event\n */\nconst isPrevented = (event) => event && event.defaultPrevented;\n\n/**\n * @param {KeyStrokes} keyStrokes\n * @param {KeyboardEventInit} [options]\n * @returns {KeyboardEventInit}\n */\nconst parseKeyStrokes = (keyStrokes, options) =>\n    (isIterable(keyStrokes) ? [...keyStrokes] : [keyStrokes]).map((key) => {\n        const lower = key.toLowerCase();\n        return {\n            ...options,\n            key: lower.length === 1 ? key : KEY_ALIASES[lower] || key,\n        };\n    });\n\n/**\n * Redirects all 'submit' events to explicit network requests.\n *\n * This allows the `mockFetch` helper to take control over submit requests.\n *\n * @param {SubmitEvent} ev\n */\nconst redirectSubmit = (ev) => {\n    if (isPrevented(ev)) {\n        return;\n    }\n\n    ev.preventDefault();\n\n    /** @type {HTMLFormElement} */\n    const form = ev.target;\n\n    globalThis.fetch(form.action, {\n        method: form.method,\n        body: new FormData(form, ev.submitter),\n    });\n};\n\n/**\n * @param {PointerEventInit} eventInit\n * @param {boolean} toggle\n */\nconst registerButton = (eventInit, toggle) => {\n    let value = 0;\n    switch (eventInit.button) {\n        case btn.LEFT: {\n            // Main button (left button)\n            value = 1;\n            break;\n        }\n        case btn.MIDDLE: {\n            // Auxiliary button (middle button)\n            value = 4;\n            break;\n        }\n        case btn.RIGHT: {\n            // Secondary button (right button)\n            value = 2;\n            break;\n        }\n        case btn.BACK: {\n            // Fourth button (Browser Back)\n            value = 8;\n            break;\n        }\n        case btn.FORWARD: {\n            // Fifth button (Browser Forward)\n            value = 16;\n            break;\n        }\n    }\n\n    runTime.buttons = $max(runTime.buttons + (toggle ? value : -value), 0);\n};\n\n/**\n * @param {Event} ev\n */\nconst registerFileInput = ({ target }) => {\n    if (getTag(target) === \"input\" && target.type === \"file\") {\n        runTime.fileInput = target;\n    } else {\n        runTime.fileInput = null;\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {string} initialValue\n * @param {ConfirmAction} confirmAction\n */\nconst registerForChange = async (target, initialValue, confirmAction) => {\n    const triggerChange = () => {\n        removeChangeTargetListeners();\n\n        if (target.value !== initialValue) {\n            afterNextDispatch = () => dispatch(target, \"change\");\n        }\n    };\n\n    confirmAction &&= confirmAction.toLowerCase();\n    if (confirmAction === \"auto\") {\n        confirmAction = getTag(target) === \"input\" ? \"enter\" : \"blur\";\n    }\n    if (confirmAction === \"enter\") {\n        if (getTag(target) === \"input\") {\n            changeTargetListeners.push(\n                on(\n                    target,\n                    \"keydown\",\n                    (ev) => !isPrevented(ev) && ev.key === \"Enter\" && triggerChange()\n                )\n            );\n        } else {\n            throw new HootDomError(`\"enter\" confirm action is only supported on <input/> elements`);\n        }\n    }\n\n    changeTargetListeners.push(\n        on(target, \"blur\", triggerChange),\n        on(target, \"change\", removeChangeTargetListeners)\n    );\n\n    switch (confirmAction) {\n        case \"blur\": {\n            await _click(getDocument(target).body, {\n                position: { x: 0, y: 0 },\n            });\n            break;\n        }\n        case \"enter\": {\n            await _press(target, { key: \"Enter\" });\n            break;\n        }\n        case \"tab\": {\n            await _press(target, { key: \"Tab\" });\n            break;\n        }\n    }\n};\n\n/**\n * @param {KeyboardEventInit} eventInit\n * @param {boolean} toggle\n */\nconst registerSpecialKey = (eventInit, toggle) => {\n    switch (eventInit.key) {\n        case \"Alt\": {\n            runTime.modifierKeys.altKey = toggle;\n            break;\n        }\n        case \"Control\": {\n            runTime.modifierKeys.ctrlKey = toggle;\n            break;\n        }\n        case \"Meta\": {\n            runTime.modifierKeys.metaKey = toggle;\n            break;\n        }\n        case \"Shift\": {\n            runTime.modifierKeys.shiftKey = toggle;\n            break;\n        }\n    }\n};\n\nconst removeChangeTargetListeners = () => {\n    while (changeTargetListeners.length) {\n        changeTargetListeners.pop()();\n    }\n};\n\n/**\n * @param {HTMLElement | null} target\n */\nconst setPointerDownTarget = (target) => {\n    if (runTime.pointerDownTarget) {\n        runTime.previousPointerDownTarget = runTime.pointerDownTarget;\n    }\n    runTime.pointerDownTarget = target;\n    runTime.canStartDrag = false;\n};\n\n/**\n * @param {HTMLElement | null} target\n * @param {PointerOptions} [options]\n */\nconst setPointerTarget = async (target, options) => {\n    runTime.previousPointerTarget = runTime.pointerTarget;\n    runTime.pointerTarget = target;\n\n    if (runTime.pointerTarget !== runTime.previousPointerTarget && runTime.canStartDrag) {\n        /**\n         * Special action: drag start\n         *  On: unprevented 'pointerdown' on a draggable element (DESKTOP ONLY)\n         *  Do: triggers a 'dragstart' event\n         */\n        const dragStartEvent = await dispatch(runTime.previousPointerTarget, \"dragstart\");\n\n        runTime.isDragging = !isPrevented(dragStartEvent);\n        runTime.canStartDrag = false;\n    }\n\n    runTime.position = target && getPosition(target, options);\n};\n\n/**\n * @param {string} type\n */\nconst setupEvents = (type) => {\n    currentEventTypes.push(type);\n\n    return async () => {\n        const events = new EventList(getCurrentEvents());\n        const currentType = currentEventTypes.pop();\n        delete currentEvents[currentType];\n        if (!allowLogs) {\n            return events;\n        }\n        const groupName = [`${type}: dispatched`, events.length, `events`];\n        $groupCollapsed(...groupName);\n        for (const event of events) {\n            /** @type {(keyof typeof LOG_COLORS)[]} */\n            const colors = [\"blue\"];\n\n            const typeList = [event.type];\n            if (event.key) {\n                typeList.push(event.key);\n            } else if (event.button) {\n                typeList.push(event.button);\n            }\n            [...Array(typeList.length)].forEach(() => colors.push(\"orange\"));\n\n            const typeString = typeList.map((t) => `%c\"${t}\"%c`).join(\", \");\n            let message = `%c${event.constructor.name}%c<${typeString}>`;\n            if (event.__bubbleCount) {\n                message += ` (${event.__bubbleCount})`;\n            }\n            const target = event.__originalTarget || event.target;\n            if (isNode(target)) {\n                const targetParts = toSelector(target, { object: true });\n                colors.push(\"blue\");\n                if (targetParts.id) {\n                    colors.push(\"orange\");\n                }\n                if (targetParts.class) {\n                    colors.push(\"lightBlue\");\n                }\n                const targetString = $values(targetParts)\n                    .map((part) => `%c${part}%c`)\n                    .join(\"\");\n                message += ` @${targetString}`;\n            }\n            const messageColors = colors.flatMap((color) => [\n                `color: ${LOG_COLORS[color]}; font-weight: normal`,\n                `color: ${LOG_COLORS.reset}`,\n            ]);\n\n            $groupCollapsed(message, ...messageColors);\n            $dir(event);\n            $log(target);\n            $groupEnd();\n        }\n        $groupEnd();\n\n        return events;\n    };\n};\n\n/**\n * @param {number} clientX\n * @param {number} clientY\n * @param {Partial<EventPosition>} [position]\n */\nconst toEventPosition = (clientX, clientY, position) => {\n    clientX ||= 0;\n    clientY ||= 0;\n    return {\n        clientX,\n        clientY,\n        pageX: position?.pageX ?? clientX,\n        pageY: position?.pageY ?? clientY,\n        screenX: position?.screenX ?? clientX,\n        screenY: position?.screenY ?? clientY,\n    };\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerEventInit} pointerInit\n */\nconst triggerClick = async (target, pointerInit) => {\n    if (target.disabled) {\n        return;\n    }\n    const eventType = (pointerInit.button ?? 0) === btn.LEFT ? \"click\" : \"auxclick\";\n    const clickEvent = await dispatch(target, eventType, pointerInit);\n    if (isPrevented(clickEvent)) {\n        return;\n    }\n    if (isFirefox()) {\n        // Thanks Firefox\n        switch (getTag(target)) {\n            case \"label\": {\n                /**\n                 * @firefox\n                 * Special action: label 'Click'\n                 *  On: unprevented 'click' on a <label/>\n                 *  Do: triggers a 'click' event on the first <input/> descendant\n                 */\n                target = target.control;\n                if (target) {\n                    await triggerClick(target, pointerInit);\n                }\n                break;\n            }\n            case \"option\": {\n                /**\n                 * @firefox\n                 * Special action: option 'Click'\n                 *  On: unprevented 'click' on an <option/>\n                 *  Do: triggers a 'change' event on the parent <select/>\n                 */\n                const parent = target.parentElement;\n                if (parent && getTag(parent) === \"select\") {\n                    await dispatch(parent, \"change\");\n                }\n                break;\n            }\n        }\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {DragEventInit} eventInit\n */\nconst triggerDrag = async (target, eventInit) => {\n    await dispatch(target, \"drag\", eventInit);\n    // Only \"dragover\" being prevented is taken into account for \"drop\" events\n    const dragOverEvent = await dispatch(target, \"dragover\", eventInit);\n    runTime.lastDragOverCancelled = isPrevented(dragOverEvent);\n};\n\n/**\n * @param {EventTarget} target\n */\nconst triggerFocus = async (target) => {\n    const previous = getActiveElement(target);\n    if (previous === target) {\n        return;\n    }\n    if (previous !== target.ownerDocument.body) {\n        if ($hasFocus() && isNodeVisible(previous)) {\n            catchNextEvent(previous, \"focusout\");\n        }\n        // If document is focused, this will trigger a trusted \"blur\" event\n        previous.blur();\n        if (!$hasFocus()) {\n            // When document is not focused: manually trigger a \"blur\" event\n            const eventInit = { relatedTarget: target };\n            await dispatch(previous, \"blur\", eventInit);\n            await dispatch(previous, \"focusout\", eventInit);\n        }\n    }\n    if (isNodeFocusable(target)) {\n        const previousSelection = getStringSelection(target);\n\n        // If document is focused, this will trigger a trusted \"focus\" event\n        if ($hasFocus() && isNodeVisible(target)) {\n            catchNextEvent(target, \"focusin\");\n        }\n        target.focus();\n        if (!$hasFocus()) {\n            // When document is not focused: manually trigger a \"focus\" event\n            const eventInit = { relatedTarget: previous };\n            await dispatch(target, \"focus\", eventInit);\n            await dispatch(target, \"focusin\", eventInit);\n        }\n\n        if (previousSelection && previousSelection === getStringSelection(target)) {\n            target.selectionStart = target.selectionEnd = target.value.length;\n        }\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {FillOptions} options\n */\nconst _clear = async (target, options) => {\n    // Inputs and text areas\n    const initialValue = target.value;\n\n    // Simulates 2 key presses:\n    // - Control + A: selects all the text\n    // - Backspace: deletes the text\n    fullClear = true;\n    await _press(target, { ctrlKey: true, key: \"a\" });\n    await _press(target, { key: \"Backspace\" });\n    fullClear = false;\n\n    await registerForChange(target, initialValue, options?.confirm);\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerOptions} [option]\n */\nconst _click = async (target, options) => {\n    await _pointerDown(target, options);\n    await _pointerUp(target, options);\n};\n\n/**\n * @param {EventTarget} target\n * @param {InputValue} value\n * @param {FillOptions} [options]\n */\nconst _fill = async (target, value, options) => {\n    const initialValue = target.value;\n\n    if (getTag(target) === \"input\") {\n        switch (target.type) {\n            case \"color\": {\n                target.value = String(value);\n                await dispatch(target, \"input\");\n                await dispatch(target, \"change\");\n                return;\n            }\n            case \"file\": {\n                const dataTransfer = new DataTransfer();\n                const files = ensureArray(value);\n                if (files.length > 1 && !target.multiple) {\n                    throw new HootDomError(`input[type=\"file\"] does not support multiple files`);\n                }\n                for (const file of files) {\n                    if (!(file instanceof File)) {\n                        throw new TypeError(`file input only accept 'File' objects`);\n                    }\n                    dataTransfer.items.add(file);\n                }\n                target.files = dataTransfer.files;\n\n                await dispatch(target, \"change\");\n                return;\n            }\n            case \"range\": {\n                const numberValue = $parseFloat(value);\n                if ($isNaN(numberValue)) {\n                    throw new TypeError(`input[type=\"range\"] only accept 'number' values`);\n                }\n\n                target.value = String(numberValue);\n                await dispatch(target, \"input\");\n                await dispatch(target, \"change\");\n                return;\n            }\n        }\n    }\n\n    if (options?.instantly) {\n        // Simulates filling the clipboard with the value (can be from external source)\n        globalThis.navigator.clipboard.writeText(value).catch();\n        await _press(target, { ctrlKey: true, key: \"v\" });\n    } else {\n        if (options?.composition) {\n            runTime.isComposing = true;\n            // Simulates the start of a composition\n            await dispatch(target, \"compositionstart\");\n        }\n        for (const char of String(value)) {\n            const key = char.toLowerCase();\n            await _press(target, { key, shiftKey: key !== char });\n        }\n        if (options?.composition) {\n            runTime.isComposing = false;\n            // Simulates the end of a composition\n            await dispatch(target, \"compositionend\");\n        }\n    }\n\n    await registerForChange(target, initialValue, options?.confirm);\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerOptions} options\n */\nconst _hover = async (target, options) => {\n    const isDifferentTarget = target !== runTime.pointerTarget;\n    const previousPosition = runTime.position;\n\n    await setPointerTarget(target, options);\n\n    const { previousPointerTarget: previous, pointerTarget: current } = runTime;\n    if (isDifferentTarget && previous && (!current || !previous.contains(current))) {\n        // Leaves previous target\n        const leaveEventInit = {\n            ...previousPosition,\n            relatedTarget: current,\n        };\n\n        if (runTime.isDragging) {\n            // If dragging, only drag events are triggered\n            await triggerDrag(previous, leaveEventInit);\n            await dispatch(previous, \"dragleave\", leaveEventInit);\n        } else {\n            // Regular case: pointer events are triggered\n            await dispatchPointerEvent(previous, \"pointermove\", leaveEventInit, {\n                mouse: [\"mousemove\"],\n                touch: [\"touchmove\"],\n            });\n            await dispatchPointerEvent(previous, \"pointerout\", leaveEventInit, {\n                mouse: [\"mouseout\"],\n            });\n            const leaveEvents = await Promise.all(\n                getDifferentParents(current, previous).map((element) =>\n                    dispatch(element, \"pointerleave\", leaveEventInit)\n                )\n            );\n            if (!hasTouch()) {\n                await dispatchRelatedEvents(leaveEvents, \"mouseleave\", leaveEventInit);\n            }\n        }\n    }\n\n    if (current) {\n        const enterEventInit = {\n            ...runTime.position,\n            relatedTarget: previous,\n        };\n        if (runTime.isDragging) {\n            // If dragging, only drag events are triggered\n            if (isDifferentTarget) {\n                await dispatch(target, \"dragenter\", enterEventInit);\n            }\n            await triggerDrag(target, enterEventInit);\n        } else {\n            // Regular case: pointer events are triggered\n            if (isDifferentTarget) {\n                await dispatchPointerEvent(target, \"pointerover\", enterEventInit, {\n                    mouse: [\"mouseover\"],\n                });\n                const enterEvents = await Promise.all(\n                    getDifferentParents(previous, current).map((element) =>\n                        dispatch(element, \"pointerenter\", enterEventInit)\n                    )\n                );\n                if (!hasTouch()) {\n                    await dispatchRelatedEvents(enterEvents, \"mouseenter\", enterEventInit);\n                }\n            }\n            await dispatchPointerEvent(target, \"pointermove\", enterEventInit, {\n                mouse: [\"mousemove\"],\n                touch: [\"touchmove\"],\n            });\n        }\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerOptions} [options]\n */\nconst _implicitHover = async (target, options) => {\n    if (runTime.pointerTarget !== target || isDifferentPosition(target, options)) {\n        await _hover(target, options);\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {KeyboardEventInit} eventInit\n */\nconst _keyDown = async (target, eventInit) => {\n    registerSpecialKey(eventInit, true);\n\n    const repeat =\n        typeof eventInit.repeat === \"boolean\" ? eventInit.repeat : runTime.key === eventInit.key;\n    runTime.key = eventInit.key;\n    const keyDownEvent = await dispatch(target, \"keydown\", { ...eventInit, repeat });\n\n    if (isPrevented(keyDownEvent)) {\n        return;\n    }\n\n    /**\n     * @param {string} toInsert\n     * @param {string} type\n     */\n    const insertValue = (toInsert, type) => {\n        const { selectionStart, selectionEnd, value } = target;\n        inputData = toInsert;\n        inputType = type;\n        if (isNil(selectionStart) && isNil(selectionEnd)) {\n            nextValue += toInsert;\n        } else {\n            nextValue = value.slice(0, selectionStart) + toInsert + value.slice(selectionEnd);\n            if (selectionStart === selectionEnd) {\n                nextSelectionStart = nextSelectionEnd = selectionStart + 1;\n            }\n        }\n    };\n\n    const { ctrlKey, key, shiftKey } = keyDownEvent;\n    let inputData = null;\n    let inputType = null;\n    let nextSelectionEnd = null;\n    let nextSelectionStart = null;\n    let nextValue = target.value;\n\n    if (isEditable(target)) {\n        switch (key) {\n            case \"ArrowDown\":\n            case \"ArrowLeft\":\n            case \"ArrowUp\":\n            case \"ArrowRight\": {\n                const { selectionStart, selectionEnd, value } = target;\n                if (isNil(selectionStart) || isNil(selectionEnd)) {\n                    break;\n                }\n                const start = key === \"ArrowLeft\" || key === \"ArrowUp\";\n                let selectionTarget;\n                if (ctrlKey) {\n                    // Move to the start/end of the line\n                    selectionTarget = start ? 0 : value.length;\n                } else {\n                    // Move the cursor left or right\n                    selectionTarget = start ? selectionStart - 1 : selectionEnd + 1;\n                }\n                target.selectionStart = target.selectionEnd = $max(\n                    $min(selectionTarget, value.length),\n                    0\n                );\n                break;\n            }\n            case \"Backspace\": {\n                const { selectionStart, selectionEnd, value } = target;\n                if (fullClear) {\n                    // Remove all characters\n                    nextValue = \"\";\n                } else if (isNil(selectionStart) || isNil(selectionEnd)) {\n                    // Remove last character\n                    nextValue = value.slice(0, -1);\n                } else if (selectionStart === selectionEnd) {\n                    // Remove previous character from target value\n                    nextValue = value.slice(0, selectionStart - 1) + value.slice(selectionEnd);\n                } else {\n                    // Remove current selection from target value\n                    nextValue = deleteSelection(target);\n                }\n                inputType = \"deleteContentBackward\";\n                break;\n            }\n            case \"Delete\": {\n                const { selectionStart, selectionEnd, value } = target;\n                if (fullClear) {\n                    // Remove all characters\n                    nextValue = \"\";\n                } else if (isNil(selectionStart) || isNil(selectionEnd)) {\n                    // Remove first character\n                    nextValue = value.slice(1);\n                } else if (selectionStart === selectionEnd) {\n                    // Remove next character from target value\n                    nextValue = value.slice(0, selectionStart) + value.slice(selectionEnd + 1);\n                } else {\n                    // Remove current selection from target value\n                    nextValue = deleteSelection(target);\n                }\n                inputType = \"deleteContentForward\";\n                break;\n            }\n            case \"Enter\": {\n                if (target.tagName === \"TEXTAREA\") {\n                    // Insert new line\n                    insertValue(\"\\n\", \"insertLineBreak\");\n                }\n                break;\n            }\n            default: {\n                if (key.length === 1 && !ctrlKey) {\n                    // Character coming from the keystroke\n                    // ! TODO: Doesn't work with non-roman locales\n                    insertValue(\n                        shiftKey ? key.toUpperCase() : key.toLowerCase(),\n                        runTime.isComposing ? \"insertCompositionText\" : \"insertText\"\n                    );\n                }\n            }\n        }\n    }\n\n    switch (key) {\n        case \"a\": {\n            if (ctrlKey) {\n                // Select all\n                if (isEditable(target)) {\n                    await dispatch(target, \"select\");\n                    if (!isNil(target.selectionStart) && !isNil(target.selectionEnd)) {\n                        target.selectionStart = 0;\n                        target.selectionEnd = target.value.length;\n                    }\n                } else {\n                    const selection = globalThis.getSelection();\n                    const range = $createRange();\n                    range.selectNodeContents(target);\n                    selection.removeAllRanges();\n                    selection.addRange(range);\n                }\n            }\n            break;\n        }\n        /**\n         * Special action: copy\n         *  On: unprevented 'Control + c' keydown\n         *  Do: copy current selection to clipboard\n         */\n        case \"c\": {\n            if (ctrlKey) {\n                // Get selection from window\n                const text = globalThis.getSelection().toString();\n                globalThis.navigator.clipboard.writeText(text).catch();\n\n                await dispatch(target, \"copy\", {\n                    clipboardData: eventInit.dataTransfer || new DataTransfer(),\n                });\n            }\n            break;\n        }\n        case \"Enter\": {\n            const tag = getTag(target);\n            const parentForm = target.closest(\"form\");\n            if (parentForm && target.type !== \"button\") {\n                /**\n                 * Special action: <form> 'Enter'\n                 *  On: unprevented 'Enter' keydown on any element that\n                 *      is not a <button type=\"button\"/> in a form element\n                 *  Do: triggers a 'submit' event on the form\n                 */\n                await dispatch(parentForm, \"submit\");\n            } else if (\n                !keyDownEvent.repeat &&\n                (tag === \"a\" || tag === \"button\" || (tag === \"input\" && target.type === \"button\"))\n            ) {\n                /**\n                 * Special action: <a>, <button> or <input type=\"button\"> 'Enter'\n                 *  On: unprevented and unrepeated 'Enter' keydown on mentioned elements\n                 *  Do: triggers a 'click' event on the element\n                 */\n                await dispatch(target, \"click\", { button: btn.LEFT });\n            }\n            break;\n        }\n        case \"Escape\": {\n            runTime.isDragging = false;\n            break;\n        }\n        /**\n         * Special action: shift focus\n         *  On: unprevented 'Tab' keydown\n         *  Do: focus next (or previous with 'Shift') focusable element\n         */\n        case \"Tab\": {\n            const next = shiftKey\n                ? getPreviousFocusableElement({ tabbable: true })\n                : getNextFocusableElement({ tabbable: true });\n            if (next) {\n                await triggerFocus(next);\n            }\n            break;\n        }\n        /**\n         * Special action: paste\n         *  On: unprevented 'Control + v' keydown on editable element\n         *  Do: paste current clipboard content to current element\n         */\n        case \"v\": {\n            if (ctrlKey && isEditable(target)) {\n                // Set target value (if possible)\n                try {\n                    nextValue = await globalThis.navigator.clipboard.readText();\n                } catch (err) {}\n                inputType = \"insertFromPaste\";\n\n                await dispatch(target, \"paste\", {\n                    clipboardData: eventInit.dataTransfer || new DataTransfer(),\n                });\n            }\n            break;\n        }\n        /**\n         * Special action: cut\n         *  On: unprevented 'Control + x' keydown on editable element\n         *  Do: cut current selection to clipboard and remove selection\n         */\n        case \"x\": {\n            if (ctrlKey && isEditable(target)) {\n                // Get selection from window\n                const text = globalThis.getSelection().toString();\n                globalThis.navigator.clipboard.writeText(text).catch();\n\n                nextValue = deleteSelection(target);\n                inputType = \"deleteByCut\";\n\n                await dispatch(target, \"cut\", {\n                    clipboardData: eventInit.dataTransfer || new DataTransfer(),\n                });\n            }\n            break;\n        }\n    }\n\n    if (target.value !== nextValue) {\n        target.value = nextValue;\n        if (!isNil(nextSelectionStart)) {\n            target.selectionStart = nextSelectionStart;\n        }\n        if (!isNil(nextSelectionEnd)) {\n            target.selectionEnd = nextSelectionEnd;\n        }\n        const inputEventInit = {\n            data: inputData,\n            inputType,\n        };\n        const beforeInputEvent = await dispatch(target, \"beforeinput\", inputEventInit);\n        if (!isPrevented(beforeInputEvent)) {\n            await dispatch(target, \"input\", inputEventInit);\n        }\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {KeyboardEventInit} eventInit\n */\nconst _keyUp = async (target, eventInit) => {\n    await dispatch(target, \"keyup\", eventInit);\n\n    runTime.key = null;\n    registerSpecialKey(eventInit, false);\n\n    if (eventInit.key === \" \" && getTag(target) === \"input\" && target.type === \"checkbox\") {\n        /**\n         * Special action: input[type=checkbox] 'Space'\n         *  On: unprevented ' ' keydown on an <input type=\"checkbox\"/>\n         *  Do: triggers a 'click' event on the input\n         */\n        await triggerClick(target, { button: btn.LEFT });\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerOptions} options\n */\nconst _pointerDown = async (target, options) => {\n    setPointerDownTarget(target);\n\n    const pointerDownTarget = runTime.pointerDownTarget;\n    const eventInit = {\n        ...runTime.position,\n        button: options?.button || 0,\n    };\n\n    registerButton(eventInit, true);\n\n    if (pointerDownTarget !== runTime.previousPointerDownTarget) {\n        runTime.clickCount = 0;\n    }\n\n    runTime.touchStartPosition = { ...runTime.position };\n    const prevented = await dispatchPointerEvent(pointerDownTarget, \"pointerdown\", eventInit, {\n        mouse: !pointerDownTarget.disabled && [\n            \"mousedown\",\n            { ...eventInit, detail: runTime.clickCount + 1 },\n        ],\n        touch: [\"touchstart\"],\n    });\n\n    if (prevented) {\n        return;\n    }\n\n    // Focus the element (if focusable)\n    await triggerFocus(target);\n\n    if (eventInit.button === btn.LEFT && !hasTouch() && pointerDownTarget.draggable) {\n        runTime.canStartDrag = true;\n    } else if (eventInit.button === btn.RIGHT) {\n        /**\n         * Special action: context menu\n         *  On: unprevented 'pointerdown' with right click and its related\n         *      event on an element\n         *  Do: triggers a 'contextmenu' event\n         */\n        await dispatch(target, \"contextmenu\", eventInit);\n    }\n};\n\n/**\n * @param {EventTarget} target\n * @param {PointerOptions} options\n */\nconst _pointerUp = async (target, options) => {\n    const pointerDownTarget = runTime.pointerDownTarget;\n    const eventInit = {\n        ...runTime.position,\n        button: options?.button || 0,\n    };\n\n    registerButton(eventInit, false);\n\n    if (runTime.isDragging) {\n        // If dragging, only drag events are triggered\n        runTime.isDragging = false;\n        if (runTime.lastDragOverCancelled) {\n            /**\n             * Special action: drop\n             * - On: prevented 'dragover'\n             * - Do: triggers a 'drop' event on the target\n             */\n            await dispatch(target, \"drop\", eventInit);\n        }\n\n        await dispatch(target, \"dragend\", eventInit);\n        return;\n    }\n\n    const mouseEventInit = {\n        ...eventInit,\n        detail: runTime.clickCount + 1,\n    };\n    await dispatchPointerEvent(target, \"pointerup\", eventInit, {\n        mouse: !target.disabled && [\"mouseup\", mouseEventInit],\n        touch: [\"touchend\"],\n    });\n\n    const touchStartPosition = runTime.touchStartPosition;\n    runTime.touchStartPosition = {};\n\n    if (hasTouch() && isDifferentPosition(touchStartPosition)) {\n        // No further event is trigger: there was a swiping motion since the \"touchstart\"\n        // event.\n        return;\n    }\n\n    let actualTarget;\n    if (hasTouch()) {\n        actualTarget = pointerDownTarget === target && target;\n    } else {\n        actualTarget = getFirstCommonParent(target, pointerDownTarget);\n    }\n    if (actualTarget) {\n        await triggerClick(actualTarget, mouseEventInit);\n        if (mouseEventInit.button === btn.LEFT) {\n            runTime.clickCount++;\n            if (!hasTouch() && runTime.clickCount % 2 === 0) {\n                await dispatch(actualTarget, \"dblclick\", mouseEventInit);\n            }\n        }\n    }\n\n    setPointerDownTarget(null);\n    if (runTime.pointerDownTimeout) {\n        globalThis.clearTimeout(runTime.pointerDownTimeout);\n    }\n    runTime.pointerDownTimeout = globalThis.setTimeout(() => {\n        // Use `globalThis.setTimeout` to potentially make use of the mock timeouts\n        // since the events run in the same temporal context as the tests\n        runTime.clickCount = 0;\n        runTime.pointerDownTimeout = 0;\n    }, DOUBLE_CLICK_DELAY);\n};\n\n/**\n * @param {EventTarget} target\n * @param {KeyboardEventInit} eventInit\n */\nconst _press = async (target, eventInit) => {\n    await _keyDown(target, eventInit);\n    await _keyUp(target, eventInit);\n};\n\n/**\n * @param {EventTarget} target\n * @param {string | number | (string | number)[]} value\n */\nconst _select = async (target, value) => {\n    const values = ensureArray(value).map(String);\n    let found = false;\n    for (const option of target.options) {\n        option.selected = values.includes(option.value);\n        found ||= option.selected;\n    }\n    if (!value) {\n        target.selectedIndex = -1;\n    } else if (!found) {\n        throw new HootDomError(\n            `error when calling \\`select()\\`: no option found with value \"${values.join(\", \")}\"`\n        );\n    }\n    await dispatch(target, \"change\");\n};\n\nconst btn = {\n    LEFT: 0,\n    MIDDLE: 1,\n    RIGHT: 2,\n    BACK: 3,\n    FORWARD: 4,\n};\nconst DEPRECATED_EVENT_PROPERTIES = {\n    keyCode: \"key\",\n    which: \"key\",\n};\nconst DEPRECATED_EVENTS = {\n    keypress: \"keydown\",\n    mousewheel: \"wheel\",\n};\nconst DOUBLE_CLICK_DELAY = 500;\nconst KEY_ALIASES = {\n    // case insensitive aliases\n    alt: \"Alt\",\n    arrowdown: \"ArrowDown\",\n    arrowleft: \"ArrowLeft\",\n    arrowright: \"ArrowRight\",\n    arrowup: \"ArrowUp\",\n    backspace: \"Backspace\",\n    control: \"Control\",\n    delete: \"Delete\",\n    enter: \"Enter\",\n    escape: \"Escape\",\n    meta: \"Meta\",\n    shift: \"Shift\",\n    tab: \"Tab\",\n\n    // Other aliases\n    caps: \"Shift\",\n    cmd: \"Meta\",\n    command: \"Meta\",\n    ctrl: \"Control\",\n    del: \"Delete\",\n    down: \"ArrowDown\",\n    esc: \"Escape\",\n    left: \"ArrowLeft\",\n    right: \"ArrowRight\",\n    space: \" \",\n    up: \"ArrowUp\",\n    win: \"Meta\",\n};\nconst LOG_COLORS = {\n    blue: \"#5db0d7\",\n    orange: \"#f29364\",\n    lightBlue: \"#9bbbdc\",\n    reset: \"inherit\",\n};\n\n/** @type {Record<string, Event[]>} */\nconst currentEvents = {};\n/** @type {string[]} */\nconst currentEventTypes = [];\n/** @type {(() => Promise<void>) | null} */\nlet afterNextDispatch = null;\nlet allowLogs = false;\nlet fullClear = false;\n\n// Keyboard global variables\nconst changeTargetListeners = [];\n\n// Other global variables\nconst runTime = getDefaultRunTimeValue();\n\n//-----------------------------------------------------------------------------\n// Event init attributes mappers\n//-----------------------------------------------------------------------------\n\nconst BUBBLES = 0b001;\nconst CANCELABLE = 0b010;\n\n// Generic mappers\n// ---------------\n\n/**\n * - does not bubble\n * - cannot be canceled\n * @param {FullEventInit} eventInit\n */\nconst mapEvent = (eventInit) => eventInit;\n\n// Pointer, mouse & wheel event mappers\n// ------------------------------------\n\n/**\n * @param {FullEventInit<MouseEventInit>} eventInit\n */\nconst mapMouseEvent = (eventInit) => ({\n    button: -1,\n    buttons: runTime.buttons,\n    clientX: eventInit.clientX ?? eventInit.pageX ?? eventInit.screenX ?? 0,\n    clientY: eventInit.clientY ?? eventInit.pageY ?? eventInit.screenY ?? 0,\n    view: getWindow(),\n    ...runTime.modifierKeys,\n    ...eventInit,\n});\n\n/**\n * @param {FullEventInit<PointerEventInit>} eventInit\n */\nconst mapPointerEvent = (eventInit) => ({\n    ...mapMouseEvent(eventInit),\n    button: btn.LEFT,\n    pointerId: 1,\n    pointerType: hasTouch() ? \"touch\" : \"mouse\",\n    ...eventInit,\n});\n\n/**\n * @param {FullEventInit<WheelEventInit>} eventInit\n */\nconst mapWheelEvent = (eventInit) => ({\n    ...mapMouseEvent(eventInit),\n    button: btn.LEFT,\n    ...eventInit,\n});\n\n// Touch event mappers\n// -------------------\n\n/**\n * @param {FullEventInit<TouchEventInit>} eventInit\n */\nconst mapTouchEvent = (eventInit) => {\n    const touches = eventInit.targetTouches ||\n        eventInit.touches || [new Touch({ identifier: 0, ...eventInit })];\n    return {\n        view: getWindow(),\n        ...eventInit,\n        changedTouches: eventInit.changedTouches || touches,\n        target: eventInit.target,\n        targetTouches: eventInit.targetTouches || touches,\n        touches: eventInit.touches || (eventInit.type === \"touchend\" ? [] : touches),\n    };\n};\n\n// Keyboard & input event mappers\n// ------------------------------\n\n/**\n * @param {FullEventInit<InputEventInit>} eventInit\n */\nconst mapInputEvent = (eventInit) => ({\n    data: null,\n    isComposing: Boolean(runTime.isComposing),\n    view: getWindow(),\n    ...eventInit,\n});\n\n/**\n * @param {FullEventInit<KeyboardEventInit>} eventInit\n */\nconst mapKeyboardEvent = (eventInit) => ({\n    isComposing: Boolean(runTime.isComposing),\n    view: getWindow(),\n    ...runTime.modifierKeys,\n    ...eventInit,\n});\n\n//-----------------------------------------------------------------------------\n// Exports\n//-----------------------------------------------------------------------------\n\n/**\n * Ensures that the given {@link AsyncTarget} is checked.\n *\n * If it is not checked, a click is simulated on the input.\n * If the input is still not checked after the click, an error is thrown.\n *\n * @see {@link click}\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  check(\"input[type=checkbox]\"); // Checks the first <input> checkbox element\n */\nexport async function check(target, options) {\n    const finalizeEvents = setupEvents(\"check\");\n    const element = queryFirst(await target, options);\n    if (!isCheckable(element)) {\n        throw new HootDomError(\n            `cannot call \\`check()\\`: target should be a checkbox or radio input`\n        );\n    }\n\n    const checkTarget = getTag(element) === \"label\" ? element.control : element;\n    if (!checkTarget.checked) {\n        await _implicitHover(element, options);\n        await _click(element, options);\n\n        if (!checkTarget.checked) {\n            throw new HootDomError(\n                `error when calling \\`check()\\`: target is not checked after interaction`\n            );\n        }\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Clears the **value** of the current **active element**.\n *\n * This is done using the following sequence:\n * - pressing \"Control\" + \"A\" to select the whole value;\n * - pressing \"Backspace\" to delete the value;\n * - (optional) triggering a \"change\" event by pressing \"Enter\".\n *\n * @param {FillOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  clear(); // Clears the value of the current active element\n */\nexport async function clear(options) {\n    const finalizeEvents = setupEvents(\"clear\");\n    const element = getActiveElement();\n\n    if (!hasTagName(element, \"select\") && !isEditable(element)) {\n        throw new HootDomError(\n            `cannot call \\`clear()\\`: target should be editable or a <select> element`\n        );\n    }\n\n    if (isEditable(element)) {\n        await _clear(element, options);\n    } else {\n        // Selects\n        await _select(element, \"\");\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a click sequence on the given {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n *  - `pointerdown`\n *  - [desktop] `mousedown`\n *  - [touch] `touchstart`\n *  - [target is not active element] `blur`\n *  - [target is focusable] `focus`\n *  - `pointerup`\n *  - [desktop] `mouseup`\n *  - [touch] `touchend`\n *  - `click`\n *  - `dblclick` if click is not prevented & current click count is even\n *\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  click(\"button\"); // Clicks on the first <button> element\n */\nexport async function click(target, options) {\n    const finalizeEvents = setupEvents(\"click\");\n    const element = queryFirst(await target, options);\n\n    await _implicitHover(element, options);\n    await _click(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs two click sequences on the given {@link AsyncTarget}.\n *\n * @see {@link click}\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  dblclick(\"button\"); // Double-clicks on the first <button> element\n */\nexport async function dblclick(target, options) {\n    const finalizeEvents = setupEvents(\"dblclick\");\n    const element = queryFirst(await target, options);\n\n    options = { ...options, button: btn.LEFT };\n    await _implicitHover(element, options);\n    await _click(element, options);\n    await _click(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Creates a new DOM {@link Event} of the given type and dispatches it on the given\n * {@link Target}.\n *\n * Note that this function is free of side-effects and does not trigger any other\n * event or special action. It also only supports standard DOM events, and will\n * crash when trying to dispatch a non-standard or deprecated event.\n *\n * @template {EventType} T\n * @param {EventTarget} target\n * @param {T} type\n * @param {EventInit} [eventInit]\n * @example\n * await dispatch(document.querySelector(\"input\"), \"paste\"); // Dispatches a \"paste\" event on the given <input>\n */\nexport async function dispatch(target, type, eventInit) {\n    if (type in DEPRECATED_EVENTS) {\n        throw new HootDomError(\n            `cannot dispatch \"${type}\" event: this event type is deprecated, use \"${DEPRECATED_EVENTS[type]}\" instead`\n        );\n    }\n    if (type !== type.toLowerCase()) {\n        throw new HootDomError(\n            `cannot dispatch \"${type}\" event: this event type is either non-standard or deprecated`\n        );\n    }\n    if (eventInit && typeof eventInit === \"object\") {\n        for (const key in eventInit) {\n            if (key in DEPRECATED_EVENT_PROPERTIES) {\n                throw new HootDomError(\n                    `cannot dispatch \"${type}\" event: property \"${key}\" is deprecated, use \"${DEPRECATED_EVENT_PROPERTIES[key]}\" instead`\n                );\n            }\n        }\n    }\n\n    const [Constructor, processParams, flags] = getEventConstructor(type);\n    const params = processParams({\n        composed: true,\n        ...eventInit,\n        target,\n        type,\n    });\n    if (flags & BUBBLES) {\n        params.bubbles = true;\n    }\n    if (flags & CANCELABLE) {\n        params.cancelable = true;\n    }\n    const event = new Constructor(type, params);\n\n    await Promise.resolve(target.dispatchEvent(event));\n\n    getCurrentEvents().push(event);\n\n    if (afterNextDispatch) {\n        const callback = afterNextDispatch;\n        afterNextDispatch = null;\n        await callback();\n    }\n\n    return event;\n}\n\n/**\n * Starts a drag sequence on the given {@link AsyncTarget}.\n *\n * Returns a set of helper functions to direct the sequence:\n * - `moveTo`: moves the pointer to the given target;\n * - `drop`: drops the dragged element on the given target (if any);\n * - `cancel`: cancels the drag sequence.\n *\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<DragHelpers>}\n * @example\n *  drag(\".card:first\").drop(\".card:last\"); // Drags the first card onto the last one\n * @example\n *  drag(\".card:first\").moveTo(\".card:last\").drop(); // Same as above\n * @example\n *  const { cancel, moveTo } = await drag(\".card:first\"); // Starts the drag sequence\n *  moveTo(\".card:eq(3)\"); // Moves the dragged card to the 4th card\n *  cancel(); // Cancels the drag sequence\n */\nexport async function drag(target, options) {\n    /**\n     * @template T\n     * @param {T} fn\n     * @param {boolean} endDrag\n     * @returns {T}\n     */\n    const expectIsDragging = (fn, endDrag) => {\n        return {\n            async [fn.name](...args) {\n                if (dragEndReason) {\n                    throw new HootDomError(\n                        `cannot execute drag helper \\`${fn.name}\\`: drag sequence has been ended by \\`${dragEndReason}\\``\n                    );\n                }\n                const result = await fn(...args);\n                if (endDrag) {\n                    dragEndReason = fn.name;\n                }\n                return result;\n            },\n        }[fn.name];\n    };\n\n    const cancel = expectIsDragging(\n        /** @type {DragHelpers[\"cancel\"]} */\n        async function cancel(options) {\n            const finalizeEvents = setupEvents(\"drag & drop: cancel\");\n            const element = getDocument().body;\n\n            // Reset buttons\n            runTime.buttons = 0;\n\n            await _press(element, { key: \"Escape\" });\n\n            dragEvents.push(...(await finalizeEvents(options)));\n\n            return dragEvents;\n        },\n        true\n    );\n\n    const drop = expectIsDragging(\n        /** @type {DragHelpers[\"drop\"]} */\n        async function drop(to, options) {\n            if (to) {\n                await moveTo(to, options);\n            }\n\n            const finalizeEvents = setupEvents(\"drag & drop: drop\");\n\n            await _pointerUp(runTime.pointerTarget, options);\n\n            dragEvents.push(...(await finalizeEvents(options)));\n\n            return dragEvents;\n        },\n        true\n    );\n\n    const moveTo = expectIsDragging(\n        /** @type {DragHelpers[\"moveTo\"]} */\n        async function moveTo(to, options) {\n            const finalizeEvents = setupEvents(\"drag & drop: move\");\n\n            await _hover(queryFirst(await to), options);\n\n            dragEvents.push(...(await finalizeEvents(options)));\n\n            return dragHelpers;\n        },\n        false\n    );\n\n    const finalizeEvents = setupEvents(\"drag & drop: start\");\n    const dragHelpers = { cancel, drop, moveTo };\n    const element = queryFirst(await target);\n\n    let dragEndReason = null;\n\n    // Pointer down on main target\n    await _implicitHover(element, options);\n    await _pointerDown(element, options);\n\n    const dragEvents = await finalizeEvents(options);\n\n    return dragHelpers;\n}\n\n/**\n * Combination of {@link clear} and {@link fill}:\n * - first, clears the input value (if any)\n * - then fills the input with the given value\n *\n * @see {@link clear}\n * @see {@link fill}\n * @param {InputValue} value\n * @param {FillOptions} options\n * @returns {Promise<EventList>}\n * @example\n *  fill(\"foo\"); // Types \"foo\" in the active element\n *  edit(\"Hello World\"); // Replaces \"foo\" by \"Hello World\"\n */\nexport async function edit(value, options) {\n    const finalizeEvents = setupEvents(\"edit\");\n    const element = getActiveElement();\n    if (!isEditable(element)) {\n        throw new HootDomError(`cannot call \\`edit()\\`: target should be editable`);\n    }\n\n    if (getNodeValue(element)) {\n        await _clear(element);\n    }\n    await _fill(element, value, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * @param {boolean} toggle\n */\nexport function enableEventLogs(toggle) {\n    allowLogs = toggle ?? true;\n}\n\n/**\n * Fills the current **active element** with the given `value`. This helper is intended\n * for `<input>` and `<textarea>` elements, with the exception of `\"checkbox\"` and\n * `\"radio\"` types, which should be selected using the {@link check} helper.\n *\n * If the target is an editable input, its string `value` will be input one character\n * at a time, each generating its corresponding keyboard event sequence. This behavior\n * can be overriden by passing the `instantly` option, which will instead simulate\n * a `control` + `v` keyboard sequence, resulting in the whole text being pasted.\n *\n * Note that the given value is **appended** to the current value of the element.\n *\n * If the active element is a `<input type=\"file\"/>`, the `value` should be a\n * `File`/list of `File` object(s).\n *\n * @param {InputValue} value\n * @param {FillOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  fill(\"Hello World\"); // Types \"Hello World\" in the active element\n * @example\n *  fill(\"Hello World\", { instantly: true }); // Pastes \"Hello World\" in the active element\n * @example\n *  fill(new File([\"Hello World\"], \"hello.txt\")); // Uploads a file named \"hello.txt\" with \"Hello World\" as content\n */\nexport async function fill(value, options) {\n    const finalizeEvents = setupEvents(\"fill\");\n    const element = getActiveElement();\n\n    if (!isEditable(element)) {\n        throw new HootDomError(`cannot call \\`fill()\\`: target should be editable`);\n    }\n\n    await _fill(element, value, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a hover sequence on the given {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n *  - `pointerover`\n *  - [desktop] `mouseover`\n *  - `pointerenter`\n *  - [desktop] `mouseenter`\n *  - `pointermove`\n *  - [desktop] `mousemove`\n *  - [touch] `touchmove`\n *\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  hover(\"button\"); // Hovers the first <button> element\n */\nexport async function hover(target, options) {\n    const finalizeEvents = setupEvents(\"hover\");\n    const element = queryFirst(await target, options);\n\n    await _hover(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a key down sequence on the current **active element**.\n *\n * The event sequence is as follows:\n *  - `keydown`\n *\n * Additional actions will be performed depending on the key pressed:\n * - `Tab`: focus next (or previous with `shift`) focusable element;\n * - `c`: copy current selection to clipboard;\n * - `v`: paste current clipboard content to current element;\n * - `Enter`: submit the form if the target is a `<button type=\"button\">` or\n *  a `<form>` element, or trigger a `change` event on the target if it is\n *  an `<input>` element;\n * - `Space`: trigger a `click` event on the target if it is an `<input type=\"checkbox\">`\n *  element.\n *\n * @param {KeyStrokes} keyStrokes\n * @param {KeyboardOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  keyDown(\" \"); // Space key\n */\nexport async function keyDown(keyStrokes, options) {\n    const finalizeEvents = setupEvents(\"keyDown\");\n    const eventInits = parseKeyStrokes(keyStrokes, options);\n    for (const eventInit of eventInits) {\n        await _keyDown(getActiveElement(), eventInit);\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a key up sequence on the current **active element**.\n *\n * The event sequence is as follows:\n *  - `keyup`\n *\n * @param {KeyStrokes} keyStrokes\n * @param {KeyboardOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  keyUp(\"Enter\");\n */\nexport async function keyUp(keyStrokes, options) {\n    const finalizeEvents = setupEvents(\"keyUp\");\n    const eventInits = parseKeyStrokes(keyStrokes, options);\n    for (const eventInit of eventInits) {\n        await _keyUp(getActiveElement(), eventInit);\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a leave sequence on the current **window**.\n *\n * The event sequence is as follows:\n *  - `pointermove`\n *  - [desktop] `mousemove`\n *  - [touch] `touchmove`\n *  - `pointerout`\n *  - [desktop] `mouseout`\n *  - `pointerleave`\n *  - [desktop] `mouseleave`\n *\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  leave(\"button\"); // Moves out of <button>\n */\nexport async function leave(options) {\n    const finalizeEvents = setupEvents(\"leave\");\n\n    await _hover(null, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a middle-click sequence on the given {@link AsyncTarget}.\n *\n * @see {@link click}\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  middleClick(\"button\"); // Middle-clicks on the first <button> element\n */\nexport async function middleClick(target, options) {\n    const finalizeEvents = setupEvents(\"middleClick\");\n    const element = queryFirst(await target, options);\n\n    options = { ...options, button: btn.MIDDLE };\n    await _implicitHover(element, options);\n    await _click(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Shorthand helper to attach an event listener to the given {@link Target}, and\n * returning a function to remove the listener.\n *\n * @template {EventType} T\n * @param {Target<EventTarget>} target\n * @param {T} type\n * @param {(event: GlobalEventHandlersEventMap[T]) => any} listener\n * @param {boolean | AddEventListenerOptions} [options]\n * @returns {() => void}\n * @example\n *  const off = on(\"button\", \"click\", onClick);\n *  after(off);\n */\nexport function on(target, type, listener, options) {\n    const targets = isEventTarget(target) ? [target] : queryAll(target);\n    if (!targets.length) {\n        throw new HootDomError(`expected at least 1 event target, got none`);\n    }\n    for (const eventTarget of targets) {\n        eventTarget.addEventListener(type, listener, options);\n    }\n\n    return function off() {\n        for (const eventTarget of targets) {\n            eventTarget.removeEventListener(type, listener, options);\n        }\n    };\n}\n\n/**\n * Performs a pointer down on the given {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n *  - `pointerdown`\n *  - [desktop] `mousedown`\n *  - [touch] `touchstart`\n *  - [target is not active element] `blur`\n *  - [target is focusable] `focus`\n *\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  pointerDown(\"button\"); // Focuses to the first <button> element\n */\nexport async function pointerDown(target, options) {\n    const finalizeEvents = setupEvents(\"pointerDown\");\n    const element = queryFirst(await target, options);\n\n    await _implicitHover(element, options);\n    await _pointerDown(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a pointer up on the given {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n * - `pointerup`\n * - [desktop] `mouseup`\n * - [touch] `touchend`\n *\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  pointerUp(\"body\"); // Triggers a pointer up on the <body> element\n */\nexport async function pointerUp(target, options) {\n    const finalizeEvents = setupEvents(\"pointerUp\");\n    const element = queryFirst(await target, options);\n\n    await _implicitHover(element, options);\n    await _pointerUp(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a keyboard event sequence on the current **active element**.\n *\n * The event sequence is as follows:\n *  - `keydown`\n *  - `keyup`\n *\n * @param {KeyStrokes} keyStrokes\n * @param {KeyboardOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  pointerDown(\"button[type=submit]\"); // Moves focus to <button>\n *  keyDown(\"Enter\"); // Submits the form\n * @example\n *  keyDown(\"Shift+Tab\"); // Focuses previous focusable element\n * @example\n *  keyDown([\"ctrl\", \"v\"]); // Pastes current clipboard content\n */\nexport async function press(keyStrokes, options) {\n    const finalizeEvents = setupEvents(\"press\");\n    const eventInits = parseKeyStrokes(keyStrokes, options);\n    const activeElement = getActiveElement();\n\n    for (const eventInit of eventInits) {\n        await _keyDown(activeElement, eventInit);\n    }\n    for (const eventInit of eventInits.reverse()) {\n        await _keyUp(activeElement, eventInit);\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a resize event sequence on the current **window**.\n *\n * The event sequence is as follows:\n *  - `resize`\n *\n * The target will be resized to the given dimensions, enforced by `!important` style\n * attributes.\n *\n * @param {Dimensions} dimensions\n * @param {EventOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  resize(\"body\", { width: 1000, height: 500 }); // Resizes <body> to 1000x500\n */\nexport async function resize(dimensions, options) {\n    const finalizeEvents = setupEvents(\"resize\");\n    const [width, height] = parseDimensions(dimensions);\n\n    setDimensions(width, height);\n\n    await dispatch(getWindow(), \"resize\");\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a right-click sequence on the given {@link AsyncTarget}.\n *\n * @see {@link click}\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  rightClick(\"button\"); // Middle-clicks on the first <button> element\n */\nexport async function rightClick(target, options) {\n    const finalizeEvents = setupEvents(\"rightClick\");\n    const element = queryFirst(await target, options);\n\n    options = { ...options, button: btn.RIGHT };\n    await _implicitHover(element, options);\n    await _click(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a scroll event sequence on the given {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n *  - [desktop] `wheel`\n *  - `scroll`\n *\n * @param {AsyncTarget} target\n * @param {Position} position\n * @param {EventOptions & QueryOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  scroll(\"body\", { y: 0 }); // Scrolls to the top of <body>\n */\nexport async function scroll(target, position, options) {\n    const finalizeEvents = setupEvents(\"scroll\");\n\n    /** @type {ScrollToOptions} */\n    const scrollOptions = {};\n    const [x, y] = parsePosition(position);\n    if (!$isNaN(x)) {\n        scrollOptions.left = x;\n    }\n    if (!$isNaN(y)) {\n        scrollOptions.top = y;\n    }\n    const element = queryFirst(await target, { ...options, scrollable: true });\n    if (!hasTouch()) {\n        await dispatch(element, \"wheel\");\n    }\n    // This will trigger a trusted \"scroll\" event\n    catchNextEvent(element, \"scroll\");\n    await Promise.resolve(element.scrollTo(scrollOptions));\n\n    return finalizeEvents(options);\n}\n\n/**\n * Performs a selection event sequence current **active element**. This helper is\n * intended for `<select>` elements only.\n *\n * The event sequence is as follows:\n *  - `change`\n *\n * @param {string | number | (string | number)[]} value\n * @param {SelectOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  click(\"select[name=country]\"); // Focuses <select> element\n *  select(\"belgium\"); // Selects the <option value=\"belgium\"> element\n */\nexport async function select(value, options) {\n    const finalizeEvents = setupEvents(\"select\");\n    const element = options?.target ? queryFirst(await options.target) : getActiveElement();\n\n    if (!hasTagName(element, \"select\")) {\n        throw new HootDomError(`cannot call \\`select()\\`: target should be a <select> element`);\n    }\n\n    if (options?.target) {\n        await _implicitHover(element);\n        await _pointerDown(element);\n    }\n    await _select(element, value);\n    if (options?.target) {\n        await _pointerUp(element);\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Gives the given {@link File} list to the current file input. This helper only\n * works if a file input has been previously interacted with (by clicking on it).\n *\n * @param {MaybeIterable<File>} files\n * @param {EventOptions} [options]\n * @returns {Promise<EventList>}\n */\nexport async function setInputFiles(files, options) {\n    if (!runTime.fileInput) {\n        throw new HootDomError(\n            `cannot call \\`setInputFiles()\\`: no file input has been interacted with`\n        );\n    }\n\n    const finalizeEvents = setupEvents(\"setInputFiles\");\n\n    await _fill(runTime.fileInput, files);\n\n    runTime.fileInput = null;\n\n    return finalizeEvents(options);\n}\n\n/**\n * Sets the given value to the given \"input[type=range]\" {@link AsyncTarget}.\n *\n * The event sequence is as follows:\n *  - `pointerdown`\n *  - `input`\n *  - `change`\n *  - `pointerup`\n *\n * @param {AsyncTarget} target\n * @param {number} value\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n */\nexport async function setInputRange(target, value, options) {\n    const finalizeEvents = setupEvents(\"setInputRange\");\n    const element = queryFirst(await target, options);\n\n    await _implicitHover(element, options);\n    await _pointerDown(element, options);\n    await _fill(element, value);\n    await _pointerUp(element, options);\n\n    return finalizeEvents(options);\n}\n\n/**\n * @param {HTMLElement} fixture\n */\nexport function setupEventActions(fixture) {\n    if (runTime.pointerDownTimeout) {\n        globalThis.clearTimeout(runTime.pointerDownTimeout);\n    }\n\n    removeChangeTargetListeners();\n\n    fixture.addEventListener(\"click\", registerFileInput, { capture: true });\n    fixture.addEventListener(\"focus\", registerFileInput, { capture: true });\n    fixture.addEventListener(\"submit\", redirectSubmit);\n\n    // Runtime global variables\n    $assign(runTime, getDefaultRunTimeValue());\n}\n\n/**\n * Ensures that the given {@link AsyncTarget} is unchecked.\n *\n * If it is checked, a click is triggered on the input.\n * If the input is still checked after the click, an error is thrown.\n *\n * @see {@link click}\n * @param {AsyncTarget} target\n * @param {PointerOptions} [options]\n * @returns {Promise<EventList>}\n * @example\n *  uncheck(\"input[type=checkbox]\"); // Unchecks the first <input> checkbox element\n */\nexport async function uncheck(target, options) {\n    const finalizeEvents = setupEvents(\"uncheck\");\n    const element = queryFirst(await target, options);\n    if (!isCheckable(element)) {\n        throw new HootDomError(\n            `cannot call \\`uncheck()\\`: target should be a checkbox or radio input`\n        );\n    }\n\n    const checkTarget = getTag(element) === \"label\" ? element.control : element;\n    if (checkTarget.checked) {\n        await _implicitHover(element, options);\n        await _click(element, options);\n\n        if (checkTarget.checked) {\n            throw new HootDomError(\n                `error when calling \\`uncheck()\\`: target is still checked after interaction`\n            );\n        }\n    }\n\n    return finalizeEvents(options);\n}\n\n/**\n * Triggers a \"beforeunload\" event the current **window**.\n *\n * @param {EventOptions} [options]\n * @returns {Promise<EventList>}\n */\nexport async function unload(options) {\n    const finalizeEvents = setupEvents(\"unload\");\n\n    await dispatch(getWindow(), \"beforeunload\");\n\n    return finalizeEvents(options);\n}\n\n/** @extends {Array<Event>} */\nexport class EventList extends Array {\n    constructor(...args) {\n        super(...args.flat());\n    }\n\n    /**\n     * @param {EventListPredicate} predicate\n     */\n    get(predicate) {\n        return this.getAll(predicate)[0] || null;\n    }\n\n    /**\n     * @param {EventListPredicate} predicate\n     */\n    getAll(predicate) {\n        if (typeof predicate !== \"function\") {\n            const type = predicate;\n            predicate = (ev) => ev.type === type;\n        }\n        return this.filter(predicate);\n    }\n}\n", "/** @odoo-module */\n\nimport { HootDomError } from \"../hoot_dom_utils\";\n\n/**\n * @typedef {{\n *  message?: string | () => string;\n *  timeout?: number;\n * }} WaitOptions\n */\n\n//-----------------------------------------------------------------------------\n// Global\n//-----------------------------------------------------------------------------\n\nconst {\n    cancelAnimationFrame,\n    clearInterval,\n    clearTimeout,\n    Error,\n    Math: { ceil: $ceil, floor: $floor, max: $max, min: $min },\n    performance,\n    Promise,\n    requestAnimationFrame,\n    setInterval,\n    setTimeout,\n} = globalThis;\n/** @type {Performance[\"now\"]} */\nconst $performanceNow = performance.now.bind(performance);\n\n//-----------------------------------------------------------------------------\n// Internal\n//-----------------------------------------------------------------------------\n\n/**\n * @param {number} id\n */\nconst animationToId = (id) => ID_PREFIX.animation + String(id);\n\nconst getNextTimerValues = () => {\n    /** @type {[number, () => any, string] | null} */\n    let timerValues = null;\n    for (const [internalId, [callback, init, delay]] of timers.entries()) {\n        const timeout = init + delay;\n        if (!timerValues || timeout < timerValues[0]) {\n            timerValues = [timeout, callback, internalId];\n        }\n    }\n    return timerValues;\n};\n\n/**\n * @param {string} id\n */\nconst idToAnimation = (id) => Number(id.slice(ID_PREFIX.animation.length));\n\n/**\n * @param {string} id\n */\nconst idToInterval = (id) => Number(id.slice(ID_PREFIX.interval.length));\n\n/**\n * @param {string} id\n */\nconst idToTimeout = (id) => Number(id.slice(ID_PREFIX.timeout.length));\n\n/**\n * @param {number} id\n */\nconst intervalToId = (id) => ID_PREFIX.interval + String(id);\n\nconst now = () => (freezed ? 0 : $performanceNow()) + timeOffset;\n\n/**\n * @param {number} id\n */\nconst timeoutToId = (id) => ID_PREFIX.timeout + String(id);\n\nconst ID_PREFIX = {\n    animation: \"a_\",\n    interval: \"i_\",\n    timeout: \"t_\",\n};\n\n/** @type {Map<string, [() => any, number, number]>} */\nconst timers = new Map();\n\nlet allowTimers = false;\nlet freezed = false;\nlet frameDelay = 1000 / 60;\nlet nextDummyId = 1;\nlet timeOffset = 0;\n\n//-----------------------------------------------------------------------------\n// Exports\n//-----------------------------------------------------------------------------\n\n/**\n * @param {number} [frameCount]\n */\nexport function advanceFrame(frameCount) {\n    return advanceTime(frameDelay * $max(1, frameCount));\n}\n\n/**\n * Advances the current time by the given amount of milliseconds. This will\n * affect all timeouts, intervals, animations and date objects.\n *\n * It returns a promise resolved after all related callbacks have been executed.\n *\n * @param {number} ms\n * @returns {Promise<number>} time consumed by timers (in ms).\n */\nexport function advanceTime(ms) {\n    const targetTime = now() + ms;\n    let remaining = ms;\n    /** @type {ReturnType<typeof getNextTimerValues>} */\n    let timerValues;\n    while ((timerValues = getNextTimerValues()) && timerValues[0] <= targetTime) {\n        const [timeout, handler, id] = timerValues;\n        const diff = timeout - now();\n        if (diff > 0) {\n            timeOffset += $min(remaining, diff);\n            remaining = $max(remaining - diff, 0);\n        }\n        if (timers.has(id)) {\n            handler(timeout);\n        }\n    }\n\n    if (remaining > 0) {\n        timeOffset += remaining;\n    }\n\n    // Waits for callbacks to execute\n    return animationFrame().then(() => ms);\n}\n\n/**\n * Returns a promise resolved after the next animation frame, typically allowing\n * Owl components to render.\n *\n * @returns {Promise<void>}\n */\nexport function animationFrame() {\n    return new Promise((resolve) => requestAnimationFrame(() => delay().then(resolve)));\n}\n\n/**\n * Cancels all current timeouts, intervals and animations.\n */\nexport function cancelAllTimers() {\n    for (const id of timers.keys()) {\n        if (id.startsWith(ID_PREFIX.animation)) {\n            globalThis.cancelAnimationFrame(idToAnimation(id));\n        } else if (id.startsWith(ID_PREFIX.interval)) {\n            globalThis.clearInterval(idToInterval(id));\n        } else if (id.startsWith(ID_PREFIX.timeout)) {\n            globalThis.clearTimeout(idToTimeout(id));\n        }\n    }\n}\n\nexport async function cleanupTime() {\n    allowTimers = false;\n    freezed = false;\n\n    cancelAllTimers();\n\n    // Wait for remaining async code to run\n    await delay();\n}\n\n/**\n * Returns a promise resolved after a given amount of milliseconds (default to 0).\n *\n * @param {number} [duration]\n * @returns {Promise<void>}\n * @example\n *  await delay(1000); // waits for 1 second\n */\nexport function delay(duration) {\n    return new Promise((resolve) => setTimeout(resolve, duration));\n}\n\n/**\n * @param {boolean} setFreeze\n */\nexport function freezeTime(setFreeze) {\n    freezed = setFreeze ?? !freezed;\n}\n\nexport function getTimeOffset() {\n    return timeOffset;\n}\n\nexport function isTimeFreezed() {\n    return freezed;\n}\n\n/**\n * Returns a promise resolved after the next microtask tick.\n *\n * @returns {Promise<void>}\n */\nexport function microTick() {\n    return new Promise(queueMicrotask);\n}\n\n/** @type {typeof cancelAnimationFrame} */\nexport function mockedCancelAnimationFrame(handle) {\n    if (!freezed) {\n        cancelAnimationFrame(handle);\n    }\n    timers.delete(animationToId(handle));\n}\n\n/** @type {typeof clearInterval} */\nexport function mockedClearInterval(intervalId) {\n    if (!freezed) {\n        clearInterval(intervalId);\n    }\n    timers.delete(intervalToId(intervalId));\n}\n\n/** @type {typeof clearTimeout} */\nexport function mockedClearTimeout(timeoutId) {\n    if (!freezed) {\n        clearTimeout(timeoutId);\n    }\n    timers.delete(timeoutToId(timeoutId));\n}\n\n/** @type {typeof requestAnimationFrame} */\nexport function mockedRequestAnimationFrame(callback) {\n    if (!allowTimers) {\n        return 0;\n    }\n\n    const handler = () => {\n        mockedCancelAnimationFrame(handle);\n        return callback(now());\n    };\n\n    const animationValues = [handler, now(), frameDelay];\n    const handle = freezed ? nextDummyId++ : requestAnimationFrame(handler);\n    const internalId = animationToId(handle);\n    timers.set(internalId, animationValues);\n\n    return handle;\n}\n\n/** @type {typeof setInterval} */\nexport function mockedSetInterval(callback, ms, ...args) {\n    if (!allowTimers) {\n        return 0;\n    }\n\n    if (isNaN(ms) || !ms || ms < 0) {\n        ms = 0;\n    }\n\n    const handler = () => {\n        if (allowTimers) {\n            intervalValues[1] = Math.max(now(), intervalValues[1] + ms);\n        } else {\n            mockedClearInterval(intervalId);\n        }\n        return callback(...args);\n    };\n\n    const intervalValues = [handler, now(), ms];\n    const intervalId = freezed ? nextDummyId++ : setInterval(handler, ms);\n    const internalId = intervalToId(intervalId);\n    timers.set(internalId, intervalValues);\n\n    return intervalId;\n}\n\n/** @type {typeof setTimeout} */\nexport function mockedSetTimeout(callback, ms, ...args) {\n    if (!allowTimers) {\n        return 0;\n    }\n\n    if (isNaN(ms) || !ms || ms < 0) {\n        ms = 0;\n    }\n\n    const handler = () => {\n        mockedClearTimeout(timeoutId);\n        return callback(...args);\n    };\n\n    const timeoutValues = [handler, now(), ms];\n    const timeoutId = freezed ? nextDummyId++ : setTimeout(handler, ms);\n    const internalId = timeoutToId(timeoutId);\n    timers.set(internalId, timeoutValues);\n\n    return timeoutId;\n}\n\nexport function resetTimeOffset() {\n    timeOffset = 0;\n}\n\n/**\n * Calculates the amount of time needed to run all current timeouts, intervals and\n * animations, and then advances the current time by that amount.\n *\n * @see {@link advanceTime}\n * @returns {Promise<number>} time consumed by timers (in ms).\n */\nexport async function runAllTimers() {\n    if (!timers.size) {\n        return 0;\n    }\n\n    const endts = $max(...[...timers.values()].map(([, init, delay]) => init + delay));\n    const ms = await advanceTime($ceil(endts - now()));\n\n    return ms;\n}\n\n/**\n * Sets the current frame rate (in fps) used by animation frames (default to 60fps).\n *\n * @param {number} frameRate\n */\nexport function setFrameRate(frameRate) {\n    if (!Number.isInteger(frameRate) || frameRate <= 0 || frameRate > 1000) {\n        throw new Error(\"frame rate must be an number between 1 and 1000\");\n    }\n    frameDelay = 1000 / frameRate;\n}\n\nexport function setupTime() {\n    allowTimers = true;\n}\n\n/**\n * Returns a promise resolved after the next task tick.\n *\n * @returns {Promise<void>}\n */\nexport function tick() {\n    return delay();\n}\n\n/**\n * Returns a promise fulfilled when the given `predicate` returns a truthy value,\n * with the value of the promise being the return value of the `predicate`.\n *\n * The `predicate` is run once initially, and then on each animation frame until\n * it succeeds or fail.\n *\n * The promise automatically rejects after a given `timeout` (defaults to 5 seconds).\n *\n * @template T\n * @param {() => T} predicate\n * @param {WaitOptions} [options]\n * @returns {Promise<T>}\n * @example\n *  await waitUntil(() => []); // -> []\n * @example\n *  const button = await waitUntil(() => queryOne(\"button:visible\"));\n *  button.click();\n */\nexport function waitUntil(predicate, options) {\n    // Early check before running the loop\n    const result = predicate();\n    if (result) {\n        return Promise.resolve().then(() => result);\n    }\n\n    const timeout = $floor(options?.timeout ?? 200);\n    let handle;\n    let timeoutId;\n    let running = true;\n\n    return new Promise((resolve, reject) => {\n        const runCheck = () => {\n            const result = predicate();\n            if (result) {\n                resolve(result);\n            } else if (running) {\n                handle = requestAnimationFrame(runCheck);\n            } else {\n                let message =\n                    options?.message || `'waitUntil' timed out after %timeout% milliseconds`;\n                if (typeof message === \"function\") {\n                    message = message();\n                }\n                reject(new HootDomError(message.replace(\"%timeout%\", String(timeout))));\n            }\n        };\n\n        handle = requestAnimationFrame(runCheck);\n        timeoutId = setTimeout(() => (running = false), timeout);\n    }).finally(() => {\n        cancelAnimationFrame(handle);\n        clearTimeout(timeoutId);\n    });\n}\n\n/**\n * Manually resolvable and rejectable promise. It introduces 2 new methods:\n *  - {@link reject} rejects the deferred with the given reason;\n *  - {@link resolve} resolves the deferred with the given value.\n *\n * @template [T=unknown]\n */\nexport class Deferred extends Promise {\n    /** @type {typeof Promise.resolve<T>} */\n    _resolve;\n    /** @type {typeof Promise.reject<T>} */\n    _reject;\n\n    /**\n     * @param {(resolve: (value?: T) => any, reject: (reason?: any) => any) => any} [executor]\n     */\n    constructor(executor) {\n        let _resolve, _reject;\n\n        super((resolve, reject) => {\n            _resolve = resolve;\n            _reject = reject;\n            executor?.(_resolve, _reject);\n        });\n\n        this._resolve = _resolve;\n        this._reject = _reject;\n    }\n\n    /**\n     * @param {any} [reason]\n     */\n    async reject(reason) {\n        return this._reject(reason);\n    }\n\n    /**\n     * @param {T} [value]\n     */\n    async resolve(value) {\n        return this._resolve(value);\n    }\n}\n", "/** @odoo-module alias=@odoo/hoot-dom default=false */\n\n/**\n * @typedef {import(\"./helpers/dom\").Dimensions} Dimensions\n * @typedef {import(\"./helpers/dom\").FormatXmlOptions} FormatXmlOptions\n * @typedef {import(\"./helpers/dom\").Position} Position\n * @typedef {import(\"./helpers/dom\").QueryOptions} QueryOptions\n * @typedef {import(\"./helpers/dom\").QueryRectOptions} QueryRectOptions\n * @typedef {import(\"./helpers/dom\").QueryTextOptions} QueryTextOptions\n * @typedef {import(\"./helpers/dom\").Target} Target\n *\n * @typedef {import(\"./helpers/events\").DragHelpers} DragHelpers\n * @typedef {import(\"./helpers/events\").EventType} EventType\n * @typedef {import(\"./helpers/events\").FillOptions} FillOptions\n * @typedef {import(\"./helpers/events\").InputValue} InputValue\n * @typedef {import(\"./helpers/events\").KeyStrokes} KeyStrokes\n * @typedef {import(\"./helpers/events\").PointerOptions} PointerOptions\n */\n\nexport {\n    formatXml,\n    getActiveElement,\n    getFocusableElements,\n    getNextFocusableElement,\n    getParentFrame,\n    getPreviousFocusableElement,\n    isDisplayed,\n    isEditable,\n    isFocusable,\n    isInDOM,\n    isInViewPort,\n    isScrollable,\n    isVisible,\n    matches,\n    observe,\n    queryAll,\n    queryAllAttributes,\n    queryAllProperties,\n    queryAllRects,\n    queryAllTexts,\n    queryAllValues,\n    queryAttribute,\n    queryFirst,\n    queryOne,\n    queryRect,\n    queryText,\n    queryValue,\n    waitFor,\n    waitForNone,\n} from \"./helpers/dom\";\nexport {\n    check,\n    clear,\n    click,\n    dblclick,\n    drag,\n    edit,\n    fill,\n    hover,\n    keyDown,\n    keyUp,\n    leave,\n    dispatch as manuallyDispatchProgrammaticEvent,\n    middleClick,\n    on,\n    pointerDown,\n    pointerUp,\n    press,\n    resize,\n    rightClick,\n    scroll,\n    select,\n    setInputFiles,\n    setInputRange,\n    uncheck,\n    unload,\n} from \"./helpers/events\";\nexport {\n    Deferred,\n    advanceFrame,\n    advanceTime,\n    animationFrame,\n    cancelAllTimers,\n    delay,\n    freezeTime,\n    microTick,\n    runAllTimers,\n    setFrameRate,\n    tick,\n    waitUntil,\n} from \"./helpers/time\";\n", "/** @odoo-module */\n\n/**\n * @typedef {ArgumentPrimitive | `${ArgumentPrimitive}[]` | null} ArgumentType\n *\n * @typedef {\"any\"\n *  | \"bigint\"\n *  | \"boolean\"\n *  | \"error\"\n *  | \"function\"\n *  | \"integer\"\n *  | \"node\"\n *  | \"number\"\n *  | \"object\"\n *  | \"regex\"\n *  | \"string\"\n *  | \"symbol\"\n *  | \"undefined\"} ArgumentPrimitive\n */\n\n/**\n * @template T\n * @typedef {T | Iterable<T>} MaybeIterable\n */\n\n/**\n * @template T\n * @typedef {T | PromiseLike<T>} MaybePromise\n */\n\n//-----------------------------------------------------------------------------\n// Global\n//-----------------------------------------------------------------------------\n\nconst {\n    Boolean,\n    navigator: { userAgent: $userAgent },\n    RegExp,\n    SyntaxError,\n} = globalThis;\n\n//-----------------------------------------------------------------------------\n// Internal\n//-----------------------------------------------------------------------------\n\nconst R_REGEX_PATTERN = /^\\/(.*)\\/([dgimsuvy]+)?$/;\n\n//-----------------------------------------------------------------------------\n// Exports\n//-----------------------------------------------------------------------------\n\n/**\n * @param {Node} node\n */\nexport function getTag(node) {\n    return node?.nodeName.toLowerCase() || \"\";\n}\n\n/**\n * @returns {boolean}\n */\nexport function isFirefox() {\n    return /firefox/i.test($userAgent);\n}\n\n/**\n * Returns whether the given object is iterable (*excluding strings*).\n *\n * @template T\n * @template {T | Iterable<T>} V\n * @param {V} object\n * @returns {V extends Iterable<T> ? true : false}\n */\nexport function isIterable(object) {\n    return Boolean(object && typeof object === \"object\" && object[Symbol.iterator]);\n}\n\n/**\n * @param {string} filter\n * @returns {boolean}\n */\nexport function isRegExpFilter(filter) {\n    return R_REGEX_PATTERN.test(filter);\n}\n\n/**\n * @param {string} value\n * @param {{ safe?: boolean }} [options]\n * @returns {string | RegExp}\n */\nexport function parseRegExp(value, options) {\n    const regexParams = value.match(R_REGEX_PATTERN);\n    if (regexParams) {\n        const unified = regexParams[1].replace(/\\s+/g, \"\\\\s+\");\n        const flag = regexParams[2] || \"i\";\n        try {\n            return new RegExp(unified, flag);\n        } catch (error) {\n            if (error instanceof SyntaxError && options?.safe) {\n                return value;\n            } else {\n                throw error;\n            }\n        }\n    }\n    return value;\n}\n\n/**\n * @param {Node} node\n * @param {{ raw?: boolean }} [options]\n */\nexport function toSelector(node, options) {\n    const tagName = getTag(node);\n    const id = node.id ? `#${node.id}` : \"\";\n    const classNames = node.classList\n        ? [...node.classList].map((className) => `.${className}`)\n        : [];\n    if (options?.raw) {\n        return { tagName, id, classNames };\n    } else {\n        return [tagName, id, ...classNames].join(\"\");\n    }\n}\n\nexport class HootDomError extends Error {\n    name = \"HootDomError\";\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { Attachment, FileSelector, IMAGE_MIMETYPES } from \"./file_selector\";\n\nexport class DocumentAttachment extends Attachment {\n    static template = \"html_editor.DocumentAttachment\";\n}\n\nexport class DocumentSelector extends FileSelector {\n    static mediaSpecificClasses = [\"o_image\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [];\n    static tagNames = [\"A\"];\n    static attachmentsListTemplate = \"html_editor.DocumentsListTemplate\";\n    static components = {\n        ...FileSelector.components,\n        DocumentAttachment,\n    };\n\n    setup() {\n        super.setup();\n\n        this.uploadText = _t(\"Upload a document\");\n        this.urlPlaceholder = \"https://www.odoo.com/mydocument\";\n        this.addText = _t(\"Add URL\");\n        this.searchPlaceholder = _t(\"Search a document\");\n        this.allLoadedText = _t(\"All documents have been loaded\");\n    }\n\n    get attachmentsDomain() {\n        const domain = super.attachmentsDomain;\n        domain.push([\"mimetype\", \"not in\", IMAGE_MIMETYPES]);\n        // The assets should not be part of the documents.\n        // All assets begin with '/web/assets/', see _get_asset_template_url().\n        domain.unshift(\"&\", \"|\", [\"url\", \"=\", null], \"!\", [\"url\", \"=like\", \"/web/assets/%\"]);\n        return domain;\n    }\n\n    async onClickDocument(document) {\n        this.selectAttachment(document);\n        await this.props.save();\n    }\n\n    async fetchAttachments(...args) {\n        const attachments = await super.fetchAttachments(...args);\n\n        if (this.selectInitialMedia()) {\n            for (const attachment of attachments) {\n                if (\n                    `/web/content/${attachment.id}` ===\n                    this.props.media.getAttribute(\"href\").replace(/[?].*/, \"\")\n                ) {\n                    this.selectAttachment(attachment);\n                }\n            }\n        }\n        return attachments;\n    }\n\n    /**\n     * Utility method used by the MediaDialog component.\n     */\n    static async createElements(selectedMedia, { orm }) {\n        return Promise.all(\n            selectedMedia.map(async (attachment) => {\n                const linkEl = document.createElement(\"a\");\n                let href = `/web/content/${encodeURIComponent(\n                    attachment.id\n                )}?unique=${encodeURIComponent(attachment.checksum)}&download=true`;\n                if (!attachment.public) {\n                    let accessToken = attachment.access_token;\n                    if (!accessToken) {\n                        [accessToken] = await orm.call(\"ir.attachment\", \"generate_access_token\", [\n                            attachment.id,\n                        ]);\n                    }\n                    href += `&access_token=${encodeURIComponent(accessToken)}`;\n                }\n                linkEl.href = href;\n                linkEl.title = attachment.name;\n                linkEl.dataset.mimetype = attachment.mimetype;\n                return linkEl;\n            })\n        );\n    }\n}\n", "import { DocumentSelector } from \"@html_editor/main/media/media_dialog/document_selector\";\n\n/**\n * Override the @see DocumentSelector to manage files in a @see MediaDialog used\n * by the /file command. The purpose of this override is to merge images in the\n * documents tab of the MediaDialog, since the /file block displays a default\n * mimetype for every files.\n */\nexport class FileDocumentsSelector extends DocumentSelector {\n    /**\n     * @override\n     * Filter files for the documents tab of the MediaDialog. Any file with a\n     * mimetype is valid. (images and documents are displayed together)\n     *\n     * As KnowledgeDocumentsSelector is an aggregate of multiple kinds of\n     * files, images included, the domain should be adjusted with the same\n     * constraints as @see image_selector.js\n     */\n    get attachmentsDomain() {\n        const domain = super.attachmentsDomain.map((d) => {\n            if (d[0] === \"mimetype\") {\n                return [\"mimetype\", \"!=\", false];\n            }\n            return d;\n        });\n        domain.unshift(\n            \"&\",\n            \"|\",\n            [\"url\", \"=\", null],\n            \"&\",\n            \"!\",\n            [\"url\", \"=like\", \"/%/static/%\"],\n            \"!\",\n            \"|\",\n            [\"url\", \"=ilike\", \"/html_editor/shape/%\"],\n            [\"url\", \"=ilike\", \"/web_editor/shape/%\"],\n        );\n        domain.push(\"!\", [\"name\", \"=like\", \"%.crop\"]);\n        return domain;\n    }\n}\n", "import { renderToElement } from \"@web/core/utils/render\";\nimport { MediaDialog, TABS } from \"@html_editor/main/media/media_dialog/media_dialog\";\nimport { FileDocumentsSelector } from \"./file_documents_selector\";\n\n/**\n * FileMediaDialog will allow to select documents and images altogether\n * for the /file command.\n */\nexport class FileMediaDialog extends MediaDialog {\n    /**\n     * @override\n     */\n    addTabs() {\n        super.addTabs(...arguments);\n        this.addTab({\n            ...TABS.DOCUMENTS,\n            id: \"MIXED_FILES\",\n            Component: FileDocumentsSelector,\n        });\n    }\n    /**\n     * @override\n     * Render the selected media. This needs a custom implementation because\n     * the media is rendered as a Behavior blueprint for Knowledge, hence\n     * no super call.\n     *\n     * @param {Object} selectedMedia First element of the selectedMediaArray,\n     *                 which has length = 1 in this case because this component\n     *                 is meant to be used with the prop `multiSelect = false`\n     * @returns {Array<HTMLElement>}\n     */\n    async renderMedia([selectedMedia]) {\n        let accessToken = selectedMedia.access_token;\n        if (!selectedMedia.public || !accessToken) {\n            // Generate an access token so that anyone with read access to the\n            // article can view its files.\n            [accessToken] = await this.orm.call(\"ir.attachment\", \"generate_access_token\", [\n                selectedMedia.id,\n            ]);\n        }\n        const dotSplit = selectedMedia.name.split(\".\");\n        const extension = dotSplit.length > 1 ? dotSplit.pop() : undefined;\n        const fileData = {\n            access_token: accessToken,\n            checksum: selectedMedia.checksum,\n            extension,\n            filename: selectedMedia.name,\n            id: selectedMedia.id,\n            mimetype: selectedMedia.mimetype,\n            name: selectedMedia.name,\n            type: selectedMedia.type,\n            url: selectedMedia.url || \"\",\n        };\n        const fileBlock = renderToElement(\"html_editor.EmbeddedFileBlueprint\", {\n            embeddedProps: JSON.stringify({\n                fileData,\n            }),\n        });\n        return [fileBlock];\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { ConfirmationDialog } from \"@web/core/confirmation_dialog/confirmation_dialog\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { useDebounced } from \"@web/core/utils/timing\";\nimport { SearchMedia } from \"./search_media\";\n\nimport { Component, xml, useState, useRef, onWillStart, useEffect } from \"@odoo/owl\";\n\nexport const IMAGE_MIMETYPES = [\n    \"image/jpg\",\n    \"image/jpeg\",\n    \"image/jpe\",\n    \"image/png\",\n    \"image/svg+xml\",\n    \"image/gif\",\n    \"image/webp\",\n];\nexport const IMAGE_EXTENSIONS = [\".jpg\", \".jpeg\", \".jpe\", \".png\", \".svg\", \".gif\", \".webp\"];\n\nclass RemoveButton extends Component {\n    static template = xml`<i class=\"fa fa-trash o_existing_attachment_remove position-absolute top-0 end-0 p-2 bg-white-25 cursor-pointer opacity-0 opacity-100-hover z-index-1 transition-base\" t-att-title=\"removeTitle\" role=\"img\" t-att-aria-label=\"removeTitle\" t-on-click=\"this.remove\"/>`;\n    static props = [\"model?\", \"remove\"];\n    setup() {\n        this.removeTitle = _t(\"This file is attached to the current record.\");\n        if (this.props.model === \"ir.ui.view\") {\n            this.removeTitle = _t(\"This file is a public view attachment.\");\n        }\n    }\n\n    remove(ev) {\n        ev.stopPropagation();\n        this.props.remove();\n    }\n}\n\nexport class AttachmentError extends Component {\n    static components = { Dialog };\n    static template = xml`\n        <Dialog title=\"title\">\n            <div class=\"form-text\">\n                <p>The image could not be deleted because it is used in the\n                    following pages or views:</p>\n                <ul t-foreach=\"props.views\"  t-as=\"view\" t-key=\"view.id\">\n                    <li>\n                        <a t-att-href=\"'/odoo/ir.ui.view/' + window.encodeURIComponent(view.id)\">\n                            <t t-esc=\"view.name\"/>\n                        </a>\n                    </li>\n                </ul>\n            </div>\n            <t t-set-slot=\"footer\">\n                <button class=\"btn btn-primary\" t-on-click=\"() => this.props.close()\">\n                    Ok\n                </button>\n            </t>\n        </Dialog>`;\n    static props = [\"views\", \"close\"];\n    setup() {\n        this.title = _t(\"Alert\");\n    }\n}\n\nexport class Attachment extends Component {\n    static template = \"\";\n    static components = {\n        RemoveButton,\n    };\n    static props = [\"*\"];\n    setup() {\n        this.dialogs = useService(\"dialog\");\n    }\n\n    remove() {\n        this.dialogs.add(ConfirmationDialog, {\n            body: _t(\"Are you sure you want to delete this file?\"),\n            confirm: async () => {\n                const prevented = await rpc(\"/web_editor/attachment/remove\", {\n                    ids: [this.props.id],\n                });\n                if (!Object.keys(prevented).length) {\n                    this.props.onRemoved(this.props.id);\n                } else {\n                    this.dialogs.add(AttachmentError, {\n                        views: prevented[this.props.id],\n                    });\n                }\n            },\n        });\n    }\n}\n\nexport class FileSelectorControlPanel extends Component {\n    static template = \"html_editor.FileSelectorControlPanel\";\n    static components = {\n        SearchMedia,\n    };\n    static props = {\n        uploadUrl: Function,\n        validateUrl: Function,\n        uploadFiles: Function,\n        changeSearchService: Function,\n        changeShowOptimized: Function,\n        search: Function,\n        accept: { type: String, optional: true },\n        addText: { type: String, optional: true },\n        multiSelect: { type: true, optional: true },\n        needle: { type: String, optional: true },\n        searchPlaceholder: { type: String, optional: true },\n        searchService: { type: String, optional: true },\n        showOptimized: { type: Boolean, optional: true },\n        showOptimizedOption: { type: String, optional: true },\n        uploadText: { type: String, optional: true },\n        urlPlaceholder: { type: String, optional: true },\n        urlWarningTitle: { type: String, optional: true },\n        useMediaLibrary: { type: Boolean, optional: true },\n        useUnsplash: { type: Boolean, optional: true },\n    };\n    setup() {\n        this.state = useState({\n            showUrlInput: false,\n            urlInput: \"\",\n            isValidUrl: false,\n            isValidFileFormat: false,\n            isValidatingUrl: false,\n        });\n        this.debouncedValidateUrl = useDebounced(this.props.validateUrl, 500);\n\n        this.fileInput = useRef(\"file-input\");\n    }\n\n    get showSearchServiceSelect() {\n        return this.props.searchService && this.props.needle;\n    }\n\n    get enableUrlUploadClick() {\n        return (\n            !this.state.showUrlInput ||\n            (this.state.urlInput && this.state.isValidUrl && this.state.isValidFileFormat)\n        );\n    }\n\n    async onUrlUploadClick() {\n        if (!this.state.showUrlInput) {\n            this.state.showUrlInput = true;\n        } else {\n            await this.props.uploadUrl(this.state.urlInput);\n            this.state.urlInput = \"\";\n        }\n    }\n\n    async onUrlInput(ev) {\n        this.state.isValidatingUrl = true;\n        const { isValidUrl, isValidFileFormat } = await this.debouncedValidateUrl(ev.target.value);\n        this.state.isValidFileFormat = isValidFileFormat;\n        this.state.isValidUrl = isValidUrl;\n        this.state.isValidatingUrl = false;\n    }\n\n    onClickUpload() {\n        this.fileInput.el.click();\n    }\n\n    async onChangeFileInput() {\n        const inputFiles = this.fileInput.el.files;\n        if (!inputFiles.length) {\n            return;\n        }\n        await this.props.uploadFiles(inputFiles);\n        this.fileInput.el.value = \"\";\n    }\n}\n\nexport class FileSelector extends Component {\n    static template = \"html_editor.FileSelector\";\n    static components = {\n        FileSelectorControlPanel,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.notificationService = useService(\"notification\");\n        this.orm = useService(\"orm\");\n        this.uploadService = useService(\"upload\");\n        this.keepLast = new KeepLast();\n\n        this.loadMoreButtonRef = useRef(\"load-more-button\");\n        this.existingAttachmentsRef = useRef(\"existing-attachments\");\n\n        this.state = useState({\n            attachments: [],\n            canScrollAttachments: false,\n            canLoadMoreAttachments: false,\n            isFetchingAttachments: false,\n            needle: \"\",\n        });\n\n        this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY = 30;\n\n        onWillStart(async () => {\n            this.state.attachments = await this.fetchAttachments(\n                this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY,\n                0\n            );\n        });\n\n        this.debouncedOnScroll = useDebounced(this.updateScroll, 15);\n        this.debouncedScrollUpdate = useDebounced(this.updateScroll, 500);\n\n        useEffect(\n            (modalEl) => {\n                if (modalEl) {\n                    modalEl.addEventListener(\"scroll\", this.debouncedOnScroll);\n                    return () => {\n                        modalEl.removeEventListener(\"scroll\", this.debouncedOnScroll);\n                    };\n                }\n            },\n            () => [this.props.modalRef.el?.querySelector(\"main.modal-body\")]\n        );\n\n        useEffect(\n            () => {\n                // Updating the scroll button each time the attachments change.\n                // Hiding the \"Load more\" button to prevent it from flickering.\n                this.loadMoreButtonRef.el.classList.add(\"o_hide_loading\");\n                this.state.canScrollAttachments = false;\n                this.debouncedScrollUpdate();\n            },\n            () => [this.allAttachments.length]\n        );\n    }\n\n    get canLoadMore() {\n        return this.state.canLoadMoreAttachments;\n    }\n\n    get hasContent() {\n        return this.state.attachments.length;\n    }\n\n    get isFetching() {\n        return this.state.isFetchingAttachments;\n    }\n\n    get selectedAttachmentIds() {\n        return this.props.selectedMedia[this.props.id]\n            .filter((media) => media.mediaType === \"attachment\")\n            .map(({ id }) => id);\n    }\n\n    get attachmentsDomain() {\n        const domain = [\n            \"&\",\n            [\"res_model\", \"=\", this.props.resModel],\n            [\"res_id\", \"=\", this.props.resId || 0],\n        ];\n        domain.unshift(\"|\", [\"public\", \"=\", true]);\n        domain.push([\"name\", \"ilike\", this.state.needle]);\n        return domain;\n    }\n\n    get allAttachments() {\n        return this.state.attachments;\n    }\n\n    validateUrl(url) {\n        const path = url.split(\"?\")[0];\n        const isValidUrl = /^.+\\..+$/.test(path); // TODO improve\n        const isValidFileFormat = true;\n        return { isValidUrl, isValidFileFormat, path };\n    }\n\n    async fetchAttachments(limit, offset) {\n        this.state.isFetchingAttachments = true;\n        let attachments = [];\n        try {\n            attachments = await this.orm.call(\"ir.attachment\", \"search_read\", [], {\n                domain: this.attachmentsDomain,\n                fields: [\n                    \"name\",\n                    \"mimetype\",\n                    \"description\",\n                    \"checksum\",\n                    \"url\",\n                    \"type\",\n                    \"res_id\",\n                    \"res_model\",\n                    \"public\",\n                    \"access_token\",\n                    \"image_src\",\n                    \"image_width\",\n                    \"image_height\",\n                    \"original_id\",\n                ],\n                order: \"id desc\",\n                // Try to fetch first record of next page just to know whether there is a next page.\n                limit,\n                offset,\n            });\n            attachments.forEach((attachment) => (attachment.mediaType = \"attachment\"));\n        } catch (e) {\n            // Reading attachments as a portal user is not permitted and will raise\n            // an access error so we catch the error silently and don't return any\n            // attachment so he can still use the wizard and upload an attachment\n            if (e.exceptionName !== \"odoo.exceptions.AccessError\") {\n                throw e;\n            }\n        }\n        this.state.canLoadMoreAttachments =\n            attachments.length >= this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY;\n        this.state.isFetchingAttachments = false;\n        return attachments;\n    }\n\n    async handleLoadMore() {\n        await this.loadMore();\n    }\n\n    async loadMore() {\n        return this.keepLast\n            .add(\n                this.fetchAttachments(\n                    this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY,\n                    this.state.attachments.length\n                )\n            )\n            .then((newAttachments) => {\n                // This is never reached if another search or loadMore occurred.\n                this.state.attachments.push(...newAttachments);\n            });\n    }\n\n    async handleSearch(needle) {\n        await this.search(needle);\n    }\n\n    async search(needle) {\n        // Prepare in case loadMore results are obtained instead.\n        this.state.attachments = [];\n        // Fetch attachments relies on the state's needle.\n        this.state.needle = needle;\n        return this.keepLast\n            .add(this.fetchAttachments(this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY, 0))\n            .then((attachments) => {\n                // This is never reached if a new search occurred.\n                this.state.attachments = attachments;\n            });\n    }\n\n    async uploadFiles(files) {\n        await this.uploadService.uploadFiles(\n            files,\n            { resModel: this.props.resModel, resId: this.props.resId },\n            (attachment) => this.onUploaded(attachment)\n        );\n    }\n\n    async uploadUrl(url) {\n        await fetch(url)\n            .then(async (result) => {\n                const blob = await result.blob();\n                blob.id = new Date().getTime();\n                blob.name = new URL(url).pathname.split(\"/\").findLast((s) => s);\n                await this.uploadFiles([blob]);\n            })\n            .catch(async () => {\n                await new Promise((resolve) => {\n                    // If it works from an image, use URL.\n                    const imageEl = document.createElement(\"img\");\n                    imageEl.onerror = () => {\n                        // This message is about the blob fetch failure.\n                        // It is only displayed if the fallback did not work.\n                        this.notificationService.add(\n                            _t(\"An error occurred while fetching the entered URL.\"),\n                            {\n                                title: _t(\"Error\"),\n                                sticky: true,\n                            }\n                        );\n                        resolve();\n                    };\n                    imageEl.onload = () => {\n                        this.uploadService\n                            .uploadUrl(\n                                url,\n                                {\n                                    resModel: this.props.resModel,\n                                    resId: this.props.resId,\n                                },\n                                (attachment) => this.onUploaded(attachment)\n                            )\n                            .then(resolve);\n                    };\n                    imageEl.src = url;\n                });\n            });\n    }\n\n    async onUploaded(attachment) {\n        this.state.attachments = [\n            attachment,\n            ...this.state.attachments.filter((attach) => attach.id !== attachment.id),\n        ];\n        this.selectAttachment(attachment);\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n        if (this.props.onAttachmentChange) {\n            this.props.onAttachmentChange(attachment);\n        }\n    }\n\n    onRemoved(attachmentId) {\n        this.state.attachments = this.state.attachments.filter(\n            (attachment) => attachment.id !== attachmentId\n        );\n    }\n\n    selectAttachment(attachment) {\n        this.props.selectMedia({ ...attachment, mediaType: \"attachment\" });\n    }\n\n    selectInitialMedia() {\n        return (\n            this.props.media &&\n            this.constructor.tagNames.includes(this.props.media.tagName) &&\n            !this.selectedAttachmentIds.length\n        );\n    }\n\n    /**\n     * Updates the scroll button, depending on whether the \"Load more\" button is\n     * fully visible or not.\n     */\n    updateScroll() {\n        const loadMoreTop = this.loadMoreButtonRef.el.getBoundingClientRect().top;\n        const modalEl = this.props.modalRef.el.querySelector(\"main.modal-body\");\n        const modalBottom = modalEl.getBoundingClientRect().bottom;\n        this.state.canScrollAttachments = loadMoreTop >= modalBottom;\n        this.loadMoreButtonRef.el.classList.remove(\"o_hide_loading\");\n    }\n\n    /**\n     * Checks if the attachment is (partially) hidden.\n     *\n     * @param {Element} attachmentEl the attachment \"container\"\n     * @returns {Boolean} true if the attachment is hidden, false otherwise.\n     */\n    isAttachmentHidden(attachmentEl) {\n        const attachmentBottom = Math.round(attachmentEl.getBoundingClientRect().bottom);\n        const modalEl = this.props.modalRef.el.querySelector(\"main.modal-body\");\n        const modalBottom = modalEl.getBoundingClientRect().bottom;\n        return attachmentBottom > modalBottom;\n    }\n\n    /**\n     * Scrolls two attachments rows at a time. If there are not enough rows,\n     * scrolls to the \"Load more\" button.\n     */\n    handleScrollAttachments() {\n        let scrollToEl = this.loadMoreButtonRef.el;\n        const attachmentEls = [\n            ...this.existingAttachmentsRef.el.querySelectorAll(\".o_existing_attachment_cell\"),\n        ];\n        const firstHiddenAttachmentEl = attachmentEls.find((el) => this.isAttachmentHidden(el));\n        if (firstHiddenAttachmentEl) {\n            const attachmentBottom = firstHiddenAttachmentEl.getBoundingClientRect().bottom;\n            const attachmentIndex = attachmentEls.indexOf(firstHiddenAttachmentEl);\n            const firstNextRowAttachmentEl = attachmentEls.slice(attachmentIndex).find((el) => {\n                return el.getBoundingClientRect().bottom > attachmentBottom;\n            });\n            scrollToEl = firstNextRowAttachmentEl || scrollToEl;\n        }\n        scrollToEl.scrollIntoView({ block: \"end\", inline: \"nearest\", behavior: \"smooth\" });\n    }\n}\n", "import { SearchMedia } from \"./search_media\";\nimport { fonts } from \"@html_editor/utils/fonts\";\n\nimport { Component, useState } from \"@odoo/owl\";\n\nexport class IconSelector extends Component {\n    static mediaSpecificClasses = [\"fa\"];\n    static mediaSpecificStyles = [\"color\", \"background-color\"];\n    static mediaExtraClasses = [\n        \"rounded-circle\",\n        \"rounded\",\n        \"img-thumbnail\",\n        \"shadow\",\n        /^text-\\S+$/,\n        /^bg-\\S+$/,\n        /^fa-\\S+$/,\n    ];\n    static tagNames = [\"SPAN\", \"I\"];\n    static template = \"html_editor.IconSelector\";\n    static components = {\n        SearchMedia,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.state = useState({\n            fonts: this.props.fonts,\n            needle: \"\",\n        });\n    }\n\n    get selectedMediaIds() {\n        return this.props.selectedMedia[this.props.id].map(({ id }) => id);\n    }\n\n    search(needle) {\n        this.state.needle = needle;\n        if (!this.state.needle) {\n            this.state.fonts = this.props.fonts;\n        } else {\n            this.state.fonts = this.props.fonts.map((font) => {\n                const icons = font.icons.filter(\n                    (icon) => icon.alias.indexOf(this.state.needle.toLowerCase()) >= 0\n                );\n                return { ...font, icons };\n            });\n        }\n    }\n\n    async onClickIcon(font, icon) {\n        this.props.selectMedia({\n            ...icon,\n            fontBase: font.base,\n            // To check if the icon has changed, we only need to compare\n            // an alias of the icon with the class from the old media (some\n            // icons can have multiple classes e.g. \"fa-gears\" ~ \"fa-cogs\")\n            initialIconChanged:\n                this.props.media &&\n                !icon.names.some((name) => this.props.media.classList.contains(name)),\n        });\n        await this.props.save();\n    }\n\n    /**\n     * Utility methods, used by the MediaDialog component.\n     */\n    static createElements(selectedMedia) {\n        return selectedMedia.map((icon) => {\n            const iconEl = document.createElement(\"span\");\n            iconEl.classList.add(icon.fontBase, icon.names[0]);\n            return iconEl;\n        });\n    }\n    static initFonts() {\n        fonts.computeFonts();\n        const allFonts = fonts.fontIcons.map(({ cssData, base }) => {\n            const uniqueIcons = Array.from(\n                new Map(\n                    cssData.map((icon) => {\n                        const alias = icon.names.join(\",\");\n                        const id = `${base}_${alias}`;\n                        return [id, { ...icon, alias, id }];\n                    })\n                ).values()\n            );\n            return { base, icons: uniqueIcons };\n        });\n        return allFonts;\n    }\n}\n", "import { useEffect, useRef, useState } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { DEFAULT_PALETTE } from \"@html_editor/utils/color\";\nimport { getCSSVariableValue, getHtmlStyle } from \"@html_editor/utils/formatting\";\nimport { Attachment, FileSelector, IMAGE_EXTENSIONS, IMAGE_MIMETYPES } from \"./file_selector\";\n\nexport class AutoResizeImage extends Attachment {\n    static template = \"html_editor.AutoResizeImage\";\n    setup() {\n        super.setup();\n\n        this.image = useRef(\"auto-resize-image\");\n        this.container = useRef(\"auto-resize-image-container\");\n\n        this.state = useState({\n            loaded: false,\n        });\n\n        useEffect(\n            () => {\n                this.image.el.addEventListener(\"load\", () => this.onImageLoaded());\n                return this.image.el.removeEventListener(\"load\", () => this.onImageLoaded());\n            },\n            () => []\n        );\n    }\n\n    async onImageLoaded() {\n        if (!this.image.el) {\n            // Do not fail if already removed.\n            return;\n        }\n        if (this.props.onLoaded) {\n            await this.props.onLoaded(this.image.el);\n            if (!this.image.el) {\n                // If replaced by colored version, aspect ratio will be\n                // computed on it instead.\n                return;\n            }\n        }\n        const aspectRatio = this.image.el.offsetWidth / this.image.el.offsetHeight;\n        const width = aspectRatio * this.props.minRowHeight;\n        this.container.el.style.flexGrow = width;\n        this.container.el.style.flexBasis = `${width}px`;\n        this.state.loaded = true;\n    }\n}\nconst newLocal = \"img-fluid\";\nexport class ImageSelector extends FileSelector {\n    static mediaSpecificClasses = [\"img\", newLocal, \"o_we_custom_image\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [\n        \"rounded-circle\",\n        \"rounded\",\n        \"img-thumbnail\",\n        \"shadow\",\n        \"w-25\",\n        \"w-50\",\n        \"w-75\",\n        \"w-100\",\n    ];\n    static tagNames = [\"IMG\"];\n    static attachmentsListTemplate = \"html_editor.ImagesListTemplate\";\n    static components = {\n        ...FileSelector.components,\n        AutoResizeImage,\n    };\n\n    setup() {\n        super.setup();\n\n        this.keepLastLibraryMedia = new KeepLast();\n\n        this.state.libraryMedia = [];\n        this.state.libraryResults = null;\n        this.state.isFetchingLibrary = false;\n        this.state.searchService = \"all\";\n        this.state.showOptimized = false;\n        this.NUMBER_OF_MEDIA_TO_DISPLAY = 10;\n\n        this.uploadText = _t(\"Upload an image\");\n        this.urlPlaceholder = \"https://www.odoo.com/logo.png\";\n        this.addText = _t(\"Add URL\");\n        this.searchPlaceholder = _t(\"Search an image\");\n        this.urlWarningTitle = _t(\n            \"Uploaded image's format is not supported. Try with: \" + IMAGE_EXTENSIONS.join(\", \")\n        );\n        this.allLoadedText = _t(\"All images have been loaded\");\n        this.showOptimizedOption = this.env.debug;\n        this.MIN_ROW_HEIGHT = 128;\n\n        this.fileMimetypes = IMAGE_MIMETYPES.join(\",\");\n    }\n\n    get canLoadMore() {\n        // The user can load more library media only when the filter is set.\n        if (this.state.searchService === \"media-library\") {\n            return (\n                this.state.libraryResults &&\n                this.state.libraryMedia.length < this.state.libraryResults\n            );\n        }\n        return super.canLoadMore;\n    }\n\n    get hasContent() {\n        if (this.state.searchService === \"all\") {\n            return super.hasContent || !!this.state.libraryMedia.length;\n        } else if (this.state.searchService === \"media-library\") {\n            return !!this.state.libraryMedia.length;\n        }\n        return super.hasContent;\n    }\n\n    get isFetching() {\n        return super.isFetching || this.state.isFetchingLibrary;\n    }\n\n    get selectedMediaIds() {\n        return this.props.selectedMedia[this.props.id]\n            .filter((media) => media.mediaType === \"libraryMedia\")\n            .map(({ id }) => id);\n    }\n\n    get allAttachments() {\n        return [...super.allAttachments, ...this.state.libraryMedia];\n    }\n\n    get attachmentsDomain() {\n        const domain = super.attachmentsDomain;\n        domain.push([\"mimetype\", \"in\", IMAGE_MIMETYPES]);\n        if (!this.props.useMediaLibrary) {\n            domain.push(\"|\", [\"url\", \"=\", false],\n                \"!\", \"|\", [\"url\", \"=ilike\", \"/html_editor/shape/%\"], [\"url\", \"=ilike\", \"/web_editor/shape/%\"],\n            );\n        }\n        domain.push(\"!\", [\"name\", \"=like\", \"%.crop\"]);\n        domain.push(\"|\", [\"type\", \"=\", \"binary\"], \"!\", [\"url\", \"=like\", \"/%/static/%\"]);\n\n        // Optimized images (meaning they are related to an `original_id`) can\n        // only be shown in debug mode as the toggler to make those images\n        // appear is hidden when not in debug mode.\n        // There is thus no point to fetch those optimized images outside debug\n        // mode. Worst, it leads to bugs: it might fetch only optimized images\n        // when clicking on \"load more\" which will look like it's bugged as no\n        // images will appear on screen (they all will be hidden).\n        if (!this.env.debug) {\n            const subDomain = [false];\n\n            // Particular exception: if the edited image is an optimized\n            // image, we need to fetch it too so it's displayed as the\n            // selected image when opening the media dialog.\n            // We might get a few more optimized image than necessary if the\n            // original image has multiple optimized images but it's not a\n            // big deal.\n            const originalId = this.props.media && this.props.media.dataset.originalId;\n            if (originalId) {\n                subDomain.push(originalId);\n            }\n\n            domain.push([\"original_id\", \"in\", subDomain]);\n        }\n\n        return domain;\n    }\n\n    async uploadFiles(files) {\n        await this.uploadService.uploadFiles(\n            files,\n            { resModel: this.props.resModel, resId: this.props.resId, isImage: true },\n            (attachment) => this.onUploaded(attachment)\n        );\n    }\n\n    async validateUrl(...args) {\n        const { isValidUrl, path } = super.validateUrl(...args);\n        const isValidFileFormat =\n            isValidUrl &&\n            (await new Promise((resolve) => {\n                const img = new Image();\n                img.src = path;\n                img.onload = () => resolve(true);\n                img.onerror = () => resolve(false);\n            }));\n        return { isValidFileFormat, isValidUrl };\n    }\n\n    isInitialMedia(attachment) {\n        if (this.props.media.dataset.originalSrc) {\n            return this.props.media.dataset.originalSrc === attachment.image_src;\n        }\n        return this.props.media.getAttribute(\"src\") === attachment.image_src;\n    }\n\n    async fetchAttachments(limit, offset) {\n        const attachments = await super.fetchAttachments(limit, offset);\n        // Color-substitution for dynamic SVG attachment\n        const primaryColors = {};\n        const htmlStyle = getHtmlStyle(document);\n        for (let color = 1; color <= 5; color++) {\n            primaryColors[color] = getCSSVariableValue(\"o-color-\" + color, htmlStyle);\n        }\n        return attachments.map((attachment) => {\n            if (attachment.image_src.startsWith(\"/\")) {\n                const newURL = new URL(attachment.image_src, window.location.origin);\n                // Set the main colors of dynamic SVGs to o-color-1~5\n                if (\n                    attachment.image_src.startsWith(\"/html_editor/shape/\") ||\n                    attachment.image_src.startsWith(\"/web_editor/shape/\")\n                ) {\n                    newURL.searchParams.forEach((value, key) => {\n                        const match = key.match(/^c([1-5])$/);\n                        if (match) {\n                            newURL.searchParams.set(key, primaryColors[match[1]]);\n                        }\n                    });\n                } else {\n                    // Set height so that db images load faster\n                    newURL.searchParams.set(\"height\", 2 * this.MIN_ROW_HEIGHT);\n                }\n                attachment.thumbnail_src = newURL.pathname + newURL.search;\n            }\n            if (this.selectInitialMedia() && this.isInitialMedia(attachment)) {\n                this.selectAttachment(attachment);\n            }\n            return attachment;\n        });\n    }\n\n    async fetchLibraryMedia(offset) {\n        if (!this.state.needle) {\n            return { media: [], results: null };\n        }\n\n        this.state.isFetchingLibrary = true;\n        try {\n            const response = await rpc(\n                \"/web_editor/media_library_search\",\n                {\n                    query: this.state.needle,\n                    offset: offset,\n                },\n                {\n                    silent: true,\n                }\n            );\n            this.state.isFetchingLibrary = false;\n            const media = (response.media || []).slice(0, this.NUMBER_OF_MEDIA_TO_DISPLAY);\n            media.forEach((record) => (record.mediaType = \"libraryMedia\"));\n            return { media, results: response.results };\n        } catch {\n            // Either API endpoint doesn't exist or is misconfigured.\n            console.error(`Couldn't reach API endpoint.`);\n            this.state.isFetchingLibrary = false;\n            return { media: [], results: null };\n        }\n    }\n\n    async loadMore(...args) {\n        await super.loadMore(...args);\n        if (\n            !this.props.useMediaLibrary ||\n            // The user can load more library media only when the filter is set.\n            this.state.searchService !== \"media-library\"\n        ) {\n            return;\n        }\n        return this.keepLastLibraryMedia\n            .add(this.fetchLibraryMedia(this.state.libraryMedia.length))\n            .then(({ media }) => {\n                // This is never reached if another search or loadMore occurred.\n                this.state.libraryMedia.push(...media);\n            });\n    }\n\n    async search(...args) {\n        await super.search(...args);\n        if (!this.props.useMediaLibrary) {\n            return;\n        }\n        if (!this.state.needle) {\n            this.state.searchService = \"all\";\n        }\n        this.state.libraryMedia = [];\n        this.state.libraryResults = 0;\n        return this.keepLastLibraryMedia\n            .add(this.fetchLibraryMedia(0))\n            .then(({ media, results }) => {\n                // This is never reached if a new search occurred.\n                this.state.libraryMedia = media;\n                this.state.libraryResults = results;\n            });\n    }\n\n    async onClickAttachment(attachment) {\n        this.selectAttachment(attachment);\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    }\n\n    async onClickMedia(media) {\n        this.props.selectMedia({ ...media, mediaType: \"libraryMedia\" });\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    }\n\n    /**\n     * Utility method used by the MediaDialog component.\n     */\n    static async createElements(selectedMedia, { orm }) {\n        // Create all media-library attachments.\n        const toSave = Object.fromEntries(\n            selectedMedia\n                .filter((media) => media.mediaType === \"libraryMedia\")\n                .map((media) => [\n                    media.id,\n                    {\n                        query: media.query || \"\",\n                        is_dynamic_svg: !!media.isDynamicSVG,\n                        dynamic_colors: media.dynamicColors,\n                    },\n                ])\n        );\n        let savedMedia = [];\n        if (Object.keys(toSave).length !== 0) {\n            savedMedia = await rpc(\"/html_editor/save_library_media\", { media: toSave });\n        }\n        const selected = selectedMedia\n            .filter((media) => media.mediaType === \"attachment\")\n            .concat(savedMedia)\n            .map((attachment) => {\n                // Color-customize dynamic SVGs with the theme colors\n                if (attachment.image_src && (\n                    attachment.image_src.startsWith(\"/html_editor/shape/\") ||\n                    attachment.image_src.startsWith(\"/web_editor/shape/\")\n                )) {\n                    const colorCustomizedURL = new URL(\n                        attachment.image_src,\n                        window.location.origin\n                    );\n                    const htmlStyle = getHtmlStyle(document);\n                    colorCustomizedURL.searchParams.forEach((value, key) => {\n                        const match = key.match(/^c([1-5])$/);\n                        if (match) {\n                            colorCustomizedURL.searchParams.set(\n                                key,\n                                getCSSVariableValue(`o-color-${match[1]}`, htmlStyle)\n                            );\n                        }\n                    });\n                    attachment.image_src = colorCustomizedURL.pathname + colorCustomizedURL.search;\n                }\n                return attachment;\n            });\n        return Promise.all(\n            selected.map(async (attachment) => {\n                const imageEl = document.createElement(\"img\");\n                let src = attachment.image_src;\n                if (!attachment.public && !attachment.url) {\n                    let accessToken = attachment.access_token;\n                    if (!accessToken) {\n                        [accessToken] = await orm.call(\"ir.attachment\", \"generate_access_token\", [\n                            attachment.id,\n                        ]);\n                    }\n                    src += `?access_token=${encodeURIComponent(accessToken)}`;\n                }\n                imageEl.src = src;\n                imageEl.alt = attachment.description || \"\";\n                return imageEl;\n            })\n        );\n    }\n\n    async onImageLoaded(imgEl, attachment) {\n        this.debouncedScrollUpdate();\n        if (attachment.mediaType === \"libraryMedia\" && !imgEl.src.startsWith(\"blob\")) {\n            // This call applies the theme's color palette to the\n            // loaded illustration. Upon replacement of the image,\n            // `onImageLoad` is called again, but the replacement image\n            // has an URL that starts with 'blob'. The condition above\n            // uses this to avoid an infinite loop.\n            await this.onLibraryImageLoaded(imgEl, attachment);\n        }\n    }\n\n    /**\n     * This converts the colors of an svg coming from the media library to\n     * the palette's ones, and make them dynamic.\n     *\n     * @param {HTMLElement} imgEl\n     * @param {Object} media\n     * @returns\n     */\n    async onLibraryImageLoaded(imgEl, media) {\n        const mediaUrl = imgEl.src;\n        try {\n            const response = await fetch(mediaUrl);\n            if (response.headers.get(\"content-type\") === \"image/svg+xml\") {\n                let svg = await response.text();\n                const dynamicColors = {};\n                const combinedColorsRegex = new RegExp(\n                    Object.values(DEFAULT_PALETTE).join(\"|\"),\n                    \"gi\"\n                );\n                const htmlStyle = getHtmlStyle(document);\n                svg = svg.replace(combinedColorsRegex, (match) => {\n                    const colorId = Object.keys(DEFAULT_PALETTE).find(\n                        (key) => DEFAULT_PALETTE[key] === match.toUpperCase()\n                    );\n                    const colorKey = \"c\" + colorId;\n                    dynamicColors[colorKey] = getCSSVariableValue(\"o-color-\" + colorId, htmlStyle);\n                    return dynamicColors[colorKey];\n                });\n                const fileName = mediaUrl.split(\"/\").pop();\n                const file = new File([svg], fileName, {\n                    type: \"image/svg+xml\",\n                });\n                imgEl.src = URL.createObjectURL(file);\n                if (Object.keys(dynamicColors).length) {\n                    media.isDynamicSVG = true;\n                    media.dynamicColors = dynamicColors;\n                }\n            }\n        } catch {\n            console.error(\n                \"CORS is misconfigured on the API server, image will be treated as non-dynamic.\"\n            );\n        }\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { useService, useChildRef } from \"@web/core/utils/hooks\";\nimport { Dialog } from \"@web/core/dialog/dialog\";\nimport { Notebook } from \"@web/core/notebook/notebook\";\nimport { ImageSelector } from \"./image_selector\";\nimport { DocumentSelector } from \"./document_selector\";\nimport { IconSelector } from \"./icon_selector\";\nimport { VideoSelector } from \"./video_selector\";\n\nimport { Component, useState, useRef, useEffect } from \"@odoo/owl\";\n\nexport const TABS = {\n    IMAGES: {\n        id: \"IMAGES\",\n        title: _t(\"Images\"),\n        Component: ImageSelector,\n    },\n    DOCUMENTS: {\n        id: \"DOCUMENTS\",\n        title: _t(\"Documents\"),\n        Component: DocumentSelector,\n    },\n    ICONS: {\n        id: \"ICONS\",\n        title: _t(\"Icons\"),\n        Component: IconSelector,\n    },\n    VIDEOS: {\n        id: \"VIDEOS\",\n        title: _t(\"Videos\"),\n        Component: VideoSelector,\n    },\n};\n\nexport class MediaDialog extends Component {\n    static template = \"html_editor.MediaDialog\";\n    static defaultProps = {\n        useMediaLibrary: true,\n    };\n    static components = {\n        ...Object.keys(TABS).map((key) => TABS[key].Component),\n        Dialog,\n        Notebook,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.size = \"xl\";\n        this.contentClass = \"o_select_media_dialog h-100\";\n        this.title = _t(\"Select a media\");\n        this.modalRef = useChildRef();\n\n        this.orm = useService(\"orm\");\n        this.notificationService = useService(\"notification\");\n\n        this.tabs = [];\n        this.selectedMedia = useState({});\n\n        this.addButtonRef = useRef(\"add-button\");\n\n        this.initialIconClasses = [];\n\n        this.addTabs();\n        this.errorMessages = {};\n\n        this.state = useState({\n            activeTab: this.initialActiveTab,\n        });\n\n        useEffect(\n            (nbSelectedAttachments) => {\n                // Disable/enable the add button depending on whether some media\n                // are selected or not.\n                this.addButtonRef.el.toggleAttribute(\"disabled\", !nbSelectedAttachments);\n            },\n            () => [this.selectedMedia[this.state.activeTab].length]\n        );\n    }\n\n    get initialActiveTab() {\n        if (this.props.activeTab) {\n            return this.props.activeTab;\n        }\n        if (this.props.media) {\n            const correspondingTab = Object.keys(TABS).find((id) =>\n                TABS[id].Component.tagNames.includes(this.props.media.tagName)\n            );\n            if (correspondingTab) {\n                return correspondingTab;\n            }\n        }\n        return this.tabs[0].id;\n    }\n\n    addTab(tab, additionalProps = {}) {\n        this.selectedMedia[tab.id] = [];\n        this.tabs.push({\n            ...tab,\n            props: {\n                ...tab.props,\n                ...additionalProps,\n                id: tab.id,\n                resModel: this.props.resModel,\n                resId: this.props.resId,\n                media: this.props.media,\n                // multiImages: this.props.multiImages,\n                selectedMedia: this.selectedMedia,\n                selectMedia: (...args) =>\n                    this.selectMedia(...args, tab.id, additionalProps.multiSelect),\n                save: this.save.bind(this),\n                onAttachmentChange: this.props.onAttachmentChange,\n                errorMessages: (errorMessage) => (this.errorMessages[tab.id] = errorMessage),\n                modalRef: this.modalRef,\n            },\n        });\n    }\n\n    addTabs() {\n        const onlyImages =\n            this.props.onlyImages ||\n            (this.props.media &&\n                this.props.media.parentElement &&\n                (this.props.media.parentElement.dataset.oeField === \"image\" ||\n                    this.props.media.parentElement.dataset.oeType === \"image\"));\n        const noDocuments = onlyImages || this.props.noDocuments;\n        const noIcons = onlyImages || this.props.noIcons;\n        const noVideos = onlyImages || this.props.noVideos;\n\n        if (!this.props.noImages) {\n            this.addTab(TABS.IMAGES, {\n                useMediaLibrary: this.props.useMediaLibrary,\n                multiSelect: this.props.multiImages,\n            });\n        }\n        if (!noDocuments) {\n            this.addTab(TABS.DOCUMENTS);\n        }\n        if (!noIcons) {\n            const fonts = TABS.ICONS.Component.initFonts();\n            this.addTab(TABS.ICONS, {\n                fonts,\n            });\n\n            if (\n                this.props.media &&\n                TABS.ICONS.Component.tagNames.includes(this.props.media.tagName)\n            ) {\n                const classes = this.props.media.className.split(/\\s+/);\n                const mediaFont = fonts.find((font) => classes.includes(font.base));\n                if (mediaFont) {\n                    const selectedIcon = mediaFont.icons.find((icon) =>\n                        icon.names.some((name) => classes.includes(name))\n                    );\n                    if (selectedIcon) {\n                        this.initialIconClasses.push(...selectedIcon.names);\n                        this.selectMedia(selectedIcon, TABS.ICONS.id);\n                    }\n                }\n            }\n        }\n        if (!noVideos) {\n            this.addTab(TABS.VIDEOS, {\n                vimeoPreviewIds: this.props.vimeoPreviewIds,\n                isForBgVideo: this.props.isForBgVideo,\n            });\n        }\n    }\n\n    /**\n     * Render the selected media for insertion in the editor\n     *\n     * @param {Array<Object>} selectedMedia\n     * @returns {Array<HTMLElement>}\n     */\n    async renderMedia(selectedMedia) {\n        const elements = await TABS[this.state.activeTab].Component.createElements(selectedMedia, {\n            orm: this.orm,\n        });\n        elements.forEach((element) => {\n            if (this.props.media) {\n                element.classList.add(...this.props.media.classList);\n                const style = this.props.media.getAttribute(\"style\");\n                if (style) {\n                    element.setAttribute(\"style\", style);\n                }\n                if (this.state.activeTab === TABS.IMAGES.id) {\n                    if (this.props.media.dataset.shape) {\n                        element.dataset.shape = this.props.media.dataset.shape;\n                    }\n                    if (this.props.media.dataset.shapeColors) {\n                        element.dataset.shapeColors = this.props.media.dataset.shapeColors;\n                    }\n                    if (this.props.media.dataset.shapeFlip) {\n                        element.dataset.shapeFlip = this.props.media.dataset.shapeFlip;\n                    }\n                    if (this.props.media.dataset.shapeRotate) {\n                        element.dataset.shapeRotate = this.props.media.dataset.shapeRotate;\n                    }\n                    if (this.props.media.dataset.hoverEffect) {\n                        element.dataset.hoverEffect = this.props.media.dataset.hoverEffect;\n                    }\n                    if (this.props.media.dataset.hoverEffectColor) {\n                        element.dataset.hoverEffectColor =\n                            this.props.media.dataset.hoverEffectColor;\n                    }\n                    if (this.props.media.dataset.hoverEffectStrokeWidth) {\n                        element.dataset.hoverEffectStrokeWidth =\n                            this.props.media.dataset.hoverEffectStrokeWidth;\n                    }\n                    if (this.props.media.dataset.hoverEffectIntensity) {\n                        element.dataset.hoverEffectIntensity =\n                            this.props.media.dataset.hoverEffectIntensity;\n                    }\n                }\n            }\n            for (const otherTab of Object.keys(TABS).filter(\n                (key) => key !== this.state.activeTab\n            )) {\n                for (const property of TABS[otherTab].Component.mediaSpecificStyles) {\n                    element.style.removeProperty(property);\n                }\n                element.classList.remove(...TABS[otherTab].Component.mediaSpecificClasses);\n                const extraClassesToRemove = [];\n                for (const name of TABS[otherTab].Component.mediaExtraClasses) {\n                    if (typeof name === \"string\") {\n                        extraClassesToRemove.push(name);\n                    } else {\n                        // Regex\n                        for (const className of element.classList) {\n                            if (className.match(name)) {\n                                extraClassesToRemove.push(className);\n                            }\n                        }\n                    }\n                }\n                // Remove classes that do not also exist in the target type.\n                element.classList.remove(\n                    ...extraClassesToRemove.filter((candidateName) => {\n                        for (const name of TABS[this.state.activeTab].Component.mediaExtraClasses) {\n                            if (typeof name === \"string\") {\n                                if (candidateName === name) {\n                                    return false;\n                                }\n                            } else {\n                                // Regex\n                                for (const className of element.classList) {\n                                    if (className.match(candidateName)) {\n                                        return false;\n                                    }\n                                }\n                            }\n                        }\n                        return true;\n                    })\n                );\n            }\n            element.classList.remove(...this.initialIconClasses);\n            element.classList.remove(\"o_modified_image_to_save\");\n            element.classList.remove(\"oe_edited_link\");\n            element.classList.add(...TABS[this.state.activeTab].Component.mediaSpecificClasses);\n        });\n        return elements;\n    }\n\n    selectMedia(media, tabId, multiSelect) {\n        if (multiSelect) {\n            const isMediaSelected = this.selectedMedia[tabId]\n                .map(({ id }) => id)\n                .includes(media.id);\n            if (!isMediaSelected) {\n                this.selectedMedia[tabId].push(media);\n            } else {\n                this.selectedMedia[tabId] = this.selectedMedia[tabId].filter(\n                    (m) => m.id !== media.id\n                );\n            }\n        } else {\n            this.selectedMedia[tabId] = [media];\n        }\n    }\n\n    async save() {\n        if (this.errorMessages[this.state.activeTab]) {\n            this.notificationService.add(this.errorMessages[this.state.activeTab], {\n                type: \"danger\",\n            });\n            return;\n        }\n        const selectedMedia = this.selectedMedia[this.state.activeTab];\n        // TODO In master: clean the save method so it performs the specific\n        // adaptation before saving from the active media selector and find a\n        // way to simply close the dialog if the media element remains the same.\n        const saveSelectedMedia =\n            selectedMedia.length &&\n            (this.state.activeTab !== TABS.ICONS.id ||\n                selectedMedia[0].initialIconChanged ||\n                !this.props.media);\n        if (saveSelectedMedia) {\n            const elements = await this.renderMedia(selectedMedia);\n            if (this.props.multiImages) {\n                this.props.save(elements);\n            } else {\n                this.props.save(elements[0]);\n            }\n        }\n        this.props.close();\n    }\n\n    onTabChange(tab) {\n        this.state.activeTab = tab;\n    }\n}\n", "import { useDebounced } from \"@web/core/utils/timing\";\nimport { useAutofocus } from \"@web/core/utils/hooks\";\n\nimport { Component, xml, useEffect, useState } from \"@odoo/owl\";\n\nexport class SearchMedia extends Component {\n    static template = xml`\n        <div class=\"position-relative mw-lg-25 flex-grow-1 me-auto\">\n            <input type=\"text\" class=\"o_we_search o_input form-control\" t-att-placeholder=\"props.searchPlaceholder.trim()\" t-model=\"state.input\" t-ref=\"autofocus\"/>\n            <i class=\"oi oi-search input-group-text position-absolute end-0 top-50 me-n3 px-2 py-1 translate-middle bg-transparent border-0\" title=\"Search\" role=\"img\" aria-label=\"Search\"/>\n        </div>`;\n    static props = [\"searchPlaceholder\", \"search\", \"needle\"];\n    setup() {\n        useAutofocus({ mobile: true });\n        this.debouncedSearch = useDebounced(this.props.search, 1000);\n\n        this.state = useState({\n            input: this.props.needle || \"\",\n        });\n\n        useEffect(\n            (input) => {\n                // Do not trigger a search on the initial render.\n                if (this.hasRendered) {\n                    this.debouncedSearch(input);\n                } else {\n                    this.hasRendered = true;\n                }\n            },\n            () => [this.state.input]\n        );\n    }\n}\n", "import { useService } from \"@web/core/utils/hooks\";\nimport { Component, useState } from \"@odoo/owl\";\n\nexport class ProgressBar extends Component {\n    static template = \"html_editor.ProgressBar\";\n    static props = {\n        progress: { type: Number, optional: true },\n        hasError: { type: Boolean, optional: true },\n        uploaded: { type: Boolean, optional: true },\n        name: String,\n        size: { type: String, optional: true },\n        errorMessage: { type: String, optional: true },\n    };\n    static defaultProps = {\n        progress: 0,\n        hasError: false,\n        uploaded: false,\n        size: \"\",\n        errorMessage: \"\",\n    };\n\n    get progress() {\n        return Math.round(this.props.progress);\n    }\n}\n\nexport class UploadProgressToast extends Component {\n    static template = \"html_editor.UploadProgressToast\";\n    static components = {\n        ProgressBar,\n    };\n    static props = {\n        close: Function,\n    };\n\n    setup() {\n        this.uploadService = useService(\"upload\");\n        this.state = useState(this.uploadService.progressToast);\n    }\n}\n", "import { rpc } from \"@web/core/network/rpc\";\nimport { registry } from \"@web/core/registry\";\nimport { UploadProgressToast } from \"./upload_progress_toast\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { checkFileSize } from \"@web/core/utils/files\";\nimport { humanNumber } from \"@web/core/utils/numbers\";\nimport { getDataURLFromFile } from \"@web/core/utils/urls\";\nimport { sprintf } from \"@web/core/utils/strings\";\nimport { reactive } from \"@odoo/owl\";\n\nexport const AUTOCLOSE_DELAY = 3000;\nexport const AUTOCLOSE_DELAY_LONG = 8000;\n\nexport const uploadService = {\n    dependencies: [\"notification\"],\n    start(env, { notification }) {\n        let fileId = 0;\n        const progressToast = reactive({\n            files: {},\n            isVisible: false,\n        });\n\n        registry.category(\"main_components\").add(\"UploadProgressToast\", {\n            Component: UploadProgressToast,\n            props: {\n                close: () => (progressToast.isVisible = false),\n            },\n        });\n\n        const addFile = (file) => {\n            progressToast.files[file.id] = file;\n            progressToast.isVisible = true;\n            return progressToast.files[file.id];\n        };\n\n        const deleteFile = (fileId) => {\n            delete progressToast.files[fileId];\n            if (!Object.keys(progressToast.files).length) {\n                progressToast.isVisible = false;\n            }\n        };\n        return {\n            get progressToast() {\n                return progressToast;\n            },\n            get fileId() {\n                return fileId;\n            },\n            addFile,\n            deleteFile,\n            incrementId() {\n                fileId++;\n            },\n            uploadUrl: async (url, { resModel, resId }, onUploaded) => {\n                const attachment = await rpc(\"/html_editor/attachment/add_url\", {\n                    url,\n                    res_model: resModel,\n                    res_id: resId,\n                });\n                await onUploaded(attachment);\n            },\n            /**\n             * This takes an array of files (from an input HTMLElement), and\n             * uploads them while managing the UploadProgressToast.\n             *\n             * @param {Array<File>} files\n             * @param {Object} options\n             * @param {Function} onUploaded\n             */\n            uploadFiles: async (files, { resModel, resId, isImage }, onUploaded) => {\n                // Upload the smallest file first to block the user the least possible.\n                const sortedFiles = Array.from(files).sort((a, b) => a.size - b.size);\n                for (const file of sortedFiles) {\n                    let fileSize = file.size;\n                    if (!checkFileSize(fileSize, notification)) {\n                        return null;\n                    }\n                    if (!fileSize) {\n                        fileSize = \"\";\n                    } else {\n                        fileSize = humanNumber(fileSize) + \"B\";\n                    }\n\n                    const id = ++fileId;\n                    file.progressToastId = id;\n                    // This reactive object, built based on the files array,\n                    // is given as a prop to the UploadProgressToast.\n                    addFile({\n                        id,\n                        name: file.name,\n                        size: fileSize,\n                    });\n                }\n\n                // Upload one file at a time: no need to parallel as upload is\n                // limited by bandwidth.\n                for (const sortedFile of sortedFiles) {\n                    const file = progressToast.files[sortedFile.progressToastId];\n                    let dataURL;\n                    try {\n                        dataURL = await getDataURLFromFile(sortedFile);\n                    } catch {\n                        deleteFile(file.id);\n                        env.services.notification.add(\n                            sprintf(_t('Could not load the file \"%s\".'), sortedFile.name),\n                            { type: \"danger\" }\n                        );\n                        continue;\n                    }\n                    try {\n                        const xhr = new XMLHttpRequest();\n                        xhr.upload.addEventListener(\"progress\", (ev) => {\n                            const rpcComplete = (ev.loaded / ev.total) * 100;\n                            file.progress = rpcComplete;\n                        });\n                        xhr.upload.addEventListener(\"load\", function () {\n                            // Don't show yet success as backend code only starts now\n                            file.progress = 100;\n                        });\n                        const attachment = await rpc(\n                            \"/html_editor/attachment/add_data\",\n                            {\n                                name: file.name,\n                                data: dataURL.split(\",\")[1],\n                                res_id: resId,\n                                res_model: resModel,\n                                is_image: !!isImage,\n                                width: 0,\n                                quality: 0,\n                            },\n                            { xhr }\n                        );\n                        if (attachment.error) {\n                            file.hasError = true;\n                            file.errorMessage = attachment.error;\n                        } else {\n                            if (attachment.mimetype === \"image/webp\") {\n                                // Generate alternate format for reports.\n                                const image = document.createElement(\"img\");\n                                image.src = `data:image/webp;base64,${dataURL.split(\",\")[1]}`;\n                                await new Promise((resolve) =>\n                                    image.addEventListener(\"load\", resolve)\n                                );\n                                const canvas = document.createElement(\"canvas\");\n                                canvas.width = image.width;\n                                canvas.height = image.height;\n                                const ctx = canvas.getContext(\"2d\");\n                                ctx.fillStyle = \"rgb(255, 255, 255)\";\n                                ctx.fillRect(0, 0, canvas.width, canvas.height);\n                                ctx.drawImage(image, 0, 0);\n                                const altDataURL = canvas.toDataURL(\"image/jpeg\", 0.75);\n                                await rpc(\n                                    \"/html_editor/attachment/add_data\",\n                                    {\n                                        name: file.name.replace(/\\.webp$/, \".jpg\"),\n                                        data: altDataURL.split(\",\")[1],\n                                        res_id: attachment.id,\n                                        res_model: \"ir.attachment\",\n                                        is_image: true,\n                                        width: 0,\n                                        quality: 0,\n                                    },\n                                    { xhr }\n                                );\n                            }\n                            file.uploaded = true;\n                            await onUploaded(attachment);\n                        }\n                        // If there's an error, display the error message for longer\n                        const message_autoclose_delay = file.hasError\n                            ? AUTOCLOSE_DELAY_LONG\n                            : AUTOCLOSE_DELAY;\n                        setTimeout(() => deleteFile(file.id), message_autoclose_delay);\n                    } catch (error) {\n                        file.hasError = true;\n                        setTimeout(() => deleteFile(file.id), AUTOCLOSE_DELAY_LONG);\n                        throw error;\n                    }\n                }\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"upload\", uploadService);\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useAutofocus, useService } from \"@web/core/utils/hooks\";\nimport { debounce } from \"@web/core/utils/timing\";\n\nimport { Component, useState, useRef, onMounted, onWillStart } from \"@odoo/owl\";\n\nclass VideoOption extends Component {\n    static template = \"html_editor.VideoOption\";\n    static props = {\n        description: { type: String, optional: true },\n        label: { type: String, optional: true },\n        onChangeOption: Function,\n        value: { type: Boolean, optional: true },\n    };\n}\n\nclass VideoIframe extends Component {\n    static template = \"html_editor.VideoIframe\";\n    static props = {\n        src: { type: String },\n    };\n}\n\nexport class VideoSelector extends Component {\n    static mediaSpecificClasses = [\"media_iframe_video\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [];\n    static tagNames = [\"IFRAME\", \"DIV\"];\n    static template = \"html_editor.VideoSelector\";\n    static components = {\n        VideoIframe,\n        VideoOption,\n    };\n    static props = {\n        selectMedia: Function,\n        errorMessages: Function,\n        vimeoPreviewIds: { type: Array, optional: true },\n        isForBgVideo: { type: Boolean, optional: true },\n        media: { type: Object, optional: true },\n        \"*\": true,\n    };\n    static defaultProps = {\n        vimeoPreviewIds: [],\n        isForBgVideo: false,\n    };\n\n    setup() {\n        this.http = useService(\"http\");\n\n        this.PLATFORMS = {\n            youtube: \"youtube\",\n            dailymotion: \"dailymotion\",\n            vimeo: \"vimeo\",\n            youku: \"youku\",\n        };\n\n        this.OPTIONS = {\n            autoplay: {\n                label: _t(\"Autoplay\"),\n                description: _t(\"Videos are muted when autoplay is enabled\"),\n                platforms: [\n                    this.PLATFORMS.youtube,\n                    this.PLATFORMS.dailymotion,\n                    this.PLATFORMS.vimeo,\n                ],\n                urlParameter: \"autoplay=1\",\n            },\n            loop: {\n                label: _t(\"Loop\"),\n                platforms: [this.PLATFORMS.youtube, this.PLATFORMS.vimeo],\n                urlParameter: \"loop=1\",\n            },\n            hide_controls: {\n                label: _t(\"Hide player controls\"),\n                platforms: [\n                    this.PLATFORMS.youtube,\n                    this.PLATFORMS.dailymotion,\n                    this.PLATFORMS.vimeo,\n                ],\n                urlParameter: \"controls=0\",\n            },\n            hide_fullscreen: {\n                label: _t(\"Hide fullscreen button\"),\n                platforms: [this.PLATFORMS.youtube],\n                urlParameter: \"fs=0\",\n                isHidden: () =>\n                    this.state.options.filter((option) => option.id === \"hide_controls\")[0].value,\n            },\n            hide_dm_logo: {\n                label: _t(\"Hide Dailymotion logo\"),\n                platforms: [this.PLATFORMS.dailymotion],\n                urlParameter: \"ui-logo=0\",\n            },\n            hide_dm_share: {\n                label: _t(\"Hide sharing button\"),\n                platforms: [this.PLATFORMS.dailymotion],\n                urlParameter: \"sharing-enable=0\",\n            },\n        };\n\n        this.state = useState({\n            options: [],\n            src: \"\",\n            urlInput: \"\",\n            platform: null,\n            vimeoPreviews: [],\n            errorMessage: \"\",\n        });\n        this.urlInputRef = useRef(\"url-input\");\n\n        onWillStart(async () => {\n            if (this.props.media) {\n                const src =\n                    this.props.media.dataset.oeExpression ||\n                    this.props.media.dataset.src ||\n                    (this.props.media.tagName === \"IFRAME\" &&\n                        this.props.media.getAttribute(\"src\")) ||\n                    \"\";\n                if (src) {\n                    this.state.urlInput = src;\n                    await this.updateVideo();\n\n                    this.state.options = this.state.options.map((option) => {\n                        const { urlParameter } = this.OPTIONS[option.id];\n                        return { ...option, value: src.indexOf(urlParameter) >= 0 };\n                    });\n                }\n            }\n        });\n\n        onMounted(async () => {\n            await Promise.all(\n                this.props.vimeoPreviewIds.map(async (videoId) => {\n                    const { thumbnail_url: thumbnailSrc } = await this.http.get(\n                        `https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${encodeURIComponent(\n                            videoId\n                        )}`\n                    );\n                    this.state.vimeoPreviews.push({\n                        id: videoId,\n                        thumbnailSrc,\n                        src: `https://player.vimeo.com/video/${encodeURIComponent(videoId)}`,\n                    });\n                })\n            );\n        });\n\n        useAutofocus();\n\n        this.onChangeUrl = debounce((ev) => this.updateVideo(ev.target.value), 500);\n    }\n\n    get shownOptions() {\n        if (this.props.isForBgVideo) {\n            return [];\n        }\n        return this.state.options.filter(\n            (option) => !this.OPTIONS[option.id].isHidden || !this.OPTIONS[option.id].isHidden()\n        );\n    }\n\n    async onChangeOption(optionId) {\n        this.state.options = this.state.options.map((option) => {\n            if (option.id === optionId) {\n                return { ...option, value: !option.value };\n            }\n            return option;\n        });\n        await this.updateVideo();\n    }\n\n    async onClickSuggestion(src) {\n        this.state.urlInput = src;\n        await this.updateVideo();\n    }\n\n    async updateVideo() {\n        if (!this.state.urlInput) {\n            this.state.src = \"\";\n            this.state.urlInput = \"\";\n            this.state.options = [];\n            this.state.platform = null;\n            this.state.errorMessage = \"\";\n            /**\n             * When the url input is emptied, we need to call the `selectMedia`\n             * callback function to notify the other components that the media\n             * has changed.\n             */\n            this.props.selectMedia({});\n            return;\n        }\n\n        // Detect if we have an embed code rather than an URL\n        const embedMatch = this.state.urlInput.match(/(src|href)=[\"']?([^\"']+)?/);\n        if (embedMatch && embedMatch[2].length > 0 && embedMatch[2].indexOf(\"instagram\")) {\n            embedMatch[1] = embedMatch[2]; // Instagram embed code is different\n        }\n        const url = embedMatch ? embedMatch[1] : this.state.urlInput;\n\n        const options = {};\n        if (this.props.isForBgVideo) {\n            Object.keys(this.OPTIONS).forEach((key) => {\n                options[key] = true;\n            });\n        } else {\n            for (const option of this.shownOptions) {\n                options[option.id] = option.value;\n            }\n        }\n\n        const {\n            embed_url: src,\n            video_id: videoId,\n            params,\n            platform,\n        } = await this._getVideoURLData(url, options);\n\n        if (!src) {\n            this.state.errorMessage = _t(\"The provided url is not valid\");\n        } else if (!platform) {\n            this.state.errorMessage = _t(\"The provided url does not reference any supported video\");\n        } else {\n            this.state.errorMessage = \"\";\n        }\n        this.props.errorMessages(this.state.errorMessage);\n\n        const newOptions = [];\n        if (platform && platform !== this.state.platform) {\n            Object.keys(this.OPTIONS).forEach((key) => {\n                if (this.OPTIONS[key].platforms.includes(platform)) {\n                    const { label, description } = this.OPTIONS[key];\n                    newOptions.push({ id: key, label, description });\n                }\n            });\n        }\n\n        this.state.src = src;\n        this.props.selectMedia({\n            id: src,\n            src,\n            platform,\n            videoId,\n            params,\n        });\n        if (platform !== this.state.platform) {\n            this.state.platform = platform;\n            this.state.options = newOptions;\n        }\n    }\n\n    /**\n     * Keep rpc call in distinct method make it patchable by test.\n     */\n    async _getVideoURLData(url, options) {\n        return await rpc(\"/web_editor/video_url/data\", {\n            video_url: url,\n            ...options,\n        });\n    }\n\n    /**\n     * Utility method, called by the MediaDialog component.\n     */\n    static createElements(selectedMedia) {\n        return selectedMedia.map((video) => {\n            const div = document.createElement(\"div\");\n            div.dataset.oeExpression = video.src;\n            div.innerHTML =\n                '<div class=\"css_editable_mode_display\"></div>' +\n                '<div class=\"media_iframe_video_size\" contenteditable=\"false\"></div>' +\n                '<iframe frameborder=\"0\" contenteditable=\"false\" allowfullscreen=\"allowfullscreen\"></iframe>';\n\n            div.querySelector(\"iframe\").src = video.src;\n            return div;\n        });\n    }\n}\n", "import { closestPath, findNode } from \"./dom_traversal\";\n\nconst blockTagNames = [\n    \"ADDRESS\",\n    \"ARTICLE\",\n    \"ASIDE\",\n    \"BLOCKQUOTE\",\n    \"DETAILS\",\n    \"DIALOG\",\n    \"DD\",\n    \"DIV\",\n    \"DL\",\n    \"DT\",\n    \"FIELDSET\",\n    \"FIGCAPTION\",\n    \"FIGURE\",\n    \"FOOTER\",\n    \"FORM\",\n    \"H1\",\n    \"H2\",\n    \"H3\",\n    \"H4\",\n    \"H5\",\n    \"H6\",\n    \"HEADER\",\n    \"HGROUP\",\n    \"HR\",\n    \"LI\",\n    \"MAIN\",\n    \"NAV\",\n    \"OL\",\n    \"P\",\n    \"PRE\",\n    \"SECTION\",\n    \"TABLE\",\n    \"UL\",\n    // The following elements are not in the W3C list, for some reason.\n    \"SELECT\",\n    \"OPTION\",\n    \"TR\",\n    \"TD\",\n    \"TBODY\",\n    \"THEAD\",\n    \"TH\",\n];\n\nconst computedStyles = new WeakMap();\n\n/**\n * Return true if the given node is a block-level element, false otherwise.\n *\n * @param node\n */\nexport function isBlock(node) {\n    if (!node || node.nodeType !== Node.ELEMENT_NODE) {\n        return false;\n    }\n    const tagName = node.nodeName.toUpperCase();\n    if (tagName === \"BR\") {\n        // A <br> is always inline but getComputedStyle(br).display mistakenly\n        // returns 'block' if its parent is display:flex (at least on Chrome and\n        // Firefox (Linux)). Browsers normally support setting a <br>'s display\n        // property to 'none' but any other change is not supported. Therefore\n        // it is safe to simply declare that a <br> is never supposed to be a\n        // block.\n        return false;\n    }\n    // The node might not be in the DOM, in which case it has no CSS values.\n    if (!node.isConnected) {\n        return blockTagNames.includes(tagName);\n    }\n    // We won't call `getComputedStyle` more than once per node.\n    let style = computedStyles.get(node);\n    if (!style) {\n        style = node.ownerDocument.defaultView.getComputedStyle(node);\n        computedStyles.set(node, style);\n    }\n    if (style.display) {\n        return !style.display.includes(\"inline\") && style.display !== \"contents\";\n    }\n    return blockTagNames.includes(tagName);\n}\n\nexport function closestBlock(node) {\n    return findNode(closestPath(node), (node) => isBlock(node));\n}\n", "import { closestElement } from \"@html_editor/utils/dom_traversal\";\n\nexport const COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES = [\n    \"primary\",\n    \"secondary\",\n    \"alpha\",\n    \"beta\",\n    \"gamma\",\n    \"delta\",\n    \"epsilon\",\n    \"success\",\n    \"info\",\n    \"warning\",\n    \"danger\",\n];\n\n/**\n * Colors of the default palette, used for substitution in shapes/illustrations.\n * key: number of the color in the palette (ie, o-color-<1-5>)\n * value: color hex code\n */\nexport const DEFAULT_PALETTE = {\n    1: \"#3AADAA\",\n    2: \"#7C6576\",\n    3: \"#F6F6F6\",\n    4: \"#FFFFFF\",\n    5: \"#383E45\",\n};\n\n/**\n * These constants are colors that can be edited by the user when using\n * web_editor in a website context. We keep track of them so that color\n * palettes and their preview elements can always have the right colors\n * displayed even if website has redefined the colors during an editing\n * session.\n *\n * @type {string[]}\n */\nexport const EDITOR_COLOR_CSS_VARIABLES = [...COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES];\n\n// o-cc and o-colors\nfor (let i = 1; i <= 5; i++) {\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-color-${i}`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-bg`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-bg-gradient`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-headings`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary-border`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary-border`);\n}\n\n// Grays\nfor (let i = 100; i <= 900; i += 100) {\n    EDITOR_COLOR_CSS_VARIABLES.push(`${i}`);\n}\n\n/**\n * Takes a color (rgb, rgba or hex) and returns its hex representation. If the\n * color is given in rgba, the background color of the node whose color we're\n * converting is used in conjunction with the alpha to compute the resulting\n * color (using the formula: `alpha*color + (1 - alpha)*background` for each\n * channel).\n *\n * @param {string} rgb\n * @param {HTMLElement} [node]\n * @returns {string} hexadecimal color (#RRGGBB)\n */\nexport function rgbToHex(rgb = \"\", node = null) {\n    if (rgb.startsWith(\"#\")) {\n        return rgb;\n    } else if (rgb.startsWith(\"rgba\")) {\n        const values = rgb.match(/[\\d.]{1,5}/g) || [];\n        const alpha = parseFloat(values.pop());\n        // Retrieve the background color.\n        let bgRgbValues = [];\n        if (node) {\n            let bgColor = getComputedStyle(node).backgroundColor;\n            if (bgColor.startsWith(\"rgba\")) {\n                // The background color is itself rgba so we need to compute\n                // the resulting color using the background color of its\n                // parent.\n                bgColor = rgbToHex(bgColor, node.parentElement);\n            }\n            if (bgColor && bgColor.startsWith(\"#\")) {\n                bgRgbValues = (bgColor.match(/[\\da-f]{2}/gi) || []).map((val) => parseInt(val, 16));\n            } else if (bgColor && bgColor.startsWith(\"rgb\")) {\n                bgRgbValues = (bgColor.match(/[\\d.]{1,5}/g) || []).map((val) => parseInt(val));\n            }\n        }\n        bgRgbValues = bgRgbValues.length ? bgRgbValues : [255, 255, 255]; // Default to white.\n\n        return (\n            \"#\" +\n            values\n                .map((value, index) => {\n                    const converted = Math.floor(\n                        alpha * parseInt(value) + (1 - alpha) * bgRgbValues[index]\n                    );\n                    const hex = parseInt(converted).toString(16);\n                    return hex.length === 1 ? \"0\" + hex : hex;\n                })\n                .join(\"\")\n        );\n    } else {\n        return (\n            \"#\" +\n            (rgb.match(/\\d{1,3}/g) || [])\n                .map((x) => {\n                    x = parseInt(x).toString(16);\n                    return x.length === 1 ? \"0\" + x : x;\n                })\n                .join(\"\")\n        );\n    }\n}\n\n/**\n * @param {string|number} name\n * @returns {boolean}\n */\nexport function isColorCombinationName(name) {\n    const number = parseInt(name);\n    return !isNaN(number) && number % 100 !== 0;\n}\n\n/**\n * @param {string} [value]\n * @returns {boolean}\n */\nexport function isColorGradient(value) {\n    return value && value.includes(\"-gradient(\");\n}\n\nexport const TEXT_CLASSES_REGEX = /\\btext-[^\\s]*\\b/;\nexport const BG_CLASSES_REGEX = /\\bbg-[^\\s]*\\b/;\n\n/**\n * Returns true if the given element has a visible color (fore- or\n * -background depending on the given mode).\n *\n * @param {Element} element\n * @param {string} mode 'color' or 'backgroundColor'\n * @returns {boolean}\n */\nexport function hasColor(element, mode) {\n    const style = element.style;\n    const parent = element.parentNode;\n    const classRegex = mode === \"color\" ? TEXT_CLASSES_REGEX : BG_CLASSES_REGEX;\n    if (isColorGradient(style[\"background-image\"])) {\n        if (element.classList.contains(\"text-gradient\")) {\n            if (mode === \"color\") {\n                return true;\n            }\n        } else {\n            if (mode !== \"color\") {\n                return true;\n            }\n        }\n    }\n    return (\n        (style[mode] &&\n            style[mode] !== \"inherit\" &&\n            (!parent || style[mode] !== parent.style[mode])) ||\n        (classRegex.test(element.className) &&\n            (!parent || getComputedStyle(element)[mode] !== getComputedStyle(parent)[mode]))\n    );\n}\n\n/**\n * Returns true if any given nodes has a visible color (fore- or\n * -background depending on the given mode).\n *\n * @param {array} nodes\n * @param {string} mode 'color' or 'backgroundColor'\n * @returns {boolean}\n */\nexport function hasAnyNodesColor(nodes, mode) {\n    for (const node of nodes) {\n        if (hasColor(closestElement(node), mode)) {\n            return true;\n        }\n    }\n    return false;\n}\n", "export const CTYPES = {\n    // Short for CONTENT_TYPES\n    // Inline group\n    CONTENT: 1,\n    SPACE: 2,\n\n    // Block group\n    BLOCK_OUTSIDE: 4,\n    BLOCK_INSIDE: 8,\n\n    // Br group\n    BR: 16,\n};\nexport function ctypeToString(ctype) {\n    return Object.keys(CTYPES).find((key) => CTYPES[key] === ctype);\n}\nexport const CTGROUPS = {\n    // Short for CONTENT_TYPE_GROUPS\n    INLINE: CTYPES.CONTENT | CTYPES.SPACE,\n    BLOCK: CTYPES.BLOCK_OUTSIDE | CTYPES.BLOCK_INSIDE,\n    BR: CTYPES.BR,\n};\n", "import { closestBlock, isBlock } from \"./blocks\";\nimport { isShrunkBlock, isVisible, paragraphRelatedElements } from \"./dom_info\";\nimport { callbacksForCursorUpdate } from \"./selection\";\nimport { isEmptyBlock, isPhrasingContent } from \"../utils/dom_info\";\nimport { childNodes } from \"./dom_traversal\";\nimport { childNodeIndex, DIRECTIONS } from \"./position\";\n\n/** @typedef {import(\"@html_editor/core/selection_plugin\").Cursors} Cursors */\n\n/**\n * Take a node and unwrap all of its block contents recursively. All blocks\n * (except for firstChilds) are preceded by a <br> in order to preserve the line\n * breaks.\n *\n * @param {Node} node\n */\nexport function makeContentsInline(node) {\n    const document = node.ownerDocument;\n    let childIndex = 0;\n    for (const child of node.childNodes) {\n        if (isBlock(child)) {\n            if (childIndex && paragraphRelatedElements.includes(child.nodeName)) {\n                child.before(document.createElement(\"br\"));\n            }\n            for (const grandChild of child.childNodes) {\n                child.before(grandChild);\n                makeContentsInline(grandChild);\n            }\n            child.remove();\n        }\n        childIndex += 1;\n    }\n}\n\n/**\n * Wrap inline children nodes in Blocks, optionally updating cursors for\n * later selection restore. A paragraph is used for phrasing node, and a div\n * is used otherwise.\n *\n * @param {HTMLElement} element - block element\n * @param {Cursors} [cursors]\n */\nexport function wrapInlinesInBlocks(element, cursors = { update: () => {} }) {\n    // Helpers to manipulate preserving selection.\n    const wrapInBlock = (node, cursors) => {\n        const block = isPhrasingContent(node)\n            ? node.ownerDocument.createElement(\"P\")\n            : node.ownerDocument.createElement(\"DIV\");\n        cursors.update(callbacksForCursorUpdate.before(node, block));\n        node.before(block);\n        cursors.update(callbacksForCursorUpdate.append(block, node));\n        block.append(node);\n        return block;\n    };\n    const appendToCurrentBlock = (currentBlock, node, cursors) => {\n        if (currentBlock.tagName === \"P\" && !isPhrasingContent(node)) {\n            const block = document.createElement(\"DIV\");\n            cursors.update(callbacksForCursorUpdate.before(currentBlock, block));\n            currentBlock.before(block);\n            for (const child of [...currentBlock.childNodes]) {\n                cursors.update(callbacksForCursorUpdate.append(block, child));\n                block.append(child);\n            }\n            cursors.update(callbacksForCursorUpdate.remove(currentBlock));\n            currentBlock.remove();\n            currentBlock = block;\n        }\n        cursors.update(callbacksForCursorUpdate.append(currentBlock, node));\n        currentBlock.append(node);\n        return currentBlock;\n    };\n    const removeNode = (node, cursors) => {\n        cursors.update(callbacksForCursorUpdate.remove(node));\n        node.remove();\n    };\n\n    const children = childNodes(element);\n    const visibleNodes = new Set(children.filter(isVisible));\n\n    let currentBlock;\n    let shouldBreakLine = true;\n    for (const node of children) {\n        if (isBlock(node)) {\n            shouldBreakLine = true;\n        } else if (!visibleNodes.has(node)) {\n            removeNode(node, cursors);\n        } else if (node.nodeName === \"BR\") {\n            if (shouldBreakLine) {\n                wrapInBlock(node, cursors);\n            } else {\n                // BR preceded by inline content: discard it and make sure\n                // next inline goes in a new Block\n                removeNode(node, cursors);\n                shouldBreakLine = true;\n            }\n        } else if (shouldBreakLine) {\n            currentBlock = wrapInBlock(node, cursors);\n            shouldBreakLine = false;\n        } else {\n            currentBlock = appendToCurrentBlock(currentBlock, node, cursors);\n        }\n    }\n}\n\nexport function unwrapContents(node) {\n    const contents = childNodes(node);\n    for (const child of contents) {\n        node.parentNode.insertBefore(child, node);\n    }\n    node.parentNode.removeChild(node);\n    return contents;\n}\n\n// @todo @phoenix\n// This utils seem to handle a particular case of LI element.\n// If only relevant to the list plugin, a specific util should be created\n// that plugin instead.\nexport function setTagName(el, newTagName) {\n    const document = el.ownerDocument;\n    if (el.tagName === newTagName) {\n        return el;\n    }\n    const newEl = document.createElement(newTagName);\n    while (el.firstChild) {\n        newEl.append(el.firstChild);\n    }\n    if (el.tagName === \"LI\") {\n        el.append(newEl);\n    } else {\n        for (const attribute of el.attributes) {\n            newEl.setAttribute(attribute.name, attribute.value);\n        }\n        el.parentNode.replaceChild(newEl, el);\n    }\n    return newEl;\n}\n\n/**\n * Removes the specified class names from the given element.  If the element has\n * no more class names after removal, the \"class\" attribute is removed.\n *\n * @param {Element} element - The element from which to remove the class names.\n * @param {...string} classNames - The class names to be removed.\n */\nexport function removeClass(element, ...classNames) {\n    element.classList.remove(...classNames);\n    if (!element.classList.length) {\n        element.removeAttribute(\"class\");\n    }\n}\n\n/**\n * Add a BR in the given node if its closest ancestor block has nothing to make\n * it visible, and/or add a zero-width space in the given node if it's an empty\n * inline so the cursor can stay in it.\n *\n * @param {HTMLElement} el\n * @returns {Object} { br: the inserted <br> if any,\n *                     zws: the inserted zero-width space if any }\n */\nexport function fillEmpty(el) {\n    const document = el.ownerDocument;\n    const fillers = { ...fillShrunkPhrasingParent(el) };\n    if (!isBlock(el) && !isVisible(el) && !el.hasAttribute(\"data-oe-zws-empty-inline\")) {\n        const zws = document.createTextNode(\"\\u200B\");\n        el.appendChild(zws);\n        el.setAttribute(\"data-oe-zws-empty-inline\", \"\");\n        fillers.zws = zws;\n        const previousSibling = el.previousSibling;\n        if (previousSibling && previousSibling.nodeName === \"BR\") {\n            previousSibling.remove();\n        }\n    }\n    return fillers;\n}\n\n/**\n * Add a BR in a shrunk phrasing parent to make it visible.\n * A shrunk block is assumed to be a phrasing parent, and the inserted\n * <br> must be wrapped in a paragraph by the caller if necessary.\n *\n * @param {HTMLElement} el\n * @returns {Object} { br: the inserted <br> if any }\n */\nexport function fillShrunkPhrasingParent(el) {\n    const document = el.ownerDocument;\n    const fillers = {};\n    const blockEl = closestBlock(el);\n    if (isShrunkBlock(blockEl)) {\n        const br = document.createElement(\"br\");\n        blockEl.appendChild(br);\n        fillers.br = br;\n    }\n    return fillers;\n}\n\n/**\n * Removes a trailing BR if it is unnecessary:\n * in a non-empty block, if the last childNode is a BR and its previous sibling\n * is not a BR, remove the BR.\n *\n * @param {HTMLElement} el\n * @returns {HTMLElement|undefined} the removed br, if any\n */\nexport function cleanTrailingBR(el) {\n    const candidate = el?.lastChild;\n    if (\n        candidate?.nodeName === \"BR\" &&\n        candidate.previousSibling?.nodeName !== \"BR\" &&\n        !isEmptyBlock(el)\n    ) {\n        candidate.remove();\n        return candidate;\n    }\n}\n\nexport function toggleClass(node, className) {\n    node.classList.toggle(className);\n    if (!node.className) {\n        node.removeAttribute(\"class\");\n    }\n}\n\n/**\n * Remove all occurrences of a character from a text node and optionally update\n * cursors for later selection restore.\n *\n * @param {Node} node text node\n * @param {String} char character to remove (string of length 1)\n * @param {Cursors} [cursors]\n */\nexport function cleanTextNode(node, char, cursors) {\n    const removedIndexes = [];\n    node.textContent = node.textContent.replaceAll(char, (_, offset) => {\n        removedIndexes.push(offset);\n        return \"\";\n    });\n    cursors?.update((cursor) => {\n        if (cursor.node === node) {\n            cursor.offset -= removedIndexes.filter((index) => cursor.offset > index).length;\n        }\n    });\n}\n\n/**\n * Splits a text node in two parts.\n * If the split occurs at the beginning or the end, the text node stays\n * untouched and unsplit. If a split actually occurs, the original text node\n * still exists and become the right part of the split.\n *\n * Note: if split after or before whitespace, that whitespace may become\n * invisible, it is up to the caller to replace it by nbsp if needed.\n *\n * @param {Text} textNode\n * @param {number} offset\n * @param {boolean} originalNodeSide Whether the original node ends up on left\n * or right after the split\n * @returns {number} The parentOffset if the cursor was between the two text\n *          node parts after the split.\n */\nexport function splitTextNode(textNode, offset, originalNodeSide = DIRECTIONS.RIGHT) {\n    const document = textNode.ownerDocument;\n    let parentOffset = childNodeIndex(textNode);\n\n    if (offset > 0) {\n        parentOffset++;\n\n        if (offset < textNode.length) {\n            const left = textNode.nodeValue.substring(0, offset);\n            const right = textNode.nodeValue.substring(offset);\n            if (originalNodeSide === DIRECTIONS.LEFT) {\n                const newTextNode = document.createTextNode(right);\n                textNode.after(newTextNode);\n                textNode.nodeValue = left;\n            } else {\n                const newTextNode = document.createTextNode(left);\n                textNode.before(newTextNode);\n                textNode.nodeValue = right;\n            }\n        }\n    }\n    return parentOffset;\n}\n", "import { closestBlock, isBlock } from \"./blocks\";\nimport { closestElement, firstLeaf, lastLeaf } from \"./dom_traversal\";\nimport { DIRECTIONS, nodeSize } from \"./position\";\n\nexport function isEmpty(el) {\n    if (isProtecting(el) || isProtected(el)) {\n        return false;\n    }\n    const content = el.innerHTML.trim();\n    if (content === \"\" || content === \"<br>\") {\n        return true;\n    }\n    return false;\n}\n\n/**\n * Return true if the given node appears bold. The node is considered to appear\n * bold if its font weight is bigger than 500 (eg.: Heading 1), or if its font\n * weight is bigger than that of its closest block.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isBold(node) {\n    const fontWeight = +getComputedStyle(closestElement(node)).fontWeight;\n    return fontWeight > 500 || fontWeight > +getComputedStyle(closestBlock(node)).fontWeight;\n}\n\n/**\n * Return true if the given node appears italic.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isItalic(node) {\n    return getComputedStyle(closestElement(node)).fontStyle === \"italic\";\n}\n\n/**\n * Return true if the given node appears underlined.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isUnderline(node) {\n    let parent = closestElement(node);\n    while (parent) {\n        if (getComputedStyle(parent).textDecorationLine.includes(\"underline\")) {\n            return true;\n        }\n        parent = parent.parentElement;\n    }\n    return false;\n}\n\n/**\n * Return true if the given node appears struck through.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isStrikeThrough(node) {\n    let parent = closestElement(node);\n    while (parent) {\n        if (getComputedStyle(parent).textDecorationLine.includes(\"line-through\")) {\n            return true;\n        }\n        parent = parent.parentElement;\n    }\n    return false;\n}\n\n/**\n * Return true if the given node font-size is equal to `props.size`.\n *\n * @param {Object} props\n * @param {Node} props.node A node to compare the font-size against.\n * @param {String} props.size The font-size value of the node that will be\n *     checked against.\n * @returns {boolean}\n */\nexport function isFontSize(node, props) {\n    const element = closestElement(node);\n    return getComputedStyle(element)[\"font-size\"] === props.size;\n}\n\n/**\n * Return true if the given node classlist contains `props.className`.\n *\n * @param {Object} props\n * @param {Node} node A node to compare the font-size against.\n * @param {String} props.className The name of the class.\n * @returns {boolean}\n */\nexport function hasClass(node, props) {\n    const element = closestElement(node);\n    return element.classList.contains(props.className);\n}\n\n/**\n * Return true if the given node appears in a different direction than that of\n * the editable ('ltr' or 'rtl').\n *\n * Note: The direction of the editable is set on its \"dir\" attribute, to the\n * value of the \"direction\" option on instantiation of the editor.\n *\n * @param {Node} node\n * @param {Element} editable\n * @returns {boolean}\n */\nexport function isDirectionSwitched(node, editable) {\n    const defaultDirection = editable.getAttribute(\"dir\") || \"ltr\";\n    return getComputedStyle(closestElement(node)).direction !== defaultDirection;\n}\n\n// /**\n//  * Return true if the given node is a row element.\n//  */\nexport function isRow(node) {\n    return [\"TH\", \"TD\"].includes(node.tagName);\n}\n\nexport function isZWS(node) {\n    return node && node.textContent === \"\\u200B\";\n}\n\n/**\n * Returns true if the given node is in a PRE context for whitespace handling.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isInPre(node) {\n    const element = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;\n    return (\n        !!element &&\n        (!!element.closest(\"pre\") ||\n            getComputedStyle(element).getPropertyValue(\"white-space\") === \"pre\")\n    );\n}\n\nexport const ZERO_WIDTH_CHARS = [\"\\u200b\", \"\\ufeff\"];\n\nexport const whitespace = `[^\\\\S\\\\u00A0\\\\u0009\\\\ufeff]`; // for formatting (no \"real\" content) (TODO: 0009 shouldn't be included)\nconst whitespaceRegex = new RegExp(`^${whitespace}*$`);\nexport function isWhitespace(value) {\n    const str = typeof value === \"string\" ? value : value.nodeValue;\n    return whitespaceRegex.test(str);\n}\n\n// eslint-disable-next-line no-control-regex\nconst visibleCharRegex = /[^\\s\\u200b]|[\\u00A0\\u0009]$/; // contains at least a char that is always visible (TODO: 0009 shouldn't be included)\nexport function isVisibleTextNode(testedNode) {\n    if (!testedNode || !testedNode.length || testedNode.nodeType !== Node.TEXT_NODE) {\n        return false;\n    }\n    if (isProtected(testedNode)) {\n        return true;\n    }\n    if (\n        visibleCharRegex.test(testedNode.textContent) ||\n        (isInPre(testedNode) && isWhitespace(testedNode))\n    ) {\n        return true;\n    }\n    if (ZERO_WIDTH_CHARS.includes(testedNode.textContent)) {\n        return false; // a ZW(NB)SP is always invisible, regardless of context.\n    }\n    // The following assumes node is made entirely of whitespace and is not\n    // preceded of followed by a block.\n    // Find out contiguous preceding and following text nodes\n    let preceding;\n    let following;\n    // Control variable to know whether the current node has been found\n    let foundTestedNode;\n    const currentNodeParentBlock = closestBlock(testedNode);\n    if (!currentNodeParentBlock) {\n        return false;\n    }\n    const nodeIterator = document.createNodeIterator(currentNodeParentBlock);\n    for (let node = nodeIterator.nextNode(); node; node = nodeIterator.nextNode()) {\n        if (node.nodeType === Node.TEXT_NODE) {\n            // If we already found the tested node, the current node is the\n            // contiguous following, and we can stop looping\n            // If the current node is the tested node, mark it as found and\n            // continue.\n            // If we haven't reached the tested node, overwrite the preceding\n            // node.\n            if (foundTestedNode) {\n                following = node;\n                break;\n            } else if (testedNode === node) {\n                foundTestedNode = true;\n            } else {\n                preceding = node;\n            }\n        } else if (isBlock(node)) {\n            // If we found the tested node, then the following node is irrelevant\n            // If we didn't, then the current preceding node is irrelevant\n            if (foundTestedNode) {\n                break;\n            } else {\n                preceding = null;\n            }\n        } else if (foundTestedNode && !isWhitespace(node)) {\n            // <block>space<inline>text</inline></block> -> space is visible\n            following = node;\n            break;\n        }\n    }\n    while (following && !visibleCharRegex.test(following.textContent)) {\n        following = following.nextSibling;\n    }\n    // Missing preceding or following: invisible.\n    // Preceding or following not in the same block as tested node: invisible.\n    if (\n        !(preceding && following) ||\n        currentNodeParentBlock !== closestBlock(preceding) ||\n        currentNodeParentBlock !== closestBlock(following)\n    ) {\n        return false;\n    }\n    // Preceding is whitespace or following is whitespace: invisible\n    return visibleCharRegex.test(preceding.textContent);\n}\n\n/**\n * Returns whether the given node is a element that could be considered to be\n * removed by itself = self closing tags.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nconst selfClosingElementTags = [\"BR\", \"IMG\", \"INPUT\", \"T\", \"HR\"];\nexport function isSelfClosingElement(node) {\n    return node && selfClosingElementTags.includes(node.nodeName);\n}\n\n/**\n * Returns whether removing the given node from the DOM will have a visible\n * effect or not.\n *\n * Note: TODO this is not handling all cases right now, just the ones the\n * caller needs at the moment. For example a space text node between two inlines\n * will always return 'true' while it is sometimes invisible.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isVisible(node) {\n    return (\n        !!node &&\n        ((node.nodeType === Node.TEXT_NODE && isVisibleTextNode(node)) ||\n            isSelfClosingElement(node) ||\n            // @todo: handle it in resources?\n            isMediaElement(node) ||\n            hasVisibleContent(node) ||\n            isProtecting(node))\n    );\n}\nexport function hasVisibleContent(node) {\n    return [...(node?.childNodes || [])].some((n) => isVisible(n));\n}\n\nexport function isZwnbsp(node) {\n    return node?.nodeType === Node.TEXT_NODE && node.textContent === \"\\ufeff\";\n}\n\nexport function isTangible(node) {\n    return isVisible(node) || isZwnbsp(node) || hasTangibleContent(node);\n}\n\nexport function hasTangibleContent(node) {\n    return [...(node?.childNodes || [])].some((n) => isTangible(n));\n}\n\nexport const isNotEditableNode = (node) =>\n    node.getAttribute &&\n    node.getAttribute(\"contenteditable\") &&\n    node.getAttribute(\"contenteditable\").toLowerCase() === \"false\";\n\nconst iconTags = [\"I\", \"SPAN\"];\n// @todo @phoenix: move the specific part in a proper plugin.\nconst iconClasses = [\"fa\", \"fab\", \"fad\", \"far\", \"oi\"];\n\nexport const ICON_SELECTOR = iconTags\n    .map((tag) => {\n        return iconClasses\n            .map((cls) => {\n                return `${tag}.${cls}`;\n            })\n            .join(\", \");\n    })\n    .join(\", \");\n\n/**\n * Indicates if the given node is an icon element.\n *\n * @see ICON_SELECTOR\n * @param {?Node} [node]\n * @returns {boolean}\n */\nexport function isIconElement(node) {\n    return !!(\n        node &&\n        iconTags.includes(node.nodeName) &&\n        iconClasses.some((cls) => node.classList.contains(cls))\n    );\n}\n// @todo @phoenix: move the specific part in a proper plugin.\nexport function isMediaElement(node) {\n    return (\n        isIconElement(node) ||\n        (node.classList &&\n            (node.classList.contains(\"o_image\") || node.classList.contains(\"media_iframe_video\")))\n    );\n}\n\n// See https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#phrasing_content\nconst phrasingTagNames = new Set([\n    \"ABBR\",\n    \"AUDIO\",\n    \"B\",\n    \"BDI\",\n    \"BDO\",\n    \"BR\",\n    \"BUTTON\",\n    \"CANVAS\",\n    \"CITE\",\n    \"CODE\",\n    \"DATA\",\n    \"DATALIST\",\n    \"DFN\",\n    \"EM\",\n    \"EMBED\",\n    \"I\",\n    \"IFRAME\",\n    \"IMG\",\n    \"INPUT\",\n    \"KBD\",\n    \"LABEL\",\n    \"MARK\",\n    \"MATH\",\n    \"METER\",\n    \"NOSCRIPT\",\n    \"OBJECT\",\n    \"OUTPUT\",\n    \"PICTURE\",\n    \"PROGRESS\",\n    \"Q\",\n    \"RUBY\",\n    \"S\",\n    \"SAMP\",\n    \"SCRIPT\",\n    \"SELECT\",\n    \"SLOT\",\n    \"SMALL\",\n    \"SPAN\",\n    \"STRONG\",\n    \"SUB\",\n    \"SUP\",\n    \"SVG\",\n    \"TEMPLATE\",\n    \"TEXTAREA\",\n    \"TIME\",\n    \"U\",\n    \"VAR\",\n    \"VIDEO\",\n    \"WBR\",\n    \"FONT\", // TODO @phoenix: font is deprecated, replace usage\n    // The following elements are phrasing content under specific conditions,\n    // evaluate if those conditions are applicable when using this set.\n    \"A\",\n    \"AREA\",\n    \"DEL\",\n    \"INS\",\n    \"LINK\",\n    \"MAP\",\n    \"META\",\n]);\n\nexport function isPhrasingContent(node) {\n    if (\n        node &&\n        (node.nodeType === Node.TEXT_NODE ||\n            (node.nodeType === Node.ELEMENT_NODE && phrasingTagNames.has(node.tagName)))\n    ) {\n        return true;\n    }\n    return false;\n}\n\n/**\n * A \"protected\" node will have its mutations filtered and not be registered\n * in an history step. Some editor features like selection handling, command\n * hint, toolbar, tooltip, etc. are also disabled. Protected roots have their\n * data-oe-protected attribute set to either \"\" or \"true\". If the closest parent\n * with a data-oe-protected attribute has the value \"false\", it is not\n * protected. Unknown values are ignored.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isProtected(node) {\n    if (!node) {\n        return false;\n    }\n    const candidate = node.parentElement\n        ? closestElement(node.parentElement, \"[data-oe-protected]\")\n        : null;\n    if (!candidate || candidate.dataset.oeProtected === \"false\") {\n        return false;\n    }\n    return true;\n}\n\n/**\n * A \"protecting\" element contains childNodes that are protected.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isProtecting(node) {\n    if (!node) {\n        return false;\n    }\n    return (\n        node.nodeType === Node.ELEMENT_NODE &&\n        node.dataset.oeProtected !== \"false\" &&\n        node.dataset.oeProtected !== undefined\n    );\n}\n\nexport function isUnprotecting(node) {\n    if (!node) {\n        return false;\n    }\n    return node.nodeType === Node.ELEMENT_NODE && node.dataset.oeProtected === \"false\";\n}\n\n// This is a list of \"paragraph-related elements\", defined as elements that\n// behave like paragraphs.\nexport const paragraphRelatedElements = [\n    \"P\",\n    \"H1\",\n    \"H2\",\n    \"H3\",\n    \"H4\",\n    \"H5\",\n    \"H6\",\n    \"PRE\",\n    \"BLOCKQUOTE\",\n];\n\n/**\n * Return true if the given node allows \"paragraph-related elements\".\n *\n * @see paragraphRelatedElements\n * @param {Node} node\n * @returns {boolean}\n */\nexport function allowsParagraphRelatedElements(node) {\n    return isBlock(node) && ![\"P\", \"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\"].includes(node.nodeName);\n}\n\nexport const phrasingContent = new Set([\"#text\", ...phrasingTagNames]);\nconst flowContent = new Set([...phrasingContent, ...paragraphRelatedElements, \"DIV\", \"HR\"]);\nexport const listItem = new Set([\"LI\"]);\n\nconst allowedContent = {\n    BLOCKQUOTE: phrasingContent, // HTML spec: flow content\n    DIV: flowContent,\n    H1: phrasingContent,\n    H2: phrasingContent,\n    H3: phrasingContent,\n    H4: phrasingContent,\n    H5: phrasingContent,\n    H6: phrasingContent,\n    HR: new Set(),\n    LI: flowContent,\n    OL: listItem,\n    UL: listItem,\n    P: phrasingContent,\n    PRE: phrasingContent,\n    TD: flowContent,\n    TR: new Set([\"TD\"]),\n};\n\n/**\n * @param {Element} parentBlock\n * @param {Node[]} nodes\n * @returns {boolean}\n */\nexport function isAllowedContent(parentBlock, nodes) {\n    const allowedContentSet = allowedContent[parentBlock.nodeName];\n    if (!allowedContentSet) {\n        // Spec: a block not listed in allowedContent allows anything.\n        // See \"custom-block\" in tests.\n        return true;\n    }\n    return nodes.every((node) => allowedContentSet.has(node.nodeName));\n}\n\n/**\n * Checks whether or not the given block has any visible content, except for\n * a placeholder BR.\n *\n * @param {HTMLElement} blockEl\n * @returns {boolean}\n */\nexport function isEmptyBlock(blockEl) {\n    if (!blockEl || blockEl.nodeType !== Node.ELEMENT_NODE) {\n        return false;\n    }\n    if (visibleCharRegex.test(blockEl.textContent)) {\n        return false;\n    }\n    if (blockEl.querySelectorAll(\"br\").length >= 2) {\n        return false;\n    }\n    if (isProtecting(blockEl) || isProtected(blockEl)) {\n        // Protecting nodes should never be considered empty for editor\n        // operations, as their content is a \"black box\". Their content should\n        // be managed by a specialized plugin.\n        return false;\n    }\n    const nodes = blockEl.querySelectorAll(\"*\");\n    for (const node of nodes) {\n        // There is no text and no double BR, the only thing that could make\n        // this visible is a \"visible empty\" node like an image.\n        if (\n            node.nodeName != \"BR\" &&\n            (isSelfClosingElement(node) || isMediaElement(node) || isProtecting(node))\n        ) {\n            return false;\n        }\n    }\n    return isBlock(blockEl);\n}\n/**\n * Checks whether or not the given block element has something to make it have\n * a visible height (except for padding / border).\n *\n * @param {HTMLElement} blockEl\n * @returns {boolean}\n */\nexport function isShrunkBlock(blockEl) {\n    return isEmptyBlock(blockEl) && !blockEl.querySelector(\"br\") && !isSelfClosingElement(blockEl);\n}\n\nexport function isEditorTab(node) {\n    return node && node.nodeName === \"SPAN\" && node.classList.contains(\"oe-tabs\");\n}\n\nexport function getDeepestPosition(node, offset) {\n    let direction = DIRECTIONS.RIGHT;\n    let next = node;\n    while (next) {\n        if (isTangible(next) || (isZWS(next) && isContentEditable(next))) {\n            // Valid node: update position then try to go deeper.\n            if (next !== node) {\n                [node, offset] = [next, direction ? 0 : nodeSize(next)];\n            }\n            // First switch direction to left if offset is at the end.\n            direction = offset < node.childNodes.length;\n            next = node.childNodes[direction ? offset : offset - 1];\n        } else if (direction && next.nextSibling && closestBlock(node).contains(next.nextSibling)) {\n            // Invalid node: skip to next sibling (without crossing blocks).\n            next = next.nextSibling;\n        } else {\n            // Invalid node: skip to previous sibling (without crossing blocks).\n            direction = DIRECTIONS.LEFT;\n            next = closestBlock(node).contains(next.previousSibling) && next.previousSibling;\n        }\n        // Avoid too-deep ranges inside self-closing elements like [BR, 0].\n        next = !isSelfClosingElement(next) && next;\n    }\n    return [node, offset];\n}\n\nexport function previousLeaf(node, editable, skipInvisible = false) {\n    let ancestor = node;\n    while (ancestor && !ancestor.previousSibling && ancestor !== editable) {\n        ancestor = ancestor.parentElement;\n    }\n    if (ancestor && ancestor !== editable) {\n        if (skipInvisible && !isVisible(ancestor.previousSibling)) {\n            return previousLeaf(ancestor.previousSibling, editable, skipInvisible);\n        } else {\n            const last = lastLeaf(ancestor.previousSibling);\n            if (skipInvisible && !isVisible(last)) {\n                return previousLeaf(last, editable, skipInvisible);\n            } else {\n                return last;\n            }\n        }\n    }\n}\nexport function nextLeaf(node, editable, skipInvisible = false) {\n    let ancestor = node;\n    while (ancestor && !ancestor.nextSibling && ancestor !== editable) {\n        ancestor = ancestor.parentElement;\n    }\n    if (ancestor && ancestor !== editable) {\n        if (skipInvisible && ancestor.nextSibling && !isVisible(ancestor.nextSibling)) {\n            return nextLeaf(ancestor.nextSibling, editable, skipInvisible);\n        } else {\n            const first = firstLeaf(ancestor.nextSibling);\n            if (skipInvisible && !isVisible(first)) {\n                return nextLeaf(first, editable, skipInvisible);\n            } else {\n                return first;\n            }\n        }\n    }\n}\n\nfunction hasPseudoElementContent(node, pseudoSelector) {\n    const content = getComputedStyle(node, pseudoSelector).getPropertyValue(\"content\");\n    return content && content !== \"none\";\n}\n\nconst NOT_A_NUMBER = /[^\\d]/g;\n\nexport function areSimilarElements(node, node2) {\n    if (![node, node2].every((n) => n?.nodeType === Node.ELEMENT_NODE)) {\n        return false; // The nodes don't both exist or aren't both elements.\n    }\n    if (node.nodeName !== node2.nodeName) {\n        return false; // The nodes aren't the same type of element.\n    }\n    for (const name of new Set([...node.getAttributeNames(), ...node2.getAttributeNames()])) {\n        if (node.getAttribute(name) !== node2.getAttribute(name)) {\n            return false; // The nodes don't have the same attributes.\n        }\n    }\n    if (\n        [node, node2].some(\n            (n) => hasPseudoElementContent(n, \":before\") || hasPseudoElementContent(n, \":after\")\n        )\n    ) {\n        return false; // The nodes have pseudo elements with content.\n    }\n    if (isBlock(node)) {\n        return false;\n    }\n    const nodeStyle = getComputedStyle(node);\n    const node2Style = getComputedStyle(node2);\n    return (\n        !+nodeStyle.padding.replace(NOT_A_NUMBER, \"\") &&\n        !+node2Style.padding.replace(NOT_A_NUMBER, \"\") &&\n        !+nodeStyle.margin.replace(NOT_A_NUMBER, \"\") &&\n        !+node2Style.margin.replace(NOT_A_NUMBER, \"\")\n    );\n}\n\nexport function isTextNode(node) {\n    return node.nodeType === Node.TEXT_NODE;\n}\n\nexport function isElement(node) {\n    return node.nodeType === Node.ELEMENT_NODE;\n}\n\nexport function isContentEditable(node) {\n    const element = isTextNode(node) ? node.parentElement : node;\n    return element.isContentEditable;\n}\n", "import { isBlock } from \"./blocks\";\nimport { CTGROUPS, CTYPES, ctypeToString } from \"./content_types\";\nimport { isInPre, isVisible, isWhitespace, whitespace } from \"./dom_info\";\nimport {\n    PATH_END_REASONS,\n    ancestors,\n    closestElement,\n    closestPath,\n    createDOMPathGenerator,\n} from \"./dom_traversal\";\nimport { DIRECTIONS, leftPos, rightPos } from \"./position\";\n\nconst prepareUpdateLockedEditables = new Set();\n/**\n * Any editor command is applied to a selection (collapsed or not). After the\n * command, the content type on the selection boundaries, in both direction,\n * should be preserved (some whitespace should disappear as went from collapsed\n * to non collapsed, or converted to &nbsp; as went from non collapsed to\n * collapsed, there also <br> to remove/duplicate, etc).\n *\n * This function returns a callback which allows to do that after the command\n * has been done.\n *\n * Note: the method has been made generic enough to work with non-collapsed\n * selection but can be used for an unique cursor position.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {...(HTMLElement|number)} args - argument 1 and 2 can be repeated for\n *     multiple preparations with only one restore callback returned. Note: in\n *     that case, the positions should be given in the document node order.\n * @param {Object} [options]\n * @param {boolean} [options.allowReenter = true] - if false, all calls to\n *     prepareUpdate before this one gets restored will be ignored.\n * @param {string} [options.label = <random 6 character string>]\n * @param {boolean} [options.debug = false] - if true, adds nicely formatted\n *     console logs to help with debugging.\n * @returns {function}\n */\nexport function prepareUpdate(...args) {\n    const closestRoot =\n        args.length &&\n        ancestors(args[0]).find((ancestor) => ancestor.classList.contains(\"odoo-editor-editable\"));\n    const isPrepareUpdateLocked = closestRoot && prepareUpdateLockedEditables.has(closestRoot);\n    const hash = (Math.random() + 1).toString(36).substring(7);\n    const options = {\n        allowReenter: true,\n        label: hash,\n        debug: false,\n        ...(args.length && args[args.length - 1] instanceof Object ? args.pop() : {}),\n    };\n    if (options.debug) {\n        console.log(\n            \"%cPreparing%c update: \" +\n                options.label +\n                (options.label === hash ? \"\" : ` (${hash})`) +\n                \"%c\" +\n                (isPrepareUpdateLocked ? \" LOCKED\" : \"\"),\n            \"color: cyan;\",\n            \"color: white;\",\n            \"color: red; font-weight: bold;\"\n        );\n    }\n    if (isPrepareUpdateLocked) {\n        return () => {\n            if (options.debug) {\n                console.log(\n                    \"%cRestoring%c update: \" +\n                        options.label +\n                        (options.label === hash ? \"\" : ` (${hash})`) +\n                        \"%c LOCKED\",\n                    \"color: lightgreen;\",\n                    \"color: white;\",\n                    \"color: red; font-weight: bold;\"\n                );\n            }\n        };\n    }\n    if (!options.allowReenter && closestRoot) {\n        prepareUpdateLockedEditables.add(closestRoot);\n    }\n    const positions = [...args];\n\n    // Check the state in each direction starting from each position.\n    const restoreData = [];\n    let el, offset;\n    while (positions.length) {\n        // Note: important to get the positions in reverse order to restore\n        // right side before left side.\n        offset = positions.pop();\n        el = positions.pop();\n        const left = getState(el, offset, DIRECTIONS.LEFT);\n        const right = getState(el, offset, DIRECTIONS.RIGHT, left.cType);\n        if (options.debug) {\n            const editable = el && closestElement(el, \".odoo-editor-editable\");\n            const oldEditableHTML =\n                (editable && editable.innerHTML.replaceAll(\" \", \"_\").replaceAll(\"\\u200B\", \"ZWS\")) ||\n                \"\";\n            left.oldEditableHTML = oldEditableHTML;\n            right.oldEditableHTML = oldEditableHTML;\n        }\n        restoreData.push(left, right);\n    }\n\n    // Create the callback that will be able to restore the state in each\n    // direction wherever the node in the opposite direction has landed.\n    return function restoreStates() {\n        if (options.debug) {\n            console.log(\n                \"%cRestoring%c update: \" +\n                    options.label +\n                    (options.label === hash ? \"\" : ` (${hash})`),\n                \"color: lightgreen;\",\n                \"color: white;\"\n            );\n        }\n        for (const data of restoreData) {\n            restoreState(data, options.debug);\n        }\n        if (!options.allowReenter && closestRoot) {\n            prepareUpdateLockedEditables.delete(closestRoot);\n        }\n    };\n}\n\nexport const leftLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.LEFT, {\n    leafOnly: true,\n    stopTraverseFunction: isBlock,\n    stopFunction: isBlock,\n});\n\nconst rightLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    stopTraverseFunction: isBlock,\n    stopFunction: isBlock,\n});\n\n/**\n * Retrieves the \"state\" from a given position looking at the given direction.\n * The \"state\" is the type of content. The functions also returns the first\n * meaninful node looking in the opposite direction = the first node we trust\n * will not disappear if a command is played in the given direction.\n *\n * Note: only work for in-between nodes positions. If the position is inside a\n * text node, first split it @see splitTextNode.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {boolean} direction @see DIRECTIONS.LEFT @see DIRECTIONS.RIGHT\n * @param {CTYPES} [leftCType]\n * @returns {Object}\n */\nexport function getState(el, offset, direction, leftCType) {\n    const leftDOMPath = leftLeafOnlyNotBlockPath;\n    const rightDOMPath = rightLeafOnlyNotBlockPath;\n\n    let domPath;\n    let inverseDOMPath;\n    const whitespaceAtStartRegex = new RegExp(\"^\" + whitespace + \"+\");\n    const whitespaceAtEndRegex = new RegExp(whitespace + \"+$\");\n    const reasons = [];\n    if (direction === DIRECTIONS.LEFT) {\n        domPath = leftDOMPath(el, offset, reasons);\n        inverseDOMPath = rightDOMPath(el, offset);\n    } else {\n        domPath = rightDOMPath(el, offset, reasons);\n        inverseDOMPath = leftDOMPath(el, offset);\n    }\n\n    // TODO I think sometimes, the node we have to consider as the\n    // anchor point to restore the state is not the first one of the inverse\n    // path (like for example, empty text nodes that may disappear\n    // after the command so we would not want to get those ones).\n    const boundaryNode = inverseDOMPath.next().value;\n\n    // We only traverse through deep inline nodes. If we cannot find a\n    // meanfingful state between them, that means we hit a block.\n    let cType = undefined;\n\n    // Traverse the DOM in the given direction to check what type of content\n    // there is.\n    let lastSpace = null;\n    for (const node of domPath) {\n        if (node.nodeType === Node.TEXT_NODE) {\n            const value = node.nodeValue;\n            // If we hit a text node, the state depends on the path direction:\n            // any space encountered backwards is a visible space if we hit\n            // visible content afterwards. If going forward, spaces are only\n            // visible if we have content backwards.\n            if (direction === DIRECTIONS.LEFT) {\n                if (!isWhitespace(value)) {\n                    if (lastSpace) {\n                        cType = CTYPES.SPACE;\n                    } else {\n                        const rightLeaf = rightLeafOnlyNotBlockPath(node).next().value;\n                        const hasContentRight =\n                            rightLeaf && !whitespaceAtStartRegex.test(rightLeaf.textContent);\n                        cType =\n                            !hasContentRight && whitespaceAtEndRegex.test(node.textContent)\n                                ? CTYPES.SPACE\n                                : CTYPES.CONTENT;\n                    }\n                    break;\n                }\n                if (value.length) {\n                    lastSpace = node;\n                }\n            } else {\n                leftCType = leftCType || getState(el, offset, DIRECTIONS.LEFT).cType;\n                if (whitespaceAtStartRegex.test(value)) {\n                    const leftLeaf = leftLeafOnlyNotBlockPath(node).next().value;\n                    const hasContentLeft =\n                        leftLeaf && !whitespaceAtEndRegex.test(leftLeaf.textContent);\n                    const rct = !isWhitespace(value)\n                        ? CTYPES.CONTENT\n                        : getState(...rightPos(node), DIRECTIONS.RIGHT).cType;\n                    cType =\n                        leftCType & CTYPES.CONTENT &&\n                        rct & (CTYPES.CONTENT | CTYPES.BR) &&\n                        !hasContentLeft\n                            ? CTYPES.SPACE\n                            : rct;\n                    break;\n                }\n                if (!isWhitespace(value)) {\n                    cType = CTYPES.CONTENT;\n                    break;\n                }\n            }\n        } else if (node.nodeName === \"BR\") {\n            cType = CTYPES.BR;\n            break;\n        } else if (isVisible(node)) {\n            // E.g. an image\n            cType = CTYPES.CONTENT;\n            break;\n        }\n    }\n\n    if (cType === undefined) {\n        cType = reasons.includes(PATH_END_REASONS.BLOCK_HIT)\n            ? CTYPES.BLOCK_OUTSIDE\n            : CTYPES.BLOCK_INSIDE;\n    }\n\n    return {\n        node: boundaryNode,\n        direction: direction,\n        cType: cType, // Short for contentType\n    };\n}\nconst priorityRestoreStateRules = [\n    // Each entry is a list of two objects, with each key being optional (the\n    // more key-value pairs, the bigger the priority).\n    // {direction: ..., cType1: ..., cType2: ...}\n    // ->\n    // {spaceVisibility: (false|true), brVisibility: (false|true)}\n    [\n        // Replace a space by &nbsp; when it was not collapsed before and now is\n        // collapsed (one-letter word removal for example).\n        { cType1: CTYPES.CONTENT, cType2: CTYPES.SPACE | CTGROUPS.BLOCK },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was content before and now it is\n        // a BR.\n        { direction: DIRECTIONS.LEFT, cType1: CTGROUPS.INLINE, cType2: CTGROUPS.BR },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was content before and now it is\n        // a BR (removal of last character before a BR for example).\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.CONTENT, cType2: CTGROUPS.BR },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was visible thanks to a BR which\n        // is now gone.\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.BR, cType2: CTYPES.SPACE | CTGROUPS.BLOCK },\n        { spaceVisibility: true },\n    ],\n    [\n        // Remove all collapsed spaces when a space is removed.\n        { cType1: CTYPES.SPACE },\n        { spaceVisibility: false },\n    ],\n    [\n        // Remove spaces once the preceeding BR is removed\n        { direction: DIRECTIONS.LEFT, cType1: CTGROUPS.BR },\n        { spaceVisibility: false },\n    ],\n    [\n        // Remove space before block once content is put after it (otherwise it\n        // would become visible).\n        { cType1: CTGROUPS.BLOCK, cType2: CTGROUPS.INLINE | CTGROUPS.BR },\n        { spaceVisibility: false },\n    ],\n    [\n        // Duplicate a BR once the content afterwards disappears\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.INLINE, cType2: CTGROUPS.BLOCK },\n        { brVisibility: true },\n    ],\n    [\n        // Remove a BR at the end of a block once inline content is put after\n        // it (otherwise it would act as a line break).\n        {\n            direction: DIRECTIONS.RIGHT,\n            cType1: CTGROUPS.BLOCK,\n            cType2: CTGROUPS.INLINE | CTGROUPS.BR,\n        },\n        { brVisibility: false },\n    ],\n    [\n        // Remove a BR once the BR that preceeds it is now replaced by\n        // content (or if it was a BR at the start of a block which now is\n        // a trailing BR).\n        {\n            direction: DIRECTIONS.LEFT,\n            cType1: CTGROUPS.BR | CTGROUPS.BLOCK,\n            cType2: CTGROUPS.INLINE,\n        },\n        { brVisibility: false, extraBRRemovalCondition: (brNode) => isFakeLineBreak(brNode) },\n    ],\n];\nfunction restoreStateRuleHashCode(direction, cType1, cType2) {\n    return `${direction}-${cType1}-${cType2}`;\n}\nconst allRestoreStateRules = (function () {\n    const map = new Map();\n\n    const keys = [\"direction\", \"cType1\", \"cType2\"];\n    for (const direction of Object.values(DIRECTIONS)) {\n        for (const cType1 of Object.values(CTYPES)) {\n            for (const cType2 of Object.values(CTYPES)) {\n                const rule = { direction: direction, cType1: cType1, cType2: cType2 };\n\n                // Search for the rules which match whatever their priority\n                const matchedRules = [];\n                for (const entry of priorityRestoreStateRules) {\n                    let priority = 0;\n                    for (const key of keys) {\n                        const entryKeyValue = entry[0][key];\n                        if (entryKeyValue !== undefined) {\n                            if (\n                                typeof entryKeyValue === \"boolean\"\n                                    ? rule[key] === entryKeyValue\n                                    : rule[key] & entryKeyValue\n                            ) {\n                                priority++;\n                            } else {\n                                priority = -1;\n                                break;\n                            }\n                        }\n                    }\n                    if (priority >= 0) {\n                        matchedRules.push([priority, entry[1]]);\n                    }\n                }\n\n                // Create the final rule by merging found rules by order of\n                // priority\n                const finalRule = {};\n                for (let p = 0; p <= keys.length; p++) {\n                    for (const entry of matchedRules) {\n                        if (entry[0] === p) {\n                            Object.assign(finalRule, entry[1]);\n                        }\n                    }\n                }\n\n                // Create an unique identifier for the set of values\n                // direction - state 1 - state2 to add the rule in the map\n                const hashCode = restoreStateRuleHashCode(direction, cType1, cType2);\n                map.set(hashCode, finalRule);\n            }\n        }\n    }\n\n    return map;\n})();\n/**\n * Restores the given state starting before the given while looking in the given\n * direction.\n *\n * @param {Object} prevStateData @see getState\n * @param {boolean} debug=false - if true, adds nicely formatted\n *     console logs to help with debugging.\n * @returns {Object|undefined} the rule that was applied to restore the state,\n *     if any, for testing purposes.\n */\nexport function restoreState(prevStateData, debug = false) {\n    const { node, direction, cType: cType1, oldEditableHTML } = prevStateData;\n    if (!node || !node.parentNode) {\n        // FIXME sometimes we want to restore the state starting from a node\n        // which has been removed by another restoreState call... Not sure if\n        // it is a problem or not, to investigate.\n        return;\n    }\n    const [el, offset] = direction === DIRECTIONS.LEFT ? leftPos(node) : rightPos(node);\n    const { cType: cType2 } = getState(el, offset, direction);\n\n    /**\n     * Knowing the old state data and the new state data, we know if we have to\n     * do something or not, and what to do.\n     */\n    const ruleHashCode = restoreStateRuleHashCode(direction, cType1, cType2);\n    const rule = allRestoreStateRules.get(ruleHashCode);\n    if (debug) {\n        const editable = closestElement(node, \".odoo-editor-editable\");\n        console.log(\n            \"%c\" +\n                node.textContent.replaceAll(\" \", \"_\").replaceAll(\"\\u200B\", \"ZWS\") +\n                \"\\n\" +\n                \"%c\" +\n                (direction === DIRECTIONS.LEFT ? \"left\" : \"right\") +\n                \"\\n\" +\n                \"%c\" +\n                ctypeToString(cType1) +\n                \"\\n\" +\n                \"%c\" +\n                ctypeToString(cType2) +\n                \"\\n\" +\n                \"%c\" +\n                \"BEFORE: \" +\n                (oldEditableHTML || \"(unavailable)\") +\n                \"\\n\" +\n                \"%c\" +\n                \"AFTER:  \" +\n                (editable\n                    ? editable.innerHTML.replaceAll(\" \", \"_\").replaceAll(\"\\u200B\", \"ZWS\")\n                    : \"(unavailable)\") +\n                \"\\n\",\n            \"color: white; display: block; width: 100%;\",\n            \"color: \" +\n                (direction === DIRECTIONS.LEFT ? \"magenta\" : \"lightgreen\") +\n                \"; display: block; width: 100%;\",\n            \"color: pink; display: block; width: 100%;\",\n            \"color: lightblue; display: block; width: 100%;\",\n            \"color: white; display: block; width: 100%;\",\n            \"color: white; display: block; width: 100%;\",\n            rule\n        );\n    }\n    if (Object.values(rule).filter((x) => x !== undefined).length) {\n        const inverseDirection = direction === DIRECTIONS.LEFT ? DIRECTIONS.RIGHT : DIRECTIONS.LEFT;\n        enforceWhitespace(el, offset, inverseDirection, rule);\n    }\n    return rule;\n}\n\n/**\n * Returns whether or not the given node is a BR element which does not really\n * act as a line break, but as a placeholder for the cursor or to make some left\n * element (like a space) visible.\n * @todo @phoenix this depends on state, so hard to move it to dom_info\n *\n * @param {HTMLBRElement} brEl\n * @returns {boolean}\n */\nexport function isFakeLineBreak(brEl) {\n    return !(getState(...rightPos(brEl), DIRECTIONS.RIGHT).cType & (CTYPES.CONTENT | CTGROUPS.BR));\n}\n\n/**\n * Enforces the whitespace and BR visibility in the given direction starting\n * from the given position.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {number} direction @see DIRECTIONS.LEFT @see DIRECTIONS.RIGHT\n * @param {Object} rule\n * @param {boolean} [rule.spaceVisibility]\n * @param {boolean} [rule.brVisibility]\n */\nexport function enforceWhitespace(el, offset, direction, rule) {\n    const document = el.ownerDocument;\n    let domPath, whitespaceAtEdgeRegex;\n    if (direction === DIRECTIONS.LEFT) {\n        domPath = leftLeafOnlyNotBlockPath(el, offset);\n        whitespaceAtEdgeRegex = new RegExp(whitespace + \"+$\");\n    } else {\n        domPath = rightLeafOnlyNotBlockPath(el, offset);\n        whitespaceAtEdgeRegex = new RegExp(\"^\" + whitespace + \"+\");\n    }\n\n    const invisibleSpaceTextNodes = [];\n    let foundVisibleSpaceTextNode = null;\n    for (const node of domPath) {\n        if (node.nodeName === \"BR\") {\n            if (rule.brVisibility === undefined) {\n                break;\n            }\n            if (rule.brVisibility) {\n                node.before(document.createElement(\"br\"));\n            } else {\n                if (!rule.extraBRRemovalCondition || rule.extraBRRemovalCondition(node)) {\n                    node.remove();\n                }\n            }\n            break;\n        } else if (node.nodeType === Node.TEXT_NODE && !isInPre(node)) {\n            if (whitespaceAtEdgeRegex.test(node.nodeValue)) {\n                // If we hit spaces going in the direction, either they are in a\n                // visible text node and we have to change the visibility of\n                // those spaces, or it is in an invisible text node. In that\n                // last case, we either remove the spaces if there are spaces in\n                // a visible text node going further in the direction or we\n                // change the visiblity or those spaces.\n                if (!isWhitespace(node)) {\n                    foundVisibleSpaceTextNode = node;\n                    break;\n                } else {\n                    invisibleSpaceTextNodes.push(node);\n                }\n            } else if (!isWhitespace(node)) {\n                break;\n            }\n        } else {\n            break;\n        }\n    }\n\n    if (rule.spaceVisibility === undefined) {\n        return;\n    }\n    if (!rule.spaceVisibility) {\n        for (const node of invisibleSpaceTextNodes) {\n            // Empty and not remove to not mess with offset-based positions in\n            // commands implementation, also remove non-block empty parents.\n            node.nodeValue = \"\";\n            const ancestorPath = closestPath(node.parentNode);\n            let toRemove = null;\n            for (const pNode of ancestorPath) {\n                if (toRemove) {\n                    toRemove.remove();\n                }\n                if (pNode.childNodes.length === 1 && !isBlock(pNode)) {\n                    pNode.after(node);\n                    toRemove = pNode;\n                } else {\n                    break;\n                }\n            }\n        }\n    }\n    const spaceNode = foundVisibleSpaceTextNode || invisibleSpaceTextNodes[0];\n    if (spaceNode) {\n        let spaceVisibility = rule.spaceVisibility;\n        // In case we are asked to replace the space by a &nbsp;, disobey and\n        // do the opposite if that space is currently not visible\n        // TODO I'd like this to not be needed, it feels wrong...\n        if (\n            spaceVisibility &&\n            !foundVisibleSpaceTextNode &&\n            getState(...rightPos(spaceNode), DIRECTIONS.RIGHT).cType & CTGROUPS.BLOCK &&\n            getState(...leftPos(spaceNode), DIRECTIONS.LEFT).cType !== CTYPES.CONTENT\n        ) {\n            spaceVisibility = false;\n        }\n        spaceNode.nodeValue = spaceNode.nodeValue.replace(\n            whitespaceAtEdgeRegex,\n            spaceVisibility ? \"\\u00A0\" : \"\"\n        );\n    }\n}\n", "import { DIRECTIONS } from \"./position\";\n\nexport const closestPath = function* (node) {\n    while (node) {\n        yield node;\n        node = node.parentNode;\n    }\n};\n\n/**\n * Find a node.\n * @param {findCallback} findCallback - This callback check if this function\n *      should return `node`.\n * @param {findCallback} stopCallback - This callback check if this function\n *      should stop when it receive `node`.\n */\nexport function findNode(domPath, findCallback = () => true, stopCallback = () => false) {\n    for (const node of domPath) {\n        if (findCallback(node)) {\n            return node;\n        }\n        if (stopCallback(node)) {\n            break;\n        }\n    }\n    return null;\n}\n\n/**\n * @param {Node} node\n * @param {HTMLElement} limitAncestor - non inclusive limit ancestor to search for\n * @param {Function} predicate\n * @returns {Node|null}\n */\nexport function findUpTo(node, limitAncestor, predicate) {\n    while (node !== limitAncestor) {\n        if (predicate(node)) {\n            return node;\n        }\n        node = node.parentElement;\n    }\n    return null;\n}\n\n/**\n * @param {Node} node\n * @param {HTMLElement} limitAncestor - non inclusive limit ancestor to search for\n * @param {Function} predicate\n * @returns {Node|undefined}\n */\nexport function findFurthest(node, limitAncestor, predicate) {\n    const nodes = [];\n    while (node !== limitAncestor) {\n        nodes.push(node);\n        node = node.parentNode;\n    }\n    return nodes.findLast(predicate);\n}\n\n/**\n * Returns the closest HTMLElement of the provided Node. If the predicate is a\n * string, returns the closest HTMLElement that match the predicate selector. If\n * the predicate is a function, returns the closest element that matches the\n * predicate. Any returned element will be contained within the editable, or is\n * disconnected from any Document.\n *\n * Rationale: this helper is used to manipulate editor nodes, and should never\n * match any node outside of that scope. Disconnected nodes are assumed to be\n * from the editor, since they are likely removed nodes evaluated in the context\n * of the MutationObserver handler @see ProtectedNodePlugin\n *\n * @param {Node} node\n * @param {string | Function} [predicate='*']\n * @returns {HTMLElement|null}\n */\nexport function closestElement(node, predicate = \"*\") {\n    let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n    const editable = element?.closest(\".odoo-editor-editable\");\n    if (typeof predicate === \"function\") {\n        while (element && !predicate(element)) {\n            element = element.parentElement;\n        }\n    } else {\n        element = element?.closest(predicate);\n    }\n    if ((editable && editable.contains(element)) || !node.isConnected) {\n        return element;\n    }\n    return null;\n}\n\n/**\n * Returns a list of all the ancestors nodes of the provided node.\n *\n * @param {Node} node\n * @param {Node} [editable] include to prevent bubbling up further than the editable.\n * @returns {HTMLElement[]}\n */\nexport function ancestors(node, editable) {\n    const result = [];\n    while (node && node.parentElement && node !== editable) {\n        result.push(node.parentElement);\n        node = node.parentElement;\n    }\n    return result;\n}\n\n/**\n * Get a static array of children, to avoid manipulating the live HTMLCollection\n * for better performances.\n *\n * @param {Element}} elem\n * @returns {Array<Element>} children\n */\nexport function children(elem) {\n    const children = [];\n    let child = elem.firstElementChild;\n    while (child) {\n        children.push(child);\n        child = child.nextElementSibling;\n    }\n    return children;\n}\n\n/**\n * Get a static array of childNodes, to avoid manipulating the live NodeList for\n * better performances.\n *\n * @param {Node}} node\n * @returns {Array<Node>} childNodes\n */\nexport function childNodes(node) {\n    const childNodes = [];\n    let child = node.firstChild;\n    while (child) {\n        childNodes.push(child);\n        child = child.nextSibling;\n    }\n    return childNodes;\n}\n\n/**\n * Take a node, return all of its descendants, in depth-first order.\n *\n * @param {Node} node\n * @returns {Node[]}\n */\nexport function descendants(node, posterity = []) {\n    let child = node.firstChild;\n    while (child) {\n        posterity.push(child);\n        descendants(child, posterity);\n        child = child.nextSibling;\n    }\n    return posterity;\n}\n\n/**\n * Values which can be returned while browsing the DOM which gives information\n * to why the path ended.\n */\nexport const PATH_END_REASONS = {\n    NO_NODE: 0,\n    BLOCK_OUT: 1,\n    BLOCK_HIT: 2,\n    OUT_OF_SCOPE: 3,\n};\n\n/**\n * Creates a generator function according to the given parameters. Pre-made\n * generators to traverse the DOM are made using this function:\n *\n * @see leftLeafFirstPath\n * @see leftLeafOnlyNotBlockPath\n * @see leftLeafOnlyInScopeNotBlockEditablePath\n * @see rightLeafOnlyNotBlockPath\n * @see rightLeafOnlyNotBlockNotEditablePath\n *\n * @param {boolean} direction\n * @param {Object} options\n * @param {boolean} [options.leafOnly] if true, do not yield any non-leaf node\n * @param {boolean} [options.inScope] if true, stop the generator as soon as a node is not\n *                      a descendant of `node` provided when traversing the\n *                      generated function.\n * @param {Function} [options.stopTraverseFunction] a function that takes a node\n *                      and should return true when a node descendant should not\n *                      be traversed.\n * @param {Function} [options.stopFunction] function that makes the generator stop when a\n *                      node is encountered.\n */\nexport function createDOMPathGenerator(\n    direction,\n    { leafOnly = false, inScope = false, stopTraverseFunction, stopFunction } = {}\n) {\n    const nextDeepest =\n        direction === DIRECTIONS.LEFT\n            ? (node) => lastLeaf(node.previousSibling, stopTraverseFunction)\n            : (node) => firstLeaf(node.nextSibling, stopTraverseFunction);\n\n    const firstNode =\n        direction === DIRECTIONS.LEFT\n            ? (node, offset) => lastLeaf(node.childNodes[offset - 1], stopTraverseFunction)\n            : (node, offset) => firstLeaf(node.childNodes[offset], stopTraverseFunction);\n\n    // Note \"reasons\" is a way for the caller to be able to know why the\n    // generator ended yielding values.\n    return function* (node, offset, reasons = []) {\n        let movedUp = false;\n\n        let currentNode = firstNode(node, offset);\n        if (!currentNode) {\n            movedUp = true;\n            currentNode = node;\n        }\n\n        while (currentNode) {\n            if (stopFunction && stopFunction(currentNode)) {\n                reasons.push(movedUp ? PATH_END_REASONS.BLOCK_OUT : PATH_END_REASONS.BLOCK_HIT);\n                break;\n            }\n            if (inScope && currentNode === node) {\n                reasons.push(PATH_END_REASONS.OUT_OF_SCOPE);\n                break;\n            }\n            if (!(leafOnly && movedUp)) {\n                yield currentNode;\n            }\n\n            movedUp = false;\n            let nextNode = nextDeepest(currentNode);\n            if (!nextNode) {\n                movedUp = true;\n                nextNode = currentNode.parentNode;\n            }\n            currentNode = nextNode;\n        }\n\n        reasons.push(PATH_END_REASONS.NO_NODE);\n    };\n}\n\n/**\n * Returns the deepest child in last position.\n *\n * @param {Node} node\n * @param {Function} [stopTraverseFunction]\n * @returns {Node}\n */\nexport function lastLeaf(node, stopTraverseFunction) {\n    while (node && node.lastChild && !(stopTraverseFunction && stopTraverseFunction(node))) {\n        node = node.lastChild;\n    }\n    return node;\n}\n/**\n * Returns the deepest child in first position.\n *\n * @param {Node} node\n * @param {Function} [stopTraverseFunction]\n * @returns {Node}\n */\nexport function firstLeaf(node, stopTraverseFunction) {\n    while (node && node.firstChild && !(stopTraverseFunction && stopTraverseFunction(node))) {\n        node = node.firstChild;\n    }\n    return node;\n}\n\n/**\n * Returns all the previous siblings of the given node until the first\n * sibling that does not satisfy the predicate, in lookup order.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacentPreviousSiblings(node, predicate = (n) => !!n) {\n    let previous = node.previousSibling;\n    const list = [];\n    while (previous && predicate(previous)) {\n        list.push(previous);\n        previous = previous.previousSibling;\n    }\n    return list;\n}\n/**\n * Returns all the next siblings of the given node until the first\n * sibling that does not satisfy the predicate, in lookup order.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacentNextSiblings(node, predicate = (n) => !!n) {\n    let next = node.nextSibling;\n    const list = [];\n    while (next && predicate(next)) {\n        list.push(next);\n        next = next.nextSibling;\n    }\n    return list;\n}\n/**\n * Returns all the adjacent siblings of the given node until the first sibling\n * (in both directions) that does not satisfy the predicate, in index order. If\n * the given node does not satisfy the predicate, an empty array is returned.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacents(node, predicate = (n) => !!n) {\n    const previous = getAdjacentPreviousSiblings(node, predicate);\n    const next = getAdjacentNextSiblings(node, predicate);\n    return predicate(node) ? [...previous.reverse(), node, ...next] : [];\n}\n\n/**\n * Returns the deepest common ancestor element of the given nodes within the\n * specified root element. If no root element is provided, the entire document\n * is considered as the root.\n *\n * @param {Node[]} nodes - The nodes for which to find the common ancestor.\n * @param {Element} [root] - The root element within which to search for the common ancestor.\n * @returns {Element|null} - The common ancestor element, or null if no common ancestor is found.\n */\nexport function getCommonAncestor(nodes, root = undefined) {\n    const pathsToRoot = nodes.map((node) => [node, ...ancestors(node, root)]);\n\n    let candidate = pathsToRoot[0]?.at(-1);\n    if (root && candidate !== root) {\n        return null;\n    }\n    let commonAncestor = null;\n    while (candidate && pathsToRoot.every((path) => path.at(-1) === candidate)) {\n        commonAncestor = candidate;\n        pathsToRoot.forEach((path) => path.pop());\n        candidate = pathsToRoot[0].at(-1);\n    }\n    return commonAncestor;\n}\n\n/**\n * Basically a wrapper around `root.querySelectorAll` that includes the\n * root.\n *\n * @param {Element} root\n * @param {string} selector\n * @returns {Generator<Element>}\n */\nexport const selectElements = function* (root, selector) {\n    if (root.matches(selector)) {\n        yield root;\n    }\n    for (const elem of root.querySelectorAll(selector)) {\n        yield elem;\n    }\n};\n", "import { makeDraggableHook } from \"@web/core/utils/draggable_hook_builder\";\nimport { pick } from \"@web/core/utils/objects\";\nimport { reactive } from \"@odoo/owl\";\nimport { throttleForAnimation } from \"@web/core/utils/timing\";\nimport { closest, touching } from \"@web/core/utils/ui\";\n\n/** @typedef {import(\"@web/core/utils/draggable_hook_builder\").DraggableHandlerParams} DraggableHandlerParams */\n/** @typedef {import(\"@web/core/utils/draggable_hook_builder\").DraggableBuilderParams} DraggableBuilderParams */\n/** @typedef {import(\"@web/core/utils/draggable\").DraggableParams} DraggableParams */\n\n/** @typedef {DraggableHandlerParams & { dropzone: HTMLElement | null, helper: HTMLElement }} DragAndDropHandlerParams */\n/** @typedef {DraggableHandlerParams & { helper: HTMLElement }} DragAndDropStartParams */\n/** @typedef {DraggableHandlerParams & { dropzone: HTMLElement }} DropzoneHandlerParams */\n/**\n * @typedef DragAndDropParams\n * @extends {DraggableParams}\n *\n * MANDATORY\n * @property {(() => Array)} dropzones a function that returns the available dropzones\n * @property {(() => HTMLElement)} helper a function that returns a helper element\n * that will follow the cursor when dragging\n * @property {HTMLElement || (() => HTMLElement)} scrollingElement the element on\n * which a scroll should be triggered\n *\n * HANDLERS (Optional)\n * @property {(params: DragAndDropStartParams) => any} [onDragStart]\n * called when a dragging sequence is initiated\n * @property {(params: DropzoneHandlerParams) => any} [dropzoneOver]\n * called when an element is over a dropzone\n * @property {(params: DropzoneHandlerParams) => any} [dropzoneOut]\n * called when an element is leaving a dropzone\n * @property {(params: DragAndDropHandlerParams) => any} [onDrag]\n * called when an element is being dragged\n * @property {(params: DragAndDropHandlerParams) => any} [onDragEnd]\n * called when the dragging sequence is over\n */\n/**\n * @typedef NativeDraggableState\n * @property {(params: DraggableParams) => any} update\n * method to update the params of the draggable\n * @property {import(\"@web/core/utils/draggable\").DraggableState} state\n * state of the draggable component\n * @property {() => any} destroy\n * method to destroy and unbind the draggable component\n */\n/**\n * Utility function to create a native draggable component\n *\n * @param {DraggableBuilderParams} hookParams\n * @param {DraggableParams} initialParams\n * @returns {NativeDraggableState}\n */\nexport function useNativeDraggable(hookParams, initialParams) {\n    const setupFunctions = new Map();\n    const cleanupFunctions = [];\n    const currentParams = { ...initialParams };\n    const setupHooks = {\n        wrapState: reactive,\n        throttle: throttleForAnimation,\n        addListener: (el, type, callback, options) => {\n            el.addEventListener(type, callback, options);\n            cleanupFunctions.push(() => el.removeEventListener(type, callback));\n        },\n        setup: (setupFn, depsFn) => setupFunctions.set(setupFn, depsFn),\n        teardown: (cleanupFn) => {\n            cleanupFunctions.push(cleanupFn);\n        },\n    };\n    // Compatibility for tests\n    const el = initialParams.ref.el;\n    // TODO this is probably to be removed in master: the received params\n    // contain the selector that should be checked and it will be transferred\n    // to the makeDraggableHook function. There should not be any need to add\n    // the default selector class here.\n    el.classList.add(\"o_draggable\");\n    cleanupFunctions.push(() => el.classList.remove(\"o_draggable\"));\n\n    const draggableState = makeDraggableHook({ setupHooks, ...hookParams })(currentParams);\n    draggableState.enable = true;\n    const draggableComponent = {\n        state: draggableState,\n        update: (newParams) => {\n            Object.assign(currentParams, newParams);\n            setupFunctions.forEach((depsFn, setupFn) => setupFn(...depsFn()));\n        },\n        destroy: () => {\n            cleanupFunctions.forEach((cleanupFn) => cleanupFn());\n        },\n    };\n    draggableComponent.update({});\n    return draggableComponent;\n}\n\nfunction updateElementPosition(el, { x, y }, styleFn, offset = { x: 0, y: 0 }) {\n    return styleFn(el, { top: `${y - offset.y}px`, left: `${x - offset.x}px` });\n}\n/** @type DraggableBuilderParams */\nconst dragAndDropHookParams = {\n    name: \"useDragAndDrop\",\n    acceptedParams: {\n        dropzones: [Function],\n        scrollingElement: [Object, Function],\n        helper: [Function],\n        extraWindow: [Object, Function],\n    },\n    edgeScrolling: { enabled: true },\n    onComputeParams({ ctx, params }) {\n        // The helper is mandatory and will follow the cursor instead\n        ctx.followCursor = false;\n        ctx.scrollingElement = params.scrollingElement;\n        ctx.getHelper = params.helper;\n        ctx.getDropZones = params.dropzones;\n    },\n    onWillStartDrag: ({ ctx }) => {\n        ctx.current.container = ctx.scrollingElement;\n        ctx.current.helperOffset = { x: 0, y: 0 };\n    },\n    onDragStart: ({ ctx, addStyle, addCleanup }) => {\n        // Use the helper as the tracking element to properly update scroll values.\n        ctx.current.element = ctx.getHelper({ ...ctx.current, ...ctx.pointer });\n        ctx.current.helper = ctx.current.element;\n        ctx.current.helper.style.position = \"fixed\";\n        // We want the pointer events on the helper so that the cursor\n        // is properly displayed.\n        ctx.current.helper.classList.remove(\"o_dragged\");\n        ctx.current.helper.style.cursor = ctx.cursor;\n        ctx.current.helper.style.pointerEvents = \"auto\";\n\n        // If the helper is inside the iframe, we want pointer events on the\n        // frame element so that they reach the window and properly apply\n        // the cursor.\n        const frameElement = ctx.current.helper.ownerDocument.defaultView.frameElement;\n        if (frameElement) {\n            addStyle(frameElement, { pointerEvents: \"auto\" });\n        }\n\n        addCleanup(() => ctx.current.helper.remove());\n\n        updateElementPosition(ctx.current.helper, ctx.pointer, addStyle, ctx.current.helperOffset);\n\n        return pick(ctx.current, \"element\", \"helper\");\n    },\n    onDrag: ({ ctx, addStyle, callHandler }) => {\n        ctx.current.helper.classList.add(\"o_draggable_dragging\");\n\n        updateElementPosition(ctx.current.helper, ctx.pointer, addStyle, ctx.current.helperOffset);\n        // Unfortunately, DOMRect is not an Object, so spreading operator from\n        // `touching` does not work, so convert DOMRect to plain object.\n        let helperRect = ctx.current.helper.getBoundingClientRect();\n        helperRect = {\n            x: helperRect.x,\n            y: helperRect.y,\n            width: helperRect.width,\n            height: helperRect.height,\n        };\n        const dropzoneEl = closest(touching(ctx.getDropZones(), helperRect), helperRect);\n        // Update the drop zone if it's in grid mode\n        if (\n            ctx.current.dropzone?.el &&\n            ctx.current.dropzone.el.classList.contains(\"oe_grid_zone\")\n        ) {\n            ctx.current.dropzone.rect = ctx.current.dropzone.el.getBoundingClientRect();\n        }\n        if (\n            ctx.current.dropzone &&\n            (ctx.current.dropzone.el === dropzoneEl ||\n                (!dropzoneEl &&\n                    touching([ctx.current.helper], ctx.current.dropzone.rect).length > 0))\n        ) {\n            // If no new dropzone but old one is still valid, return early.\n            return pick(ctx.current, \"element\", \"dropzone\", \"helper\");\n        }\n\n        if (ctx.current.dropzone && dropzoneEl !== ctx.current.dropzone.el) {\n            callHandler(\"dropzoneOut\", { dropzone: ctx.current.dropzone });\n            delete ctx.current.dropzone;\n        }\n\n        if (dropzoneEl) {\n            // Save rect information prior to calling the over function\n            // to keep a consistent dropzone even if content was added.\n            const rect = DOMRect.fromRect(dropzoneEl.getBoundingClientRect());\n            ctx.current.dropzone = {\n                el: dropzoneEl,\n                rect: {\n                    x: rect.x,\n                    y: rect.y,\n                    width: rect.width,\n                    height: rect.height,\n                },\n            };\n            callHandler(\"dropzoneOver\", { dropzone: ctx.current.dropzone });\n        }\n        return pick(ctx.current, \"element\", \"dropzone\", \"helper\");\n    },\n    onDragEnd({ ctx }) {\n        return pick(ctx.current, \"element\", \"dropzone\", \"helper\");\n    },\n};\n/**\n * Function to start a drag and drop handler\n *\n * @param {DragAndDropParams} initialParams params given to the drag and drop\n * component\n * @returns {NativeDraggableState}\n */\nexport function useDragAndDrop(initialParams) {\n    return useNativeDraggable(dragAndDropHookParams, initialParams);\n}\n", "export const fonts = {\n    /**\n     * Retrieves all the CSS rules which match the given parser (Regex).\n     *\n     * @param {Regex} filter\n     * @returns {Object[]} Array of CSS rules descriptions (objects). A rule is\n     *          defined by 3 values: 'selector', 'css' and 'names'. 'selector'\n     *          is a string which contains the whole selector, 'css' is a string\n     *          which contains the css properties and 'names' is an array of the\n     *          first captured groups for each selector part. E.g.: if the\n     *          filter is set to match .fa-* rules and capture the icon names,\n     *          the rule:\n     *              '.fa-alias1::before, .fa-alias2::before { hello: world; }'\n     *          will be retrieved as\n     *              {\n     *                  selector: '.fa-alias1::before, .fa-alias2::before',\n     *                  css: 'hello: world;',\n     *                  names: ['.fa-alias1', '.fa-alias2'],\n     *              }\n     */\n    cacheCssSelectors: {},\n    getCssSelectors: function (filter) {\n        if (this.cacheCssSelectors[filter]) {\n            return this.cacheCssSelectors[filter];\n        }\n        this.cacheCssSelectors[filter] = [];\n        var sheets = document.styleSheets;\n        for (var i = 0; i < sheets.length; i++) {\n            var rules;\n            try {\n                // try...catch because Firefox not able to enumerate\n                // document.styleSheets[].cssRules[] for cross-domain\n                // stylesheets.\n                rules = sheets[i].rules || sheets[i].cssRules;\n            } catch {\n                continue;\n            }\n            if (!rules) {\n                continue;\n            }\n\n            for (var r = 0; r < rules.length; r++) {\n                var selectorText = rules[r].selectorText;\n                if (!selectorText) {\n                    continue;\n                }\n                var selectors = selectorText.split(/\\s*,\\s*/);\n                var data = null;\n                for (var s = 0; s < selectors.length; s++) {\n                    var match = selectors[s].trim().match(filter);\n                    if (!match) {\n                        continue;\n                    }\n                    if (!data) {\n                        data = {\n                            selector: match[0],\n                            css: rules[r].cssText.replace(/(^.*\\{\\s*)|(\\s*\\}\\s*$)/g, \"\"),\n                            names: [match[1]],\n                        };\n                    } else {\n                        data.selector += \", \" + match[0];\n                        data.names.push(match[1]);\n                    }\n                }\n                if (data) {\n                    this.cacheCssSelectors[filter].push(data);\n                }\n            }\n        }\n        return this.cacheCssSelectors[filter];\n    },\n    /**\n     * List of font icons to load by editor. The icons are displayed in the media\n     * editor and identified like font and image (can be colored, spinned, resized\n     * with fa classes).\n     * To add font, push a new object {base, parser}\n     *\n     * - base: class who appear on all fonts\n     * - parser: regular expression used to select all font in css stylesheets\n     *\n     * @type Array\n     */\n    fontIcons: [{ base: \"fa\", parser: /\\.(fa-(?:\\w|-)+)::?before/i }],\n    computedFonts: false,\n    /**\n     * Searches the fonts described by the @see fontIcons variable.\n     */\n    computeFonts: function () {\n        if (!this.computedFonts) {\n            var self = this;\n            this.fontIcons.forEach((data) => {\n                data.cssData = self.getCssSelectors(data.parser);\n                data.alias = data.cssData.map((x) => x.names).flat();\n            });\n            this.computedFonts = true;\n        }\n    },\n};\n", "import { normalizeCSSColor } from \"@web/core/utils/colors\";\nimport { removeClass } from \"./dom\";\nimport { isBold, isDirectionSwitched, isItalic, isStrikeThrough, isUnderline } from \"./dom_info\";\nimport { closestElement } from \"./dom_traversal\";\n\n/**\n * Array of all the classes used by the editor to change the font size.\n */\nexport const FONT_SIZE_CLASSES = [\n    \"display-1-fs\",\n    \"display-2-fs\",\n    \"display-3-fs\",\n    \"display-4-fs\",\n    \"h1-fs\",\n    \"h2-fs\",\n    \"h3-fs\",\n    \"h4-fs\",\n    \"h5-fs\",\n    \"h6-fs\",\n    \"base-fs\",\n    \"small\",\n    \"o_small-fs\",\n];\n\nexport const TEXT_STYLE_CLASSES = [\"display-1\", \"display-2\", \"display-3\", \"display-4\", \"lead\"];\n\nexport const formatsSpecs = {\n    italic: {\n        tagName: \"em\",\n        isFormatted: isItalic,\n        isTag: (node) => [\"EM\", \"I\"].includes(node.tagName),\n        hasStyle: (node) => Boolean(node.style && node.style[\"font-style\"]),\n        addStyle: (node) => (node.style[\"font-style\"] = \"italic\"),\n        addNeutralStyle: (node) => (node.style[\"font-style\"] = \"normal\"),\n        removeStyle: (node) => removeStyle(node, \"font-style\"),\n    },\n    bold: {\n        tagName: \"strong\",\n        isFormatted: isBold,\n        isTag: (node) => [\"STRONG\", \"B\"].includes(node.tagName),\n        hasStyle: (node) => Boolean(node.style && node.style[\"font-weight\"]),\n        addStyle: (node) => (node.style[\"font-weight\"] = \"bolder\"),\n        addNeutralStyle: (node) => {\n            node.style[\"font-weight\"] = \"normal\";\n        },\n        removeStyle: (node) => removeStyle(node, \"font-weight\"),\n    },\n    underline: {\n        tagName: \"u\",\n        isFormatted: isUnderline,\n        isTag: (node) => node.tagName === \"U\",\n        hasStyle: (node) =>\n            node.style &&\n            (node.style[\"text-decoration\"].includes(\"underline\") ||\n                node.style[\"text-decoration-line\"].includes(\"underline\")),\n        addStyle: (node) => (node.style[\"text-decoration-line\"] += \" underline\"),\n        removeStyle: (node) =>\n            removeStyle(\n                node,\n                node.style[\"text-decoration\"].includes(\"underline\")\n                    ? \"text-decoration\"\n                    : \"text-decoration-line\",\n                \"underline\"\n            ),\n    },\n    strikeThrough: {\n        tagName: \"s\",\n        isFormatted: isStrikeThrough,\n        isTag: (node) => node.tagName === \"S\",\n        hasStyle: (node) =>\n            node.style &&\n            (node.style[\"text-decoration\"].includes(\"line-through\") ||\n                node.style[\"text-decoration-line\"].includes(\"line-through\")),\n        addStyle: (node) => (node.style[\"text-decoration-line\"] += \" line-through\"),\n        removeStyle: (node) =>\n            removeStyle(\n                node,\n                node.style[\"text-decoration\"].includes(\"line-through\")\n                    ? \"text-decoration\"\n                    : \"text-decoration-line\",\n                \"line-through\"\n            ),\n    },\n    fontSize: {\n        isFormatted: (node) => closestElement(node)?.style[\"font-size\"],\n        hasStyle: (node) => node.style && node.style[\"font-size\"],\n        addStyle: (node, props) => {\n            node.style[\"font-size\"] = props.size;\n            removeClass(node, ...FONT_SIZE_CLASSES);\n        },\n        removeStyle: (node) => removeStyle(node, \"font-size\"),\n    },\n    setFontSizeClassName: {\n        isFormatted: (node) =>\n            FONT_SIZE_CLASSES.find((cls) => closestElement(node)?.classList?.contains(cls)),\n        hasStyle: (node, props) => FONT_SIZE_CLASSES.find((cls) => node.classList.contains(cls)),\n        addStyle: (node, props) => node.classList.add(props.className),\n        removeStyle: (node) => removeClass(node, ...FONT_SIZE_CLASSES, ...TEXT_STYLE_CLASSES),\n    },\n    switchDirection: {\n        isFormatted: isDirectionSwitched,\n    },\n};\n\nfunction removeStyle(node, styleName, item) {\n    if (item) {\n        const newStyle = node.style[styleName]\n            .split(\" \")\n            .filter((x) => x !== item)\n            .join(\" \");\n        node.style[styleName] = newStyle || null;\n    } else {\n        node.style[styleName] = null;\n    }\n    if (node.getAttribute(\"style\") === \"\") {\n        node.removeAttribute(\"style\");\n    }\n}\n\n/**\n * @param {string} key\n * @param {object} htmlStyle\n * @returns {string}\n */\nexport function getCSSVariableValue(key, htmlStyle) {\n    // Get trimmed value from the HTML element\n    let value = htmlStyle.getPropertyValue(`--${key}`).trim();\n    // If it is a color value, it needs to be normalized\n    value = normalizeCSSColor(value);\n    // Normally scss-string values are \"printed\" single-quoted. That way no\n    // magic conversation is needed when customizing a variable: either save it\n    // quoted for strings or non quoted for colors, numbers, etc. However,\n    // Chrome has the annoying behavior of changing the single-quotes to\n    // double-quotes when reading them through getPropertyValue...\n    return value.replace(/\"/g, \"'\");\n}\n\n/**\n * Key-value mapping to list converters from an unit A to an unit B.\n * - The key is a string in the format '$1-$2' where $1 is the CSS symbol of\n *   unit A and $2 is the CSS symbol of unit B.\n * - The value is a function that converts the received value (expressed in\n *   unit A) to another value expressed in unit B. Two other parameters is\n *   received: the css property on which the unit applies and the jQuery element\n *   on which that css property may change.\n */\nconst CSS_UNITS_CONVERSION = {\n    \"s-ms\": () => 1000,\n    \"ms-s\": () => 0.001,\n    \"rem-px\": (htmlStyle) => parseFloat(htmlStyle[\"font-size\"]),\n    \"px-rem\": (htmlStyle) => 1 / parseFloat(htmlStyle[\"font-size\"]),\n    \"%-px\": () => -1, // Not implemented but should simply be ignored for now\n    \"px-%\": () => -1, // Not implemented but should simply be ignored for now\n};\n\n/**\n * Converts the given numeric value expressed in the given css unit into\n * the corresponding numeric value expressed in the other given css unit.\n *\n * e.g. fct(400, 'ms', 's') -> 0.4\n *\n * @param {number} value\n * @param {string} unitFrom\n * @param {string} unitTo\n * @param {object} htmlStyle\n * @returns {number}\n */\nexport function convertNumericToUnit(value, unitFrom, unitTo, htmlStyle) {\n    if (Math.abs(value) < Number.EPSILON || unitFrom === unitTo) {\n        return value;\n    }\n    const converter = CSS_UNITS_CONVERSION[`${unitFrom}-${unitTo}`];\n    if (converter === undefined) {\n        throw new Error(`Cannot convert '${unitFrom}' units into '${unitTo}' units !`);\n    }\n    return value * converter(htmlStyle);\n}\n\nexport function getHtmlStyle(document) {\n    return document.defaultView.getComputedStyle(document.documentElement);\n}\n\n/**\n * Finds the font size to display for the current selection. We cannot rely\n * on the computed font-size only as font-sizes are responsive and we always\n * want to display the desktop (integer when possible) one.\n *\n * @param {Selection} sel The current selection.\n * @param {Document} document The document of the current selection.\n * @returns {Float} The font size to display.\n */\nexport function getFontSizeDisplayValue(sel, document) {\n    const tagNameRelatedToFontSize = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"];\n    const styleClassesRelatedToFontSize = [\"display-1\", \"display-2\", \"display-3\", \"display-4\"];\n    const closestStartContainerEl = closestElement(sel.startContainer);\n    const closestFontSizedEl = closestStartContainerEl.closest(`\n        [style*='font-size'],\n        ${FONT_SIZE_CLASSES.map((className) => `.${className}`)},\n        ${styleClassesRelatedToFontSize.map((className) => `.${className}`)},\n        ${tagNameRelatedToFontSize}\n    `);\n    let remValue;\n    const htmlStyle = getHtmlStyle(document);\n    if (closestFontSizedEl) {\n        const useFontSizeInput = closestFontSizedEl.style.fontSize;\n        if (useFontSizeInput) {\n            // Use the computed value to always convert to px. However, this\n            // currently does not check that the inline font-size is the one\n            // actually having an effect (there could be an !important CSS rule\n            // forcing something else).\n            // TODO align with the behavior of the rest of the editor snippet\n            // options.\n            return parseFloat(getComputedStyle(closestStartContainerEl).fontSize);\n        }\n        // It's a class font size or a hN tag. We don't return the computed\n        // font size because it can be different from the one displayed in\n        // the toolbar because it's responsive.\n        const fontSizeClass = FONT_SIZE_CLASSES.find((className) =>\n            closestFontSizedEl.classList.contains(className)\n        );\n        let fsName;\n        if (fontSizeClass) {\n            fsName = fontSizeClass.substring(0, fontSizeClass.length - 3); // Without -fs\n        } else {\n            fsName =\n                styleClassesRelatedToFontSize.find((className) =>\n                    closestFontSizedEl.classList.contains(className)\n                ) || closestFontSizedEl.tagName.toLowerCase();\n        }\n        remValue = parseFloat(getCSSVariableValue(`${fsName}-font-size`, htmlStyle));\n    }\n    // It's default font size (no font size class / style).\n    if (remValue === undefined) {\n        remValue = parseFloat(getCSSVariableValue(\"font-size-base\", htmlStyle));\n    }\n    const pxValue = convertNumericToUnit(remValue, \"rem\", \"px\", htmlStyle);\n    return pxValue || parseFloat(getComputedStyle(closestStartContainerEl).fontSize);\n}\n", "import { fixInvalidHTML } from \"./sanitize\";\n\n/**\n * @param { Document } document\n * @param { string } html\n * @returns { DocumentFragment }\n */\nexport function parseHTML(document, html) {\n    const fragment = document.createDocumentFragment();\n    const parser = new document.defaultView.DOMParser();\n    const parsedDocument = parser.parseFromString(html, \"text/html\");\n    fragment.replaceChildren(...parsedDocument.body.childNodes);\n    return fragment;\n}\n\n/**\n * Server-side, HTML is stored as a string which can have a different format\n * than what the current browser returns through outerHTML or innerHTML, notably\n * because of HTML entities.\n * This function can be used to convert strings with potential HTML entities to\n * the format used by the current browser. This allows comparisons between\n * values returned by the server and values extracted from the DOM using i.e.\n * innerHTML.\n *\n * @param { string } content\n * @param { function } cleanup receives the body element containing the parsed\n *        html, to perform some cleanup for the comparison.\n * @returns { string }\n */\nexport function normalizeHTML(content, cleanup = () => {}) {\n    const parser = new document.defaultView.DOMParser();\n    const body = parser.parseFromString(fixInvalidHTML(content), \"text/html\").body;\n    cleanup(body);\n    return body.innerHTML;\n}\n", "import { isColorGradient } from \"./color\";\n\n/**\n * Extracts url and gradient parts from the background-image CSS property.\n *\n * @param {string} CSS 'background-image' property value\n * @returns {Object} contains the separated 'url' and 'gradient' parts\n */\nexport function backgroundImageCssToParts(css) {\n    const parts = {};\n    css = css || \"\";\n    if (css.startsWith(\"url(\")) {\n        const urlEnd = css.indexOf(\")\") + 1;\n        parts.url = css.substring(0, urlEnd).trim();\n        const commaPos = css.indexOf(\",\", urlEnd);\n        css = commaPos > 0 ? css.substring(commaPos + 1) : \"\";\n    }\n    if (isColorGradient(css)) {\n        parts.gradient = css.trim();\n    }\n    return parts;\n}\n\n/**\n * Combines url and gradient parts into a background-image CSS property value\n *\n * @param {Object} contains the separated 'url' and 'gradient' parts\n * @returns {string} CSS 'background-image' property value\n */\nexport function backgroundImagePartsToCss(parts) {\n    let css = parts.url || \"\";\n    if (parts.gradient) {\n        css += (css ? \", \" : \"\") + parts.gradient;\n    }\n    return css || \"none\";\n}\n", "import { rpc } from \"@web/core/network/rpc\";\nimport { pick } from \"@web/core/utils/objects\";\nimport { getAffineApproximation, getProjective } from \"./perspective_utils\";\n\n// Fields returned by cropperjs 'getData' method, also need to be passed when\n// initializing the cropper to reuse the previous crop.\nexport const cropperDataFields = [\"x\", \"y\", \"width\", \"height\", \"rotate\", \"scaleX\", \"scaleY\"];\nexport const isGif = (mimetype) => mimetype === \"image/gif\";\n\n// webgl color filters\nconst _applyAll = (result, filter, filters) => {\n    filters.forEach((f) => {\n        if (f[0] === \"blend\") {\n            const cv = f[1];\n            const ctx = result.getContext(\"2d\");\n            ctx.globalCompositeOperation = f[2];\n            ctx.globalAlpha = f[3];\n            ctx.drawImage(cv, 0, 0);\n            ctx.globalCompositeOperation = \"source-over\";\n            ctx.globalAlpha = 1.0;\n        } else {\n            filter.addFilter(...f);\n        }\n    });\n};\nlet applyAll;\n\nconst glFilters = {\n    blur: (filter) => filter.addFilter(\"blur\", 10),\n\n    1977: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        ctx.fillStyle = \"rgb(243, 106, 188)\";\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"screen\", 0.3],\n            [\"brightness\", 0.1],\n            [\"contrast\", 0.1],\n            [\"saturation\", 0.3],\n        ]);\n    },\n\n    aden: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        ctx.fillStyle = \"rgb(66, 10, 14)\";\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"darken\", 0.2],\n            [\"brightness\", 0.2],\n            [\"contrast\", -0.1],\n            [\"saturation\", -0.15],\n            [\"hue\", 20],\n        ]);\n    },\n\n    brannan: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        ctx.fillStyle = \"rgb(161, 44, 191)\";\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"lighten\", 0.31],\n            [\"sepia\", 0.5],\n            [\"contrast\", 0.4],\n        ]);\n    },\n\n    earlybird: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        const gradient = ctx.createRadialGradient(\n            cv.width / 2,\n            cv.height / 2,\n            0,\n            cv.width / 2,\n            cv.height / 2,\n            Math.hypot(cv.width, cv.height) / 2\n        );\n        gradient.addColorStop(0.2, \"#D0BA8E\");\n        gradient.addColorStop(1, \"#1D0210\");\n        ctx.fillStyle = gradient;\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"overlay\", 0.2],\n            [\"sepia\", 0.2],\n            [\"contrast\", -0.1],\n        ]);\n    },\n\n    inkwell: (filter, cv) => {\n        applyAll(filter, [\n            [\"sepia\", 0.3],\n            [\"brightness\", 0.1],\n            [\"contrast\", -0.1],\n            [\"desaturateLuminance\"],\n        ]);\n    },\n\n    // Needs hue blending mode for perfect reproduction. Close enough?\n    maven: (filter, cv) => {\n        applyAll(filter, [\n            [\"sepia\", 0.25],\n            [\"brightness\", -0.05],\n            [\"contrast\", -0.05],\n            [\"saturation\", 0.5],\n        ]);\n    },\n\n    toaster: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        const gradient = ctx.createRadialGradient(\n            cv.width / 2,\n            cv.height / 2,\n            0,\n            cv.width / 2,\n            cv.height / 2,\n            Math.hypot(cv.width, cv.height) / 2\n        );\n        gradient.addColorStop(0, \"#0F4E80\");\n        gradient.addColorStop(1, \"#3B003B\");\n        ctx.fillStyle = gradient;\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"screen\", 0.5],\n            [\"brightness\", -0.1],\n            [\"contrast\", 0.5],\n        ]);\n    },\n\n    walden: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        ctx.fillStyle = \"#CC4400\";\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"screen\", 0.3],\n            [\"sepia\", 0.3],\n            [\"brightness\", 0.1],\n            [\"saturation\", 0.6],\n            [\"hue\", 350],\n        ]);\n    },\n\n    valencia: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        ctx.fillStyle = \"#3A0339\";\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"exclusion\", 0.5],\n            [\"sepia\", 0.08],\n            [\"brightness\", 0.08],\n            [\"contrast\", 0.08],\n        ]);\n    },\n\n    xpro: (filter, cv) => {\n        const ctx = cv.getContext(\"2d\");\n        const gradient = ctx.createRadialGradient(\n            cv.width / 2,\n            cv.height / 2,\n            0,\n            cv.width / 2,\n            cv.height / 2,\n            Math.hypot(cv.width, cv.height) / 2\n        );\n        gradient.addColorStop(0.4, \"#E0E7E6\");\n        gradient.addColorStop(1, \"#2B2AA1\");\n        ctx.fillStyle = gradient;\n        ctx.fillRect(0, 0, cv.width, cv.height);\n        applyAll(filter, [\n            [\"blend\", cv, \"color-burn\", 0.7],\n            [\"sepia\", 0.3],\n        ]);\n    },\n\n    custom: (filter, cv, filterOptions) => {\n        const options = Object.assign(\n            {\n                blend: \"normal\",\n                filterColor: \"\",\n                blur: \"0\",\n                desaturateLuminance: \"0\",\n                saturation: \"0\",\n                contrast: \"0\",\n                brightness: \"0\",\n                sepia: \"0\",\n            },\n            JSON.parse(filterOptions || \"{}\")\n        );\n        const filters = [];\n        if (options.filterColor) {\n            const ctx = cv.getContext(\"2d\");\n            ctx.fillStyle = options.filterColor;\n            ctx.fillRect(0, 0, cv.width, cv.height);\n            filters.push([\"blend\", cv, options.blend, 1]);\n        }\n        delete options.blend;\n        delete options.filterColor;\n        filters.push(\n            ...Object.entries(options).map(([filter, amount]) => [filter, parseInt(amount) / 100])\n        );\n        applyAll(filter, filters);\n    },\n};\n\n/**\n * Applies data-attributes modifications to an img tag and returns a dataURL\n * containing the result. This function does not modify the original image.\n *\n * @param {HTMLImageElement} img the image to which modifications are applied\n * @param {Cropper} cropper the cropper instance\n * @returns {string} dataURL of the image with the applied modifications\n */\nexport async function applyModifications(img, cropper, dataOptions = {}) {\n    const data = Object.assign(\n        {\n            glFilter: \"\",\n            filter: \"#0000\",\n            quality: \"75\",\n            forceModification: false,\n        },\n        img.dataset,\n        dataOptions\n    );\n    let {\n        width,\n        height,\n        resizeWidth,\n        quality,\n        filter,\n        mimetype,\n        originalSrc,\n        glFilter,\n        filterOptions,\n        forceModification,\n        perspective,\n        svgAspectRatio,\n        imgAspectRatio,\n    } = data;\n    [width, height, resizeWidth] = [width, height, resizeWidth].map((s) => parseFloat(s));\n    quality = parseInt(quality);\n\n    // Skip modifications (required to add shapes on animated GIFs).\n    if (isGif(mimetype) && !forceModification) {\n        return await _loadImageDataURL(originalSrc);\n    }\n\n    // Crop\n    const container = document.createElement(\"div\");\n    const original = await loadImage(originalSrc);\n    // loadImage may have ended up loading a different src (see: LOAD_IMAGE_404)\n    originalSrc = original.getAttribute(\"src\");\n    container.appendChild(original);\n    let croppedImg = cropper.getCroppedCanvas(width, height);\n\n    // Aspect Ratio\n    if (imgAspectRatio) {\n        document.createElement(\"div\").appendChild(croppedImg);\n        imgAspectRatio = imgAspectRatio.split(\":\");\n        imgAspectRatio = parseFloat(imgAspectRatio[0]) / parseFloat(imgAspectRatio[1]);\n        const croppedCropper = await activateCropper(croppedImg, imgAspectRatio, { y: 0 });\n        croppedImg = croppedCropper.cropper(\"getCroppedCanvas\");\n        croppedCropper.destroy();\n    }\n\n    // Width\n    const result = document.createElement(\"canvas\");\n    result.width = resizeWidth || croppedImg.width;\n    result.height = perspective\n        ? result.width / svgAspectRatio\n        : (croppedImg.height * result.width) / croppedImg.width;\n    const ctx = result.getContext(\"2d\");\n    ctx.imageSmoothingQuality = \"high\";\n    ctx.mozImageSmoothingEnabled = true;\n    ctx.webkitImageSmoothingEnabled = true;\n    ctx.msImageSmoothingEnabled = true;\n    ctx.imageSmoothingEnabled = true;\n\n    // Perspective 3D\n    if (perspective) {\n        // x, y coordinates of the corners of the image as a percentage\n        // (relative to the width or height of the image) needed to apply\n        // the 3D effect.\n        const points = JSON.parse(perspective);\n        const divisions = 10;\n        const w = croppedImg.width,\n            h = croppedImg.height;\n\n        const project = getProjective(w, h, [\n            [(result.width / 100) * points[0][0], (result.height / 100) * points[0][1]], // Top-left [x, y]\n            [(result.width / 100) * points[1][0], (result.height / 100) * points[1][1]], // Top-right [x, y]\n            [(result.width / 100) * points[2][0], (result.height / 100) * points[2][1]], // bottom-right [x, y]\n            [(result.width / 100) * points[3][0], (result.height / 100) * points[3][1]], // bottom-left [x, y]\n        ]);\n\n        for (let i = 0; i < divisions; i++) {\n            for (let j = 0; j < divisions; j++) {\n                const [dx, dy] = [w / divisions, h / divisions];\n\n                const upper = {\n                    origin: [i * dx, j * dy],\n                    sides: [dx, dy],\n                    flange: 0.1,\n                    overlap: 0,\n                };\n                const lower = {\n                    origin: [i * dx + dx, j * dy + dy],\n                    sides: [-dx, -dy],\n                    flange: 0,\n                    overlap: 0.1,\n                };\n\n                for (let { origin, sides, flange, overlap } of [upper, lower]) {\n                    const [[a, c, e], [b, d, f]] = getAffineApproximation(project, [\n                        origin,\n                        [origin[0] + sides[0], origin[1]],\n                        [origin[0], origin[1] + sides[1]],\n                    ]);\n\n                    const ox = (i !== divisions ? overlap * sides[0] : 0) + flange * sides[0];\n                    const oy = (j !== divisions ? overlap * sides[1] : 0) + flange * sides[1];\n\n                    origin[0] += flange * sides[0];\n                    origin[1] += flange * sides[1];\n\n                    sides[0] -= flange * sides[0];\n                    sides[1] -= flange * sides[1];\n\n                    ctx.save();\n                    ctx.setTransform(a, b, c, d, e, f);\n\n                    ctx.beginPath();\n                    ctx.moveTo(origin[0] - ox, origin[1] - oy);\n                    ctx.lineTo(origin[0] + sides[0], origin[1] - oy);\n                    ctx.lineTo(origin[0] + sides[0], origin[1]);\n                    ctx.lineTo(origin[0], origin[1] + sides[1]);\n                    ctx.lineTo(origin[0] - ox, origin[1] + sides[1]);\n                    ctx.closePath();\n                    ctx.clip();\n                    ctx.drawImage(croppedImg, 0, 0);\n\n                    ctx.restore();\n                }\n            }\n        }\n    } else {\n        ctx.drawImage(\n            croppedImg,\n            0,\n            0,\n            croppedImg.width,\n            croppedImg.height,\n            0,\n            0,\n            result.width,\n            result.height\n        );\n    }\n\n    // GL filter\n    if (glFilter) {\n        const glf = new window.WebGLImageFilter();\n        const cv = document.createElement(\"canvas\");\n        cv.width = result.width;\n        cv.height = result.height;\n        applyAll = _applyAll.bind(null, result);\n        glFilters[glFilter](glf, cv, filterOptions);\n        const filtered = glf.apply(result);\n        ctx.drawImage(\n            filtered,\n            0,\n            0,\n            filtered.width,\n            filtered.height,\n            0,\n            0,\n            result.width,\n            result.height\n        );\n    }\n\n    // Color filter\n    ctx.fillStyle = filter || \"#0000\";\n    ctx.fillRect(0, 0, result.width, result.height);\n\n    // Quality\n    const dataURL = result.toDataURL(mimetype, quality / 100);\n    const newSize = getDataURLBinarySize(dataURL);\n    const originalSize = _getImageSizeFromCache(originalSrc);\n    const isChanged =\n        !!perspective ||\n        !!glFilter ||\n        original.width !== result.width ||\n        original.height !== result.height ||\n        original.width !== croppedImg.width ||\n        original.height !== croppedImg.height;\n    return isChanged || originalSize >= newSize ? dataURL : await _loadImageDataURL(originalSrc);\n}\n\n/**\n * Loads an src into an HTMLImageElement.\n *\n * @param {String} src URL of the image to load\n * @param {HTMLImageElement} [img] img element in which to load the image\n * @returns {Promise<HTMLImageElement>} Promise that resolves to the loaded img\n *     or a placeholder image if the src is not found.\n */\nexport function loadImage(src, img = new Image()) {\n    const handleImage = (source, resolve, reject) => {\n        img.addEventListener(\"load\", () => resolve(img), { once: true });\n        img.addEventListener(\"error\", reject, { once: true });\n        img.src = source;\n    };\n    // The server will return a placeholder image with the following src.\n    // grep: LOAD_IMAGE_404\n    const placeholderHref = \"/web/image/__odoo__unknown__src__/\";\n\n    return new Promise((resolve, reject) => {\n        fetch(src)\n            .then((response) => {\n                if (!response.ok) {\n                    src = placeholderHref;\n                }\n                handleImage(src, resolve, reject);\n            })\n            .catch((error) => {\n                src = placeholderHref;\n                handleImage(src, resolve, reject);\n            });\n    });\n}\n\n// Because cropperjs acquires images through XHRs on the image src and we don't\n// want to load big images over the network many times when adjusting quality\n// and filter, we create a local cache of the images using object URLs.\nconst imageCache = new Map();\n\n/**\n * Loads image object URL into cache if not already set and returns it.\n *\n * @param {String} src\n * @returns {Promise}\n */\nfunction _loadImageObjectURL(src) {\n    return _updateImageData(src);\n}\n\n/**\n * Gets image dataURL from cache in the same way as object URL.\n *\n * @param {String} src\n * @returns {Promise}\n */\nfunction _loadImageDataURL(src) {\n    return _updateImageData(src, \"dataURL\");\n}\n\n/**\n * @param {String} src used as a key on the image cache map.\n * @param {String} [key='objectURL'] specifies the image data to update/return.\n * @returns {Promise<String>} resolves with either dataURL/objectURL value.\n */\nasync function _updateImageData(src, key = \"objectURL\") {\n    const currentImageData = imageCache.get(src);\n    if (currentImageData && currentImageData[key]) {\n        return currentImageData[key];\n    }\n    let value = \"\";\n    const blob = await fetch(src).then((res) => res.blob());\n    if (key === \"dataURL\") {\n        value = await createDataURL(blob);\n    } else {\n        value = URL.createObjectURL(blob);\n    }\n    imageCache.set(src, Object.assign(currentImageData || {}, { [key]: value, size: blob.size }));\n    return value;\n}\n\n/**\n * Returns the size of a cached image.\n * Warning: this supposes that the image is already in the cache, i.e. that\n * _updateImageData was called before.\n *\n * @param {String} src used as a key on the image cache map.\n * @returns {Number} size of the image in bytes.\n */\nfunction _getImageSizeFromCache(src) {\n    return imageCache.get(src).size;\n}\n\n/**\n * Activates the cropper on a given image.\n *\n * @param {jQuery} $image the image on which to activate the cropper\n * @param {Number} aspectRatio the aspectRatio of the crop box\n * @param {DOMStringMap} dataset dataset containing the cropperDataFields\n */\nexport async function activateCropper(image, aspectRatio, dataset) {\n    const oldSrc = image.src;\n    const newSrc = await _loadImageObjectURL(image.getAttribute(\"src\"));\n    image.src = newSrc;\n    // eslint-disable-next-line no-undef\n    const cropper = new Cropper(image, {\n        viewMode: 2,\n        dragMode: \"move\",\n        autoCropArea: 1.0,\n        aspectRatio: aspectRatio,\n        data: Object.fromEntries(\n            Object.entries(pick(dataset, ...cropperDataFields)).map(([key, value]) => [\n                key,\n                parseFloat(value),\n            ])\n        ),\n        // Can't use 0 because it's falsy and cropperjs will then use its defaults (200x100)\n        minContainerWidth: 1,\n        minContainerHeight: 1,\n    });\n    if (oldSrc === newSrc && image.complete) {\n        return;\n    }\n    return cropper;\n}\n\n/**\n * Marks an <img> with its attachment data (originalId, originalSrc, mimetype)\n *\n * @param {HTMLImageElement} img the image whose attachment data should be found\n * @param {string} [attachmentSrc=''] specifies the URL of the corresponding\n * attachment if it can't be found in the 'src' attribute.\n */\nexport async function loadImageInfo(img, attachmentSrc = \"\") {\n    const src = attachmentSrc || img.getAttribute(\"src\");\n    // If there is a marked originalSrc, the data is already loaded.\n    // If the image does not have the \"mimetypeBeforeConversion\" attribute, it\n    // has to be added.\n    if ((img.dataset.originalSrc && img.dataset.mimetypeBeforeConversion) || !src) {\n        return;\n    }\n    // In order to be robust to absolute, relative and protocol relative URLs,\n    // the src of the img is first converted to an URL object. To do so, the URL\n    // of the document in which the img is located is used as a base to build\n    // the URL object if the src of the img is a relative or protocol relative\n    // URL. The original attachment linked to the img is then retrieved thanks\n    // to the path of the built URL object.\n    let docHref = img.ownerDocument.defaultView.location.href;\n    if (docHref.startsWith(\"about:\")) {\n        docHref = window.location.href;\n    }\n\n    const srcUrl = new URL(src, docHref);\n    const relativeSrc = srcUrl.pathname;\n\n    const { original } = await rpc(\"/html_editor/get_image_info\", { src: relativeSrc });\n    // If src was an absolute \"external\" URL, we consider unlikely that its\n    // relative part matches something from the DB and even if it does, nothing\n    // bad happens, besides using this random image as the original when using\n    // the options, instead of having no option. Note that we do not want to\n    // check if the image is local or not here as a previous bug converted some\n    // local (relative src) images to absolute URL... and that before users had\n    // setup their website domain. That means they can have an absolute URL that\n    // looks like \"https://mycompany.odoo.com/web/image/123\" that leads to a\n    // \"local\" image even if the domain name is now \"mycompany.be\".\n    //\n    // The \"redirect\" check is for when it is a redirect image attachment due to\n    // an external URL upload.\n    if (\n        original &&\n        original.image_src &&\n        !/\\/web\\/image\\/\\d+-redirect\\//.test(original.image_src)\n    ) {\n        if (!img.dataset.mimetype) {\n            // The mimetype has to be added only if it is not already present as\n            // we want to avoid to reset a mimetype set by the user.\n            img.dataset.mimetype = original.mimetype;\n        }\n        img.dataset.originalId = original.id;\n        img.dataset.originalSrc = original.image_src;\n        img.dataset.mimetypeBeforeConversion = original.mimetype;\n    }\n}\n\n/**\n * @param {Blob} blob\n * @returns {Promise}\n */\nexport function createDataURL(blob) {\n    return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.addEventListener(\"load\", () => resolve(reader.result));\n        reader.addEventListener(\"abort\", reject);\n        reader.addEventListener(\"error\", reject);\n        reader.readAsDataURL(blob);\n    });\n}\n\n/**\n * @param {String} dataURL\n * @returns {Number} number of bytes represented with base64\n */\nexport function getDataURLBinarySize(dataURL) {\n    // Every 4 bytes of base64 represent 3 bytes.\n    return (dataURL.split(\",\")[1].length / 4) * 3;\n}\n", "import { removeClass, setTagName } from \"./dom\";\n\nexport function getListMode(pnode) {\n    if (![\"UL\", \"OL\"].includes(pnode.tagName)) {\n        return;\n    }\n    if (pnode.tagName === \"OL\") {\n        return \"OL\";\n    }\n    return pnode.classList.contains(\"o_checklist\") ? \"CL\" : \"UL\";\n}\n\n/**\n * Switches the list mode of the given list element.\n *\n * @param {HTMLOListElement|HTMLUListElement} list - The list element to switch the mode of.\n * @param {\"UL\"|\"OL\"|\"CL\"} newMode - The new mode to switch to.\n * @returns {HTMLOListElement|HTMLUListElement} The modified list element.\n */\nexport function switchListMode(list, newMode) {\n    if (getListMode(list) === newMode) {\n        return;\n    }\n    const newTag = newMode === \"CL\" ? \"UL\" : newMode;\n    const newList = setTagName(list, newTag);\n    // Clear list style (@todo @phoenix - why??)\n    newList.style.removeProperty(\"list-style\");\n    for (const li of newList.children) {\n        if (li.style.listStyle !== \"none\") {\n            li.style.listStyle = null;\n            if (!li.style.all) {\n                li.removeAttribute(\"style\");\n            }\n        }\n    }\n    removeClass(newList, \"o_checklist\");\n    if (newMode === \"CL\") {\n        newList.classList.add(\"o_checklist\");\n    }\n    return newList;\n}\n\n/**\n * Converts a list element and its nested elements to the given list mode.\n *\n * @see switchListMode\n * @param {HTMLUListElement|HTMLOListElement|HTMLLIElement} node - HTML element\n * representing a list or list item.\n * @param {string} newMode - Target list mode\n * @returns {HTMLUListElement|HTMLOListElement|HTMLLIElement} node - Modified\n * list element after conversion.\n */\nexport function convertList(node, newMode) {\n    if (![\"UL\", \"OL\", \"LI\"].includes(node.tagName)) {\n        return;\n    }\n    const listMode = getListMode(node);\n    if (listMode && newMode !== listMode) {\n        node = switchListMode(node, newMode);\n    }\n    for (const child of node.children) {\n        convertList(child, newMode);\n    }\n    return node;\n}\n", "/**\n * Transform a 2D point using a projective transformation matrix. Note that\n * this method is only well behaved for points that don't map to infinity!\n *\n * @param {number[][]} matrix - A projective transformation matrix\n * @param {number[]} point - A 2D point\n * @returns The transformed 2D point\n */\nexport function transform([[a, b, c], [d, e, f], [g, h, i]], [x, y]) {\n    let z = g * x + h * y + i;\n    return [(a * x + b * y + c) / z, (d * x + e * y + f) / z];\n}\n\n/**\n * Calculate the inverse of a 3x3 matrix assuming it is invertible.\n *\n * @param {number[][]} matrix - A 3x3 matrix\n * @returns The resulting 3x3 matrix\n */\nfunction invert([[a, b, c], [d, e, f], [g, h, i]]) {\n    const determinant = a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n    return [\n        [(e * i - h * f) / determinant, (h * c - b * i) / determinant, (b * f - e * c) / determinant],\n        [(g * f - d * i) / determinant, (a * i - g * c) / determinant, (d * c - a * f) / determinant],\n        [(d * h - g * e) / determinant, (g * b - a * h) / determinant, (a * e - d * b) / determinant],\n    ];\n}\n\n/**\n * Multiply two 3x3 matrices.\n *\n * @param {number[][]} a - A 3x3 matrix\n * @param {number[][]} b - A 3x3 matrix\n * @returns The resulting 3x3 matrix\n */\nfunction multiply(a, b) {\n    const [[a0, a1, a2], [a3, a4, a5], [a6, a7, a8]] = a;\n    const [[b0, b1, b2], [b3, b4, b5], [b6, b7, b8]] = b;\n    return [\n        [a0 * b0 + a1 * b3 + a2 * b6, a0 * b1 + a1 * b4 + a2 * b7, a0 * b2 + a1 * b5 + a2 * b8],\n        [a3 * b0 + a4 * b3 + a5 * b6, a3 * b1 + a4 * b4 + a5 * b7, a3 * b2 + a4 * b5 + a5 * b8],\n        [a6 * b0 + a7 * b3 + a8 * b6, a6 * b1 + a7 * b4 + a8 * b7, a6 * b2 + a7 * b5 + a8 * b8],\n    ];\n}\n\n/**\n * Find a projective transformation mapping a rectangular area at origin (0,0)\n * with a given width and height to a certain quadrilateral.\n *\n * @param {number} width - The width of the rectangular area\n * @param {number} height - The height of the rectangular area\n * @param {number[][]} quadrilateral - The vertices of the quadrilateral\n * @returns A projective transformation matrix\n */\nexport function getProjective(width, height, [[x0, y0], [x1, y1], [x2, y2], [x3, y3]]) {\n    // Calculate a set of homogeneous coordinates a, b, c of the first\n    // point using the other three points as basis vectors in the\n    // underlying vector space.\n    const denominator = x3 * (y1 - y2) + x1 * (y2 - y3) + x2 * (y3 - y1);\n    const a = (x0 * (y2 - y3) + x2 * (y3 - y0) + x3 * (y0 - y2)) / denominator;\n    const b = (x0 * (y3 - y1) + x3 * (y1 - y0) + x1 * (y0 - y3)) / denominator;\n    const c = (x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) / denominator;\n\n    // The reverse transformation maps the homogeneous coordinates of\n    // the last three corners of the original image onto the basis vectors\n    // while mapping the first corner onto (1, 1, 1). The forward\n    // transformation maps those basis vectors in addition to (1, 1, 1)\n    // onto homogeneous coordinates of the corresponding corners of the\n    // projective image. Combining these together yields the projective\n    // transformation we are looking for.\n    const reverse = invert([[width, -width, 0], [0, -height, height], [1, -1, 1]]);\n    const forward = [[a * x1, b * x2, c * x3], [a * y1, b * y2, c * y3], [a, b, c]];\n\n    return multiply(forward, reverse);\n}\n\n/**\n * Find an affine transformation matrix that exactly maps the vertices of a\n * triangle to their corresponding images of a projective transformation. The\n * resulting transformation will be an approximation of the projective\n * transformation for the area inside the triangle.\n *\n * @param {number[][]} projective - A projective transformation matrix\n * @param {number[][]} triangle - The vertices of a triangle\n * @returns - An affine transformation matrix\n */\nexport function getAffineApproximation(projective, [[x0, y0], [x1, y1], [x2, y2]]) {\n    const a = transform(projective, [x0, y0]);\n    const b = transform(projective, [x1, y1]);\n    const c = transform(projective, [x2, y2]);\n\n    return multiply(\n        [[a[0], b[0], c[0]], [a[1], b[1], c[1]], [1, 1, 1]],\n        invert([[x0, x1, x2], [y0, y1, y2], [1, 1, 1]]),\n    );\n}\n", "// Position and sizes\n//------------------------------------------------------------------------------\n\nexport const DIRECTIONS = {\n    LEFT: false,\n    RIGHT: true,\n};\n\n/**\n * @param {Node} node\n * @returns {[HTMLElement, number]}\n */\nexport function leftPos(node) {\n    return [node.parentElement, childNodeIndex(node)];\n}\n/**\n * @param {Node} node\n * @returns {[HTMLElement, number]}\n */\nexport function rightPos(node) {\n    return [node.parentElement, childNodeIndex(node) + 1];\n}\n/**\n * @param {Node} node\n * @returns {[HTMLElement, number, HTMLElement, number]}\n */\nexport function boundariesOut(node) {\n    const index = childNodeIndex(node);\n    return [node.parentElement, index, node.parentElement, index + 1];\n}\n/**\n * @param {Node} node\n * @returns {[HTMLElement, number, HTMLElement, number]}\n */\nexport function boundariesIn(node) {\n    return [node, 0, node, nodeSize(node)];\n}\n/**\n * @param {Node} node\n * @returns {[Node, number]}\n */\nexport function startPos(node) {\n    return [node, 0];\n}\n/**\n * @param {Node} node\n * @returns {[Node, number]}\n */\nexport function endPos(node) {\n    return [node, nodeSize(node)];\n}\n/**\n * Returns the given node's position relative to its parent (= its index in the\n * child nodes of its parent).\n *\n * @param {Node} node\n * @returns {number}\n */\nexport function childNodeIndex(node) {\n    let i = 0;\n    while (node.previousSibling) {\n        i++;\n        node = node.previousSibling;\n    }\n    return i;\n}\n/**\n * Returns the size of the node = the number of characters for text nodes and\n * the number of child nodes for element nodes.\n *\n * @param {Node} node\n * @returns {number}\n */\nexport function nodeSize(node) {\n    const isTextNode = node.nodeType === Node.TEXT_NODE;\n    return isTextNode ? node.length : node.childNodes.length;\n}\n", "/* eslint-disable */\n\nconst tldWhitelist = [\n    'com', 'net', 'org', 'ac', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'an',\n    'ao', 'aq', 'ar', 'as', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd',\n    'be', 'bf', 'bg', 'bh', 'bi', 'bj', 'bl', 'bm', 'bn', 'bo', 'br', 'bq',\n    'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch',\n    'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cr', 'cs', 'cu', 'cv', 'cw', 'cx',\n    'cy', 'cz', 'dd', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'ee', 'eg',\n    'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga',\n    'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gp', 'gq',\n    'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu',\n    'id', 'ie', 'il', 'im', 'in', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm',\n    'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky',\n    'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly',\n    'ma', 'mc', 'md', 'me', 'mf', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo',\n    'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na',\n    'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om',\n    'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt',\n    'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd',\n    'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'ss',\n    'st', 'su', 'sv', 'sx', 'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj',\n    'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua',\n    'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn',\n    'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm', 'zr', 'zw', 'co\\\\.uk'];\n\nconst urlRegexBase = `|(?:www.))[-a-zA-Z0-9@:%._\\\\+~#=]{2,256}\\\\.[a-zA-Z][a-zA-Z0-9]{1,62}|(?:[-a-zA-Z0-9@:%._\\\\+~#=]{2,256}\\\\.(?:${tldWhitelist.join('|')})\\\\b))(?:(?:[/?#])[^\\\\s]*[^!.,})\\\\]'\"\\\\s]|(?:[^!(){}.,[\\\\]'\"\\\\s]+))?`;\nconst httpCapturedRegex= `(https?:\\\\/\\\\/)`;\n\nexport const URL_REGEX = new RegExp(`((?:(?:${httpCapturedRegex}${urlRegexBase})`, 'i');\n", "export const resourceSequenceSymbol = Symbol(\"resourceSequence\");\n\nexport function withSequence(sequenceNumber, object) {\n    return {\n        [resourceSequenceSymbol]: sequenceNumber,\n        object,\n    };\n}\n", "import { isBlock } from \"./blocks\";\nimport { isPhrasingContent } from \"../utils/dom_info\";\n\n// @todo @phoenix: consider using the wrapInlinesInBlocks utils instead.\n\nexport function initElementForEdition(element, options = {}) {\n    const document = element.ownerDocument;\n    // Detect if the editable base element contain orphan inline nodes. If\n    // so we transform the base element HTML to put those orphans inside\n    // `<p>` containers.\n    const orphanInlineChildNodes = [...element.childNodes].find(\n        (n) => !isBlock(n) && (n.nodeType === Node.ELEMENT_NODE || n.textContent.trim() !== \"\")\n    );\n    if (orphanInlineChildNodes && !options.allowInlineAtRoot) {\n        const childNodes = [...element.childNodes];\n        const blockMap = new WeakMap();\n        for (const node of childNodes) {\n            blockMap.set(node, isBlock(node));\n        }\n        const newChildren = [];\n        let currentBlock = document.createElement(\"DIV\");\n        let hasOnlyPhrasingContent = true;\n        currentBlock.style.marginBottom = \"0\";\n        for (let i = 0; i < childNodes.length; i++) {\n            const node = childNodes[i];\n            const nodeIsBlock = blockMap.get(node);\n            const nodeIsBR = node.nodeName === \"BR\";\n            // Append to the P unless child is block or an unneeded BR.\n            if (!(nodeIsBlock || (nodeIsBR && currentBlock.hasChildNodes()))) {\n                currentBlock.append(node);\n                if (!isPhrasingContent(node)) {\n                    hasOnlyPhrasingContent = false;\n                }\n            }\n            // Break paragraphs on blocks and BR.\n            if (nodeIsBlock || nodeIsBR || childNodes.length === i + 1) {\n                if (hasOnlyPhrasingContent) {\n                    const block = document.createElement(\"P\");\n                    block.style.marginBottom = \"0\";\n                    block.replaceChildren(...currentBlock.childNodes);\n                    currentBlock = block;\n                }\n                // Ensure we don't add an empty P or a P containing only\n                // formating spaces that should not be visible.\n                if (currentBlock.hasChildNodes() && currentBlock.innerHTML.trim() !== \"\") {\n                    newChildren.push(currentBlock);\n                }\n                currentBlock = document.createElement(\"DIV\");\n                currentBlock.style.marginBottom = \"0\";\n                hasOnlyPhrasingContent = true;\n                // Append block children directly to the template.\n                if (nodeIsBlock) {\n                    newChildren.push(node);\n                }\n            }\n        }\n        element.replaceChildren(...newChildren);\n    }\n}\n\nexport function fixInvalidHTML(content) {\n    const regex = /<\\s*(a|strong|t)[^<]*?\\/\\s*>/g;\n    return content.replace(regex, (match, g0) => match.replace(/\\/\\s*>/, `></${g0}>`));\n}\n", "import { closestBlock, isBlock } from \"./blocks\";\nimport {\n    isContentEditable,\n    isNotEditableNode,\n    isSelfClosingElement,\n    nextLeaf,\n    previousLeaf,\n} from \"./dom_info\";\nimport { isFakeLineBreak } from \"./dom_state\";\nimport { closestElement, createDOMPathGenerator } from \"./dom_traversal\";\nimport {\n    DIRECTIONS,\n    childNodeIndex,\n    endPos,\n    leftPos,\n    nodeSize,\n    rightPos,\n    startPos,\n} from \"./position\";\n\n/**\n * @typedef { import(\"./selection_plugin\").EditorSelection } EditorSelection\n */\n\n/**\n * From selection position, checks if it is left-to-right or right-to-left.\n *\n * @param {Node} anchorNode\n * @param {number} anchorOffset\n * @param {Node} focusNode\n * @param {number} focusOffset\n * @returns {boolean} the direction of the current range if the selection not is collapsed | false\n */\nexport function getCursorDirection(anchorNode, anchorOffset, focusNode, focusOffset) {\n    if (anchorNode === focusNode) {\n        if (anchorOffset === focusOffset) {\n            return false;\n        }\n        return anchorOffset < focusOffset ? DIRECTIONS.RIGHT : DIRECTIONS.LEFT;\n    }\n    return anchorNode.compareDocumentPosition(focusNode) & Node.DOCUMENT_POSITION_FOLLOWING\n        ? DIRECTIONS.RIGHT\n        : DIRECTIONS.LEFT;\n}\n\n/**\n * @param {EditorSelection} selection\n * @param {string} selector\n */\nexport function findInSelection(selection, selector) {\n    const selectorInStartAncestors = closestElement(selection.startContainer, selector);\n    if (selectorInStartAncestors) {\n        return selectorInStartAncestors;\n    } else {\n        const commonElementAncestor = closestElement(selection.commonAncestorContainer);\n        return (\n            commonElementAncestor &&\n            [...commonElementAncestor.querySelectorAll(selector)].find((node) =>\n                selection.intersectsNode(node)\n            )\n        );\n    }\n}\n\nconst leftLeafOnlyInScopeNotBlockEditablePath = createDOMPathGenerator(DIRECTIONS.LEFT, {\n    leafOnly: true,\n    inScope: true,\n    stopTraverseFunction: (node) => isNotEditableNode(node) || isBlock(node),\n    stopFunction: (node) => isNotEditableNode(node) || isBlock(node),\n});\n\nconst rightLeafOnlyInScopeNotBlockEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    inScope: true,\n    stopTraverseFunction: (node) => isNotEditableNode(node) || isBlock(node),\n    stopFunction: (node) => isNotEditableNode(node) || isBlock(node),\n});\n\nexport function normalizeSelfClosingElement(node, offset) {\n    if (isSelfClosingElement(node)) {\n        // Cannot put cursor inside those elements, put it after instead.\n        [node, offset] = rightPos(node);\n    }\n    return [node, offset];\n}\n\nexport function normalizeNotEditableNode(node, offset, position = \"right\") {\n    const editable = closestElement(node, \".odoo-editor-editable\");\n    let closest = closestElement(node);\n    while (closest && closest !== editable && !closest.isContentEditable) {\n        [node, offset] = position === \"right\" ? rightPos(node) : leftPos(node);\n        closest = node;\n    }\n    return [node, offset];\n}\n\nexport function normalizeCursorPosition(node, offset, position = \"right\") {\n    [node, offset] = normalizeSelfClosingElement(node, offset);\n    [node, offset] = normalizeNotEditableNode(node, offset, position);\n    // todo @phoenix: we should maybe remove it\n    // // Be permissive about the received offset.\n    // offset = Math.min(Math.max(offset, 0), nodeSize(node));\n    return [node, offset];\n}\n\nexport function normalizeFakeBR(node, offset) {\n    const prevNode = node.nodeType === Node.ELEMENT_NODE && node.childNodes[offset - 1];\n    if (prevNode && prevNode.nodeName === \"BR\" && isFakeLineBreak(prevNode)) {\n        // If trying to put the cursor on the right of a fake line break, put\n        // it before instead.\n        offset--;\n    }\n    return [node, offset];\n}\n\n/**\n * From a given position, returns the normalized version.\n *\n * E.g. <b>abc</b>[]def -> <b>abc[]</b>def\n *\n * @param {Node} node\n * @param {number} offset\n * @returns { [Node, number] }\n */\nexport function normalizeDeepCursorPosition(node, offset) {\n    // Put the cursor in deepest inline node around the given position if\n    // possible.\n    let el;\n    let elOffset;\n    if (node.nodeType === Node.ELEMENT_NODE) {\n        el = node;\n        elOffset = offset;\n    } else if (node.nodeType === Node.TEXT_NODE) {\n        if (offset === 0) {\n            el = node.parentNode;\n            elOffset = childNodeIndex(node);\n        } else if (offset === node.length) {\n            el = node.parentNode;\n            elOffset = childNodeIndex(node) + 1;\n        }\n    }\n    if (el) {\n        const leftInlineNode = leftLeafOnlyInScopeNotBlockEditablePath(el, elOffset).next().value;\n        let leftVisibleEmpty = false;\n        if (leftInlineNode) {\n            leftVisibleEmpty =\n                isSelfClosingElement(leftInlineNode) || !isContentEditable(leftInlineNode);\n            [node, offset] = leftVisibleEmpty ? rightPos(leftInlineNode) : endPos(leftInlineNode);\n        }\n        if (!leftInlineNode || leftVisibleEmpty) {\n            const rightInlineNode = rightLeafOnlyInScopeNotBlockEditablePath(el, elOffset).next()\n                .value;\n            if (rightInlineNode) {\n                const closest = closestElement(rightInlineNode);\n                const rightVisibleEmpty =\n                    isSelfClosingElement(rightInlineNode) || !closest || !closest.isContentEditable;\n                if (!(leftVisibleEmpty && rightVisibleEmpty)) {\n                    [node, offset] = rightVisibleEmpty\n                        ? leftPos(rightInlineNode)\n                        : startPos(rightInlineNode);\n                }\n            }\n        }\n    }\n    return [node, offset];\n}\n\nfunction updateCursorBeforeMove(destParent, destIndex, node, cursor) {\n    if (cursor.node === destParent && cursor.offset >= destIndex) {\n        // Update cursor at destination\n        cursor.offset += 1;\n    } else if (cursor.node === node.parentNode) {\n        const childIndex = childNodeIndex(node);\n        // Update cursor at origin\n        if (cursor.offset === childIndex) {\n            // Keep pointing to the moved node\n            [cursor.node, cursor.offset] = [destParent, destIndex];\n        } else if (cursor.offset > childIndex) {\n            cursor.offset -= 1;\n        }\n    }\n}\n\nfunction updateCursorBeforeRemove(node, cursor) {\n    if (node.contains(cursor.node)) {\n        [cursor.node, cursor.offset] = [node.parentNode, childNodeIndex(node)];\n    } else if (cursor.node === node.parentNode && cursor.offset > childNodeIndex(node)) {\n        cursor.offset -= 1;\n    }\n}\n\nfunction updateCursorBeforeUnwrap(node, cursor) {\n    if (cursor.node === node) {\n        [cursor.node, cursor.offset] = [node.parentNode, cursor.offset + childNodeIndex(node)];\n    } else if (cursor.node === node.parentNode && cursor.offset > childNodeIndex(node)) {\n        cursor.offset += nodeSize(node) - 1;\n    }\n}\n\nfunction updateCursorBeforeMergeIntoPreviousSibling(node, cursor) {\n    if (cursor.node === node) {\n        cursor.node = node.previousSibling;\n        cursor.offset += node.previousSibling.childNodes.length;\n    } else if (cursor.node === node.parentNode) {\n        const childIndex = childNodeIndex(node);\n        if (cursor.offset === childIndex) {\n            cursor.node = node.previousSibling;\n            cursor.offset = node.previousSibling.childNodes.length;\n        } else if (cursor.offset > childIndex) {\n            cursor.offset--;\n        }\n    }\n}\n\n/** @typedef {import(\"@html_editor/core/selection_plugin\").Cursor} Cursor */\n\nexport const callbacksForCursorUpdate = {\n    /** @type {(node: Node) => (cursor: Cursor) => void} */\n    remove: (node) => (cursor) => updateCursorBeforeRemove(node, cursor),\n    /** @type {(ref: HTMLElement, node: Node) => (cursor: Cursor) => void} */\n    before: (ref, node) => (cursor) =>\n        updateCursorBeforeMove(ref.parentNode, childNodeIndex(ref), node, cursor),\n    /** @type {(ref: HTMLElement, node: Node) => (cursor: Cursor) => void} */\n    after: (ref, node) => (cursor) =>\n        updateCursorBeforeMove(ref.parentNode, childNodeIndex(ref) + 1, node, cursor),\n    /** @type {(ref: HTMLElement, node: Node) => (cursor: Cursor) => void} */\n    append: (to, node) => (cursor) =>\n        updateCursorBeforeMove(to, to.childNodes.length, node, cursor),\n    /** @type {(ref: HTMLElement, node: Node) => (cursor: Cursor) => void} */\n    prepend: (to, node) => (cursor) => updateCursorBeforeMove(to, 0, node, cursor),\n    /** @type {(node: HTMLElement) => (cursor: Cursor) => void} */\n    unwrap: (node) => (cursor) => updateCursorBeforeUnwrap(node, cursor),\n    /** @type {(node: HTMLElement) => (cursor: Cursor) => void} */\n    merge: (node) => (cursor) => updateCursorBeforeMergeIntoPreviousSibling(node, cursor),\n};\n\n/**\n * @param {Selection} selection\n * @param {\"previous\"|\"next\"} side\n * @param {HTMLElement} editable\n * @returns {string | undefined}\n */\nexport function getAdjacentCharacter(selection, side, editable) {\n    let { focusNode, focusOffset } = selection;\n    const originalBlock = closestBlock(focusNode);\n    let adjacentCharacter;\n    while (!adjacentCharacter && focusNode) {\n        if (side === \"previous\") {\n            // @todo: this might be wrong in the first time, as focus node might not be a leaf.\n            adjacentCharacter = focusOffset > 0 && focusNode.textContent[focusOffset - 1];\n        } else {\n            adjacentCharacter = focusNode.textContent[focusOffset];\n        }\n        if (!adjacentCharacter) {\n            if (side === \"previous\") {\n                focusNode = previousLeaf(focusNode, editable);\n                focusOffset = focusNode && nodeSize(focusNode);\n            } else {\n                focusNode = nextLeaf(focusNode, editable);\n                focusOffset = 0;\n            }\n            const characterIndex = side === \"previous\" ? focusOffset - 1 : focusOffset;\n            adjacentCharacter = focusNode && focusNode.textContent[characterIndex];\n        }\n    }\n    if (!focusNode || !isContentEditable(focusNode) || closestBlock(focusNode) !== originalBlock) {\n        return undefined;\n    }\n    return adjacentCharacter;\n}\n", "import { closestElement } from \"./dom_traversal\";\n\n/**\n * Get the index of the given table row/cell.\n *\n * @private\n * @param {HTMLTableRowElement|HTMLTableCellElement} trOrTd\n * @returns {number}\n */\nexport function getRowIndex(trOrTd) {\n    const tr = closestElement(trOrTd, \"tr\");\n    return tr.rowIndex;\n}\n\n/**\n * Get the index of the given table cell.\n *\n * @private\n * @param {HTMLTableCellElement} td\n * @returns {number}\n */\nexport function getColumnIndex(td) {\n    return td.cellIndex;\n}\n", "export const excalidrawWebsiteDomainList = [\n    \"excalidraw.com\",\n    \"link.excalidraw.com\",\n    \"app.excalidraw.com\",\n];\n\n/**\n * Checks if the given URL contains the specified hostname and returns a reconstructed URL if it does.\n *\n * @param {string} url - The URL to be checked\n * @param {Array} hostname - The hostname to be included in the modified URL\n * @return {string|boolean} The modified URL with the specified hostname included, or false if the URL does not meet the conditions\n */\nexport function checkURL(url, hostnameList) {\n    if (url) {\n        let potentialURL;\n        try {\n            potentialURL = new URL(url);\n        } catch {\n            return false;\n        }\n        if (hostnameList.includes(potentialURL.hostname)) {\n            return `https://${potentialURL.hostname}${potentialURL.pathname}`;\n        }\n    }\n    return false;\n}\n\n/**\n * @param {string} url\n */\nexport function isImageUrl(url) {\n    const urlFileExtention = url.split(\".\").pop();\n    return [\"jpg\", \"jpeg\", \"png\", \"gif\", \"svg\", \"webp\"].includes(urlFileExtention.toLowerCase());\n}\n\n/**\n * @param {string} platform\n * @param {string} videoId\n * @param {Object} params\n * @throws {Error} if the given video config is not recognized\n * @returns {URL}\n */\nexport function getVideoUrl(platform, videoId, params) {\n    let url;\n    switch (platform) {\n        case \"youtube\":\n            url = new URL(`https://www.youtube.com/embed/${videoId}`);\n            break;\n        case \"vimeo\":\n            url = new URL(`https://player.vimeo.com/video/${videoId}`);\n            break;\n        case \"dailymotion\":\n            url = new URL(`https://www.dailymotion.com/embed/video/${videoId}`);\n            break;\n        case \"instagram\":\n            url = new URL(`https://www.instagram.com/p/${videoId}/embed`);\n            break;\n        case \"youku\":\n            url = new URL(`https://player.youku.com/embed/${videoId}`);\n            break;\n        default:\n            throw new Error(`Unsupported platform: ${platform}`);\n    }\n    url.search = new URLSearchParams(params);\n    return url;\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { patch } from \"@web/core/utils/patch\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { ImageSelector } from \"@html_editor/main/media/media_dialog/image_selector\";\n\nimport { UnsplashError } from \"../unsplash_error/unsplash_error\";\nimport { useState } from \"@odoo/owl\";\n\npatch(ImageSelector.prototype, {\n    setup() {\n        super.setup();\n        this.unsplash = useService(\"unsplash\");\n        this.keepLastUnsplash = new KeepLast();\n        this.unsplashState = useState({\n            unsplashRecords: [],\n            isFetchingUnsplash: false,\n            isMaxed: false,\n            unsplashError: null,\n            useUnsplash: true,\n        });\n\n        this.NUMBER_OF_RECORDS_TO_DISPLAY = 30;\n\n        this.errorMessages = {\n            key_not_found: {\n                title: _t(\"Setup Unsplash to access royalty free photos.\"),\n                subtitle: \"\",\n            },\n            401: {\n                title: _t(\"Unauthorized Key\"),\n                subtitle: _t(\"Please check your Unsplash access key and application ID.\"),\n            },\n            403: {\n                title: _t(\"Search is temporarily unavailable\"),\n                subtitle: _t(\n                    \"The max number of searches is exceeded. Please retry in an hour or extend to a better account.\"\n                ),\n            },\n        };\n    },\n\n    get canLoadMore() {\n        if (this.state.searchService === \"all\") {\n            return (\n                super.canLoadMore ||\n                (this.state.needle &&\n                    !this.unsplashState.isMaxed &&\n                    !this.unsplashState.unsplashError)\n            );\n        } else if (this.state.searchService === \"unsplash\") {\n            return (\n                this.state.needle &&\n                !this.unsplashState.isMaxed &&\n                !this.unsplashState.unsplashError\n            );\n        }\n        return super.canLoadMore;\n    },\n\n    get hasContent() {\n        if (this.state.searchService === \"all\") {\n            return super.hasContent || !!this.unsplashState.unsplashRecords.length;\n        } else if (this.state.searchService === \"unsplash\") {\n            return !!this.unsplashState.unsplashRecords.length;\n        }\n        return super.hasContent;\n    },\n\n    get errorTitle() {\n        if (this.errorMessages[this.unsplashState.unsplashError]) {\n            return this.errorMessages[this.unsplashState.unsplashError].title;\n        }\n        return _t(\"Something went wrong\");\n    },\n\n    get errorSubtitle() {\n        if (this.errorMessages[this.unsplashState.unsplashError]) {\n            return this.errorMessages[this.unsplashState.unsplashError].subtitle;\n        }\n        return _t(\"Please check your internet connection or contact administrator.\");\n    },\n\n    get selectedRecordIds() {\n        return this.props.selectedMedia[this.props.id]\n            .filter((media) => media.mediaType === \"unsplashRecord\")\n            .map(({ id }) => id);\n    },\n\n    get isFetching() {\n        return super.isFetching || this.unsplashState.isFetchingUnsplash;\n    },\n\n    get combinedRecords() {\n        /**\n         * Creates an array with alternating elements from two arrays.\n         *\n         * @param {Array} a\n         * @param {Array} b\n         * @returns {Array} alternating elements from a and b, starting with\n         *     an element of a\n         */\n        function alternate(a, b) {\n            return [a.map((v, i) => (i < b.length ? [v, b[i]] : v)), b.slice(a.length)].flat(2);\n        }\n        return alternate(this.unsplashState.unsplashRecords, this.state.libraryMedia);\n    },\n\n    get allAttachments() {\n        return [...super.allAttachments, ...this.unsplashState.unsplashRecords];\n    },\n\n    async fetchUnsplashRecords(offset) {\n        if (!this.state.needle) {\n            return { records: [], isMaxed: false };\n        }\n        this.unsplashState.isFetchingUnsplash = true;\n        try {\n            const { isMaxed, images } = await this.unsplash.getImages(\n                this.state.needle,\n                offset,\n                this.NUMBER_OF_RECORDS_TO_DISPLAY,\n                this.props.orientation\n            );\n            this.unsplashState.isFetchingUnsplash = false;\n            this.unsplashState.unsplashError = false;\n            // Ignore duplicates.\n            const existingIds = this.unsplashState.unsplashRecords.map((existing) => existing.id);\n            const newImages = images.filter((record) => !existingIds.includes(record.id));\n            const records = newImages.map((record) => {\n                const url = new URL(record.urls.regular);\n                // In small windows, row height could get quite a bit larger than the min, so we keep some leeway.\n                url.searchParams.set(\"h\", 2 * this.MIN_ROW_HEIGHT);\n                url.searchParams.delete(\"w\");\n                return Object.assign({}, record, {\n                    url: url.toString(),\n                    mediaType: \"unsplashRecord\",\n                });\n            });\n            return { isMaxed, records };\n        } catch (e) {\n            this.unsplashState.isFetchingUnsplash = false;\n            if (e === \"no_access\") {\n                this.unsplashState.useUnsplash = false;\n            } else {\n                this.unsplashState.unsplashError = e;\n            }\n            return { records: [], isMaxed: true };\n        }\n    },\n\n    async loadMore(...args) {\n        await super.loadMore(...args);\n        return this.keepLastUnsplash\n            .add(this.fetchUnsplashRecords(this.unsplashState.unsplashRecords.length))\n            .then(({ records, isMaxed }) => {\n                // This is never reached if another search or loadMore occurred.\n                this.unsplashState.unsplashRecords.push(...records);\n                this.unsplashState.isMaxed = isMaxed;\n            });\n    },\n\n    async search(...args) {\n        await super.search(...args);\n        await this.searchUnsplash();\n    },\n\n    async searchUnsplash() {\n        if (!this.state.needle) {\n            this.unsplashState.unsplashError = false;\n            this.unsplashState.unsplashRecords = [];\n            this.unsplashState.isMaxed = false;\n        }\n        return this.keepLastUnsplash\n            .add(this.fetchUnsplashRecords(0))\n            .then(({ records, isMaxed }) => {\n                // This is never reached if a new search occurred.\n                this.unsplashState.unsplashRecords = records;\n                this.unsplashState.isMaxed = isMaxed;\n            });\n    },\n\n    async onClickRecord(media) {\n        this.props.selectMedia({ ...media, mediaType: \"unsplashRecord\", query: this.state.needle });\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    },\n\n    async submitCredentials(key, appId) {\n        this.unsplashState.unsplashError = null;\n        await rpc(\"/web_unsplash/save_unsplash\", { key, appId });\n        await this.searchUnsplash();\n    },\n});\n\nImageSelector.components = {\n    ...ImageSelector.components,\n    UnsplashError,\n};\n", "import { MediaDialog, TABS } from \"@html_editor/main/media/media_dialog/media_dialog\";\nimport { patch } from \"@web/core/utils/patch\";\nimport { useService } from \"@web/core/utils/hooks\";\n\npatch(MediaDialog.prototype, {\n    setup() {\n        super.setup();\n        this.unsplashService = useService(\"unsplash\");\n    },\n\n    async save() {\n        const selectedImages = this.selectedMedia[TABS.IMAGES.id];\n        if (selectedImages) {\n            const unsplashRecords = selectedImages.filter(\n                (media) => media.mediaType === \"unsplashRecord\"\n            );\n            if (unsplashRecords.length) {\n                await this.unsplashService.uploadUnsplashRecords(\n                    unsplashRecords,\n                    { resModel: this.props.resModel, resId: this.props.resId },\n                    (attachments) => {\n                        this.selectedMedia[TABS.IMAGES.id] = this.selectedMedia[\n                            TABS.IMAGES.id\n                        ].filter((media) => media.mediaType !== \"unsplashRecord\");\n                        this.selectedMedia[TABS.IMAGES.id] = this.selectedMedia[\n                            TABS.IMAGES.id\n                        ].concat(\n                            attachments.map((attachment) => ({\n                                ...attachment,\n                                mediaType: \"attachment\",\n                            }))\n                        );\n                    }\n                );\n            }\n        }\n        return super.save(...arguments);\n    },\n});\n", "import { Component, useState } from \"@odoo/owl\";\n\nexport class UnsplashCredentials extends Component {\n    static template = \"web_unsplash.UnsplashCredentials\";\n    static props = {\n        submitCredentials: Function,\n        hasCredentialsError: Boolean,\n    };\n    setup() {\n        this.state = useState({\n            key: \"\",\n            appId: \"\",\n            hasKeyError: this.props.hasCredentialsError,\n            hasAppIdError: this.props.hasCredentialsError,\n        });\n    }\n\n    submitCredentials() {\n        if (this.state.key === \"\") {\n            this.state.hasKeyError = true;\n        } else if (this.state.appId === \"\") {\n            this.state.hasAppIdError = true;\n        } else {\n            this.props.submitCredentials(this.state.key, this.state.appId);\n        }\n    }\n}\n", "import { Component } from \"@odoo/owl\";\nimport { UnsplashCredentials } from \"../unsplash_credentials/unsplash_credentials\";\n\nexport class UnsplashError extends Component {\n    static template = \"web_unsplash.UnsplashError\";\n    static components = {\n        UnsplashCredentials,\n    };\n    static props = {\n        title: String,\n        subtitle: String,\n        showCredentials: Boolean,\n        submitCredentials: { type: Function, optional: true },\n        hasCredentialsError: { type: Boolean, optional: true },\n    };\n}\n", "import { rpc } from \"@web/core/network/rpc\";\nimport { registry } from \"@web/core/registry\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { AUTOCLOSE_DELAY } from \"@html_editor/main/media/media_dialog/upload_progress_toast/upload_service\";\n\nexport const unsplashService = {\n    dependencies: [\"upload\"],\n    async start(env, { upload }) {\n        const _cache = {};\n        return {\n            async uploadUnsplashRecords(records, { resModel, resId }, onUploaded) {\n                upload.incrementId();\n                const file = upload.addFile({\n                    id: upload.fileId,\n                    name:\n                        records.length > 1\n                            ? _t(\"Uploading %(count)s '%(query)s' images.\", {\n                                  count: records.length,\n                                  query: records[0].query,\n                              })\n                            : _t(\"Uploading '%s' image.\", records[0].query),\n                });\n\n                try {\n                    const urls = {};\n                    for (const record of records) {\n                        const _1920Url = new URL(record.urls.regular);\n                        _1920Url.searchParams.set(\"w\", \"1920\");\n                        urls[record.id] = {\n                            url: _1920Url.href,\n                            download_url: record.links.download_location,\n                            description: record.alt_description,\n                        };\n                    }\n\n                    const xhr = new XMLHttpRequest();\n                    xhr.upload.addEventListener(\"progress\", (ev) => {\n                        const rpcComplete = (ev.loaded / ev.total) * 100;\n                        file.progress = rpcComplete;\n                    });\n                    xhr.upload.addEventListener(\"load\", function () {\n                        // Don't show yet success as backend code only starts now\n                        file.progress = 100;\n                    });\n                    const attachments = await rpc(\n                        \"/web_unsplash/attachment/add\",\n                        {\n                            res_id: resId,\n                            res_model: resModel,\n                            unsplashurls: urls,\n                            query: records[0].query,\n                        },\n                        { xhr }\n                    );\n\n                    if (attachments.error) {\n                        file.hasError = true;\n                        file.errorMessage = attachments.error;\n                    } else {\n                        file.uploaded = true;\n                        await onUploaded(attachments);\n                    }\n                    setTimeout(() => upload.deleteFile(file.id), AUTOCLOSE_DELAY);\n                } catch (error) {\n                    file.hasError = true;\n                    setTimeout(() => upload.deleteFile(file.id), AUTOCLOSE_DELAY);\n                    throw error;\n                }\n            },\n\n            async getImages(query, offset = 0, pageSize = 30, orientation) {\n                const from = offset;\n                const to = offset + pageSize;\n                // Use orientation in the cache key to not show images in cache\n                // when using the same query word but changing the orientation\n                let cachedData = orientation ? _cache[query + orientation] : _cache[query];\n\n                if (\n                    cachedData &&\n                    (cachedData.images.length >= to ||\n                        (cachedData.totalImages !== 0 && cachedData.totalImages < to))\n                ) {\n                    return {\n                        images: cachedData.images.slice(from, to),\n                        isMaxed: to > cachedData.totalImages,\n                    };\n                }\n                cachedData = await this._fetchImages(query, orientation);\n                return {\n                    images: cachedData.images.slice(from, to),\n                    isMaxed: to > cachedData.totalImages,\n                };\n            },\n            /**\n             * Fetches images from unsplash and stores it in cache\n             */\n            async _fetchImages(query, orientation) {\n                const key = orientation ? query + orientation : query;\n                if (!_cache[key]) {\n                    _cache[key] = {\n                        images: [],\n                        maxPages: 0,\n                        totalImages: 0,\n                        pageCached: 0,\n                    };\n                }\n                const cachedData = _cache[key];\n                const payload = {\n                    query: query,\n                    page: cachedData.pageCached + 1,\n                    per_page: 30, // max size from unsplash API\n                };\n                if (orientation) {\n                    payload.orientation = orientation;\n                }\n                const result = await rpc(\"/web_unsplash/fetch_images\", payload);\n                if (result.error) {\n                    return Promise.reject(result.error);\n                }\n                cachedData.pageCached++;\n                cachedData.images.push(...result.results);\n                cachedData.maxPages = result.total_pages;\n                cachedData.totalImages = result.total;\n                return cachedData;\n            },\n        };\n    },\n};\n\nregistry.category(\"services\").add(\"unsplash\", unsplashService);\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { Attachment, FileSelector, IMAGE_MIMETYPES } from './file_selector';\n\nexport class DocumentAttachment extends Attachment {\n    static template = \"web_editor.DocumentAttachment\";\n}\n\nexport class DocumentSelector extends FileSelector {\n    static mediaSpecificClasses = [\"o_image\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [];\n    static tagNames = [\"A\"];\n    static attachmentsListTemplate = \"web_editor.DocumentsListTemplate\";\n    static components = {\n        ...FileSelector.components,\n        DocumentAttachment,\n    };\n\n    setup() {\n        super.setup();\n\n        this.uploadText = _t(\"Upload a document\");\n        this.urlPlaceholder = \"https://www.odoo.com/mydocument\";\n        this.addText = _t(\"Add URL\");\n        this.searchPlaceholder = _t(\"Search a document\");\n        this.allLoadedText = _t(\"All documents have been loaded\");\n    }\n\n    get attachmentsDomain() {\n        const domain = super.attachmentsDomain;\n        domain.push(['mimetype', 'not in', IMAGE_MIMETYPES]);\n        // The assets should not be part of the documents.\n        // All assets begin with '/web/assets/', see _get_asset_template_url().\n        domain.unshift('&', '|', ['url', '=', null], '!', ['url', '=like', '/web/assets/%']);\n        return domain;\n    }\n\n    async onClickDocument(document) {\n        this.selectAttachment(document);\n        await this.props.save();\n    }\n\n    async fetchAttachments(...args) {\n        const attachments = await super.fetchAttachments(...args);\n\n        if (this.selectInitialMedia()) {\n            for (const attachment of attachments) {\n                if (`/web/content/${attachment.id}` === this.props.media.getAttribute('href').replace(/[?].*/, '')) {\n                    this.selectAttachment(attachment);\n                }\n            }\n        }\n        return attachments;\n    }\n\n    /**\n     * Utility method used by the MediaDialog component.\n     */\n    static async createElements(selectedMedia, { orm }) {\n        return Promise.all(selectedMedia.map(async attachment => {\n            const linkEl = document.createElement('a');\n            let href = `/web/content/${encodeURIComponent(attachment.id)}?unique=${encodeURIComponent(attachment.checksum)}&download=true`;\n            if (!attachment.public) {\n                let accessToken = attachment.access_token;\n                if (!accessToken) {\n                    [accessToken] = await orm.call(\n                        'ir.attachment',\n                        'generate_access_token',\n                        [attachment.id],\n                    );\n                }\n                href += `&access_token=${encodeURIComponent(accessToken)}`;\n            }\n            linkEl.href = href;\n            linkEl.title = attachment.name;\n            linkEl.dataset.mimetype = attachment.mimetype;\n            return linkEl;\n        }));\n    }\n}\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useService } from '@web/core/utils/hooks';\nimport { ConfirmationDialog } from '@web/core/confirmation_dialog/confirmation_dialog';\nimport { Dialog } from '@web/core/dialog/dialog';\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { useDebounced } from \"@web/core/utils/timing\";\nimport { SearchMedia } from './search_media';\n\nimport { Component, xml, useState, useRef, onWillStart, useEffect } from \"@odoo/owl\";\n\nexport const IMAGE_MIMETYPES = ['image/jpg', 'image/jpeg', 'image/jpe', 'image/png', 'image/svg+xml', 'image/gif', 'image/webp'];\nexport const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.jpe', '.png', '.svg', '.gif', '.webp'];\n\nclass RemoveButton extends Component {\n    static template = xml`<i class=\"fa fa-trash o_existing_attachment_remove position-absolute top-0 end-0 p-2 bg-white-25 cursor-pointer opacity-0 opacity-100-hover z-1 transition-base\" t-att-title=\"removeTitle\" role=\"img\" t-att-aria-label=\"removeTitle\" t-on-click=\"this.remove\"/>`;\n    static props = [\"model?\", \"remove\"];\n    setup() {\n        this.removeTitle = _t(\"This file is attached to the current record.\");\n        if (this.props.model === 'ir.ui.view') {\n            this.removeTitle = _t(\"This file is a public view attachment.\");\n        }\n    }\n\n    remove(ev) {\n        ev.stopPropagation();\n        this.props.remove();\n    }\n}\n\nexport class AttachmentError extends Component {\n    static components = { Dialog };\n    static template = xml`\n        <Dialog title=\"title\">\n            <div class=\"form-text\">\n                <p>The image could not be deleted because it is used in the\n                    following pages or views:</p>\n                <ul t-foreach=\"props.views\"  t-as=\"view\" t-key=\"view.id\">\n                    <li>\n                        <a t-att-href=\"'/odoo/ir.ui.view/' + window.encodeURIComponent(view.id)\">\n                            <t t-esc=\"view.name\"/>\n                        </a>\n                    </li>\n                </ul>\n            </div>\n            <t t-set-slot=\"footer\">\n                <button class=\"btn btn-primary\" t-on-click=\"() => this.props.close()\">\n                    Ok\n                </button>\n            </t>\n        </Dialog>`;\n    static props = [\"views\", \"close\"];\n    setup() {\n        this.title = _t(\"Alert\");\n    }\n}\n\nexport class Attachment extends Component {\n    static template = \"\";\n    static components = {\n        RemoveButton,\n    };\n    static props = [\"*\"];\n    setup() {\n        this.dialogs = useService('dialog');\n    }\n\n    remove() {\n        this.dialogs.add(ConfirmationDialog, {\n            body: _t(\"Are you sure you want to delete this file?\"),\n            confirm: async () => {\n                const prevented = await rpc('/web_editor/attachment/remove', {\n                    ids: [this.props.id],\n                });\n                if (!Object.keys(prevented).length) {\n                    this.props.onRemoved(this.props.id);\n                } else {\n                    this.dialogs.add(AttachmentError, {\n                        views: prevented[this.props.id],\n                    });\n                }\n            },\n        });\n    }\n}\n\nexport class FileSelectorControlPanel extends Component {\n    static template = \"web_editor.FileSelectorControlPanel\";\n    static components = {\n        SearchMedia,\n    };\n    static props = {\n        uploadUrl: Function,\n        validateUrl: Function,\n        uploadFiles: Function,\n        changeSearchService: Function,\n        changeShowOptimized: Function,\n        search: Function,\n        accept: {type: String, optional: true},\n        addText: {type: String, optional: true},\n        multiSelect: {type: true, optional: true},\n        needle: {type: String, optional: true},\n        searchPlaceholder: {type: String, optional: true},\n        searchService: {type: String, optional: true},\n        showOptimized: {type: Boolean, optional: true},\n        showOptimizedOption: {type: String, optional: true},\n        uploadText: {type: String, optional: true},\n        urlPlaceholder: {type: String, optional: true},\n        urlWarningTitle: {type: String, optional: true},\n        useMediaLibrary: {type: Boolean, optional: true},\n        useUnsplash: {type: Boolean, optional: true},\n    };\n    setup() {\n        this.state = useState({\n            showUrlInput: false,\n            urlInput: '',\n            isValidUrl: false,\n            isValidFileFormat: false,\n            isValidatingUrl: false,\n        });\n        this.debouncedValidateUrl = useDebounced(this.props.validateUrl, 500);\n\n        this.fileInput = useRef('file-input');\n    }\n\n    get showSearchServiceSelect() {\n        return this.props.searchService && this.props.needle;\n    }\n\n    get enableUrlUploadClick() {\n        return !this.state.showUrlInput || (this.state.urlInput && this.state.isValidUrl && this.state.isValidFileFormat);\n    }\n\n    async onUrlUploadClick() {\n        if (!this.state.showUrlInput) {\n            this.state.showUrlInput = true;\n        } else {\n            await this.props.uploadUrl(this.state.urlInput);\n            this.state.urlInput = '';\n        }\n    }\n\n    async onUrlInput(ev) {\n        this.state.isValidatingUrl = true;\n        const { isValidUrl, isValidFileFormat } = await this.debouncedValidateUrl(ev.target.value);\n        this.state.isValidFileFormat = isValidFileFormat;\n        this.state.isValidUrl = isValidUrl;\n        this.state.isValidatingUrl = false;\n    }\n\n    onClickUpload() {\n        this.fileInput.el.click();\n    }\n\n    async onChangeFileInput() {\n        const inputFiles = this.fileInput.el.files;\n        if (!inputFiles.length) {\n            return;\n        }\n        await this.props.uploadFiles(inputFiles);\n        const fileInputEl = this.fileInput.el;\n        if (fileInputEl) {\n            fileInputEl.value = \"\";\n        }\n    }\n}\n\nexport class FileSelector extends Component {\n    static template = \"web_editor.FileSelector\";\n    static components = {\n        FileSelectorControlPanel,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.notificationService = useService(\"notification\");\n        this.orm = useService('orm');\n        this.uploadService = useService('upload');\n        this.keepLast = new KeepLast();\n\n        this.loadMoreButtonRef = useRef('load-more-button');\n        this.existingAttachmentsRef = useRef(\"existing-attachments\");\n\n        this.state = useState({\n            attachments: [],\n            canScrollAttachments: false,\n            canLoadMoreAttachments: false,\n            isFetchingAttachments: false,\n            needle: '',\n        });\n\n        this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY = 30;\n\n        onWillStart(async () => {\n            this.state.attachments = await this.fetchAttachments(this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY, 0);\n        });\n\n        this.debouncedOnScroll = useDebounced(this.updateScroll, 15);\n        this.debouncedScrollUpdate = useDebounced(this.updateScroll, 500);\n\n        useEffect(\n            (modalEl) => {\n                if (modalEl) {\n                    modalEl.addEventListener(\"scroll\", this.debouncedOnScroll);\n                    return () => {\n                        modalEl.removeEventListener(\"scroll\", this.debouncedOnScroll);\n                    };\n                }\n            },\n            () => [this.props.modalRef.el?.querySelector(\"main.modal-body\")]\n        );\n\n        useEffect(\n            () => {\n                // Updating the scroll button each time the attachments change.\n                // Hiding the \"Load more\" button to prevent it from flickering.\n                this.loadMoreButtonRef.el.classList.add(\"o_hide_loading\");\n                this.state.canScrollAttachments = false;\n                this.debouncedScrollUpdate();\n            },\n            () => [this.allAttachments.length]);\n    }\n\n    get canLoadMore() {\n        return this.state.canLoadMoreAttachments;\n    }\n\n    get hasContent() {\n        return this.state.attachments.length;\n    }\n\n    get isFetching() {\n        return this.state.isFetchingAttachments;\n    }\n\n    get selectedAttachmentIds() {\n        return this.props.selectedMedia[this.props.id].filter(media => media.mediaType === 'attachment').map(({ id }) => id);\n    }\n\n    get attachmentsDomain() {\n        const domain = [\n            '&',\n            ['res_model', '=', this.props.resModel],\n            ['res_id', '=', this.props.resId || 0]\n        ];\n        domain.unshift('|', ['public', '=', true]);\n        domain.push(['name', 'ilike', this.state.needle]);\n        return domain;\n    }\n\n    get allAttachments() {\n        return this.state.attachments;\n    }\n\n    validateUrl(url) {\n        const path = url.split('?')[0];\n        const isValidUrl = /^.+\\..+$/.test(path); // TODO improve\n        const isValidFileFormat = true;\n        return { isValidUrl, isValidFileFormat, path };\n    }\n\n    async fetchAttachments(limit, offset) {\n        this.state.isFetchingAttachments = true;\n        let attachments = [];\n        try {\n            attachments = await this.orm.call(\n                'ir.attachment',\n                'search_read',\n                [],\n                {\n                    domain: this.attachmentsDomain,\n                    fields: ['name', 'mimetype', 'description', 'checksum', 'url', 'type', 'res_id', 'res_model', 'public', 'access_token', 'image_src', 'image_width', 'image_height', 'original_id'],\n                    order: 'id desc',\n                    // Try to fetch first record of next page just to know whether there is a next page.\n                    limit,\n                    offset,\n                }\n            );\n            attachments.forEach(attachment => attachment.mediaType = 'attachment');\n        } catch (e) {\n            // Reading attachments as a portal user is not permitted and will raise\n            // an access error so we catch the error silently and don't return any\n            // attachment so he can still use the wizard and upload an attachment\n            if (e.exceptionName !== 'odoo.exceptions.AccessError') {\n                throw e;\n            }\n        }\n        this.state.canLoadMoreAttachments = attachments.length >= this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY;\n        this.state.isFetchingAttachments = false;\n        return attachments;\n    }\n\n    async handleLoadMore() {\n        await this.loadMore();\n    }\n\n    async loadMore() {\n        return this.keepLast.add(this.fetchAttachments(this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY, this.state.attachments.length)).then((newAttachments) => {\n            // This is never reached if another search or loadMore occurred.\n            this.state.attachments.push(...newAttachments);\n        });\n    }\n\n    async handleSearch(needle) {\n        await this.search(needle);\n    }\n\n    async search(needle) {\n        // Prepare in case loadMore results are obtained instead.\n        this.state.attachments = [];\n        // Fetch attachments relies on the state's needle.\n        this.state.needle = needle;\n        return this.keepLast.add(this.fetchAttachments(this.NUMBER_OF_ATTACHMENTS_TO_DISPLAY, 0)).then((attachments) => {\n            // This is never reached if a new search occurred.\n            this.state.attachments = attachments;\n        });\n    }\n\n    async uploadFiles(files) {\n        await this.uploadService.uploadFiles(files, { resModel: this.props.resModel, resId: this.props.resId }, attachment => this.onUploaded(attachment));\n    }\n\n    async uploadUrl(url) {\n        await this.uploadService.uploadUrl(url, {\n            resModel: this.props.resModel,\n            resId: this.props.resId,\n        }, attachment => this.onUploaded(attachment));\n    }\n\n    async onUploaded(attachment) {\n        this.state.attachments = [attachment, ...this.state.attachments.filter(attach => attach.id !== attachment.id)];\n        this.selectAttachment(attachment);\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n        if (this.props.onAttachmentChange) {\n            this.props.onAttachmentChange(attachment);\n        }\n    }\n\n    onRemoved(attachmentId) {\n        this.state.attachments = this.state.attachments.filter(attachment => attachment.id !== attachmentId);\n    }\n\n    selectAttachment(attachment) {\n        this.props.selectMedia({ ...attachment, mediaType: 'attachment' });\n    }\n\n    selectInitialMedia() {\n        return this.props.media\n            && this.constructor.tagNames.includes(this.props.media.tagName)\n            && !this.selectedAttachmentIds.length;\n    }\n\n    /**\n     * Updates the scroll button, depending on whether the \"Load more\" button is\n     * fully visible or not.\n     */\n    updateScroll() {\n        const loadMoreTop = this.loadMoreButtonRef.el.getBoundingClientRect().top;\n        const modalEl = this.props.modalRef.el.querySelector(\"main.modal-body\");\n        const modalBottom = modalEl.getBoundingClientRect().bottom;\n        this.state.canScrollAttachments = loadMoreTop >= modalBottom;\n        this.loadMoreButtonRef.el.classList.remove(\"o_hide_loading\");\n    }\n\n    /**\n     * Checks if the attachment is (partially) hidden.\n     *\n     * @param {Element} attachmentEl the attachment \"container\"\n     * @returns {Boolean} true if the attachment is hidden, false otherwise.\n     */\n    isAttachmentHidden(attachmentEl) {\n        const attachmentBottom = Math.round(attachmentEl.getBoundingClientRect().bottom);\n        const modalEl = this.props.modalRef.el.querySelector(\"main.modal-body\");\n        const modalBottom = modalEl.getBoundingClientRect().bottom;\n        return attachmentBottom > modalBottom;\n    }\n\n    /**\n     * Scrolls two attachments rows at a time. If there are not enough rows,\n     * scrolls to the \"Load more\" button.\n     */\n    handleScrollAttachments() {\n        let scrollToEl = this.loadMoreButtonRef.el;\n        const attachmentEls = [...this.existingAttachmentsRef.el.querySelectorAll(\".o_existing_attachment_cell\")];\n        const firstHiddenAttachmentEl = attachmentEls.find(el => this.isAttachmentHidden(el));\n        if (firstHiddenAttachmentEl) {\n            const attachmentBottom = firstHiddenAttachmentEl.getBoundingClientRect().bottom;\n            const attachmentIndex = attachmentEls.indexOf(firstHiddenAttachmentEl);\n            const firstNextRowAttachmentEl = attachmentEls.slice(attachmentIndex).find(el => {\n                return el.getBoundingClientRect().bottom > attachmentBottom;\n            })\n            scrollToEl = firstNextRowAttachmentEl || scrollToEl;\n        }\n        scrollToEl.scrollIntoView({ block: \"end\", inline: \"nearest\", behavior: \"smooth\" });\n    }\n}\n", "import fonts from '@web_editor/js/wysiwyg/fonts';\nimport { SearchMedia } from './search_media';\n\nimport { Component, useState } from \"@odoo/owl\";\n\nexport class IconSelector extends Component {\n    static mediaSpecificClasses = [\"fa\"];\n    static mediaSpecificStyles = [\"color\", \"background-color\", \"border-width\", \"border-color\", \"border-style\"];\n    static mediaExtraClasses = [\n        \"rounded-circle\",\n        \"rounded\",\n        \"img-thumbnail\",\n        \"shadow\",\n        \"border\",\n        /^text-\\S+$/,\n        /^bg-\\S+$/,\n        /^fa-\\S+$/,\n    ];\n    static tagNames = [\"SPAN\", \"I\"];\n    static template = \"web_editor.IconSelector\";\n    static components = {\n        SearchMedia,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.state = useState({\n            fonts: this.props.fonts,\n            needle: '',\n        });\n    }\n\n    get selectedMediaIds() {\n        return this.props.selectedMedia[this.props.id].map(({ id }) => id);\n    }\n\n    search(needle) {\n        this.state.needle = needle;\n        if (!this.state.needle) {\n            this.state.fonts = this.props.fonts;\n        } else {\n            this.state.fonts = this.props.fonts.map(font => {\n                const icons = font.icons.filter(icon => icon.alias.indexOf(this.state.needle) >= 0);\n                return {...font, icons};\n            });\n        }\n    }\n\n    async onClickIcon(font, icon) {\n        this.props.selectMedia({\n            ...icon,\n            fontBase: font.base,\n            // To check if the icon has changed, we only need to compare\n            // an alias of the icon with the class from the old media (some\n            // icons can have multiple classes e.g. \"fa-gears\" ~ \"fa-cogs\")\n            initialIconChanged: this.props.media\n                && !icon.names.some(name => this.props.media.classList.contains(name)),\n        });\n        await this.props.save();\n    }\n\n    /**\n     * Utility methods, used by the MediaDialog component.\n     */\n    static createElements(selectedMedia) {\n        return selectedMedia.map(icon => {\n            const iconEl = document.createElement('span');\n            iconEl.classList.add(icon.fontBase, icon.names[0]);\n            return iconEl;\n        });\n    }\n    static initFonts() {\n        fonts.computeFonts();\n        const allFonts = fonts.fontIcons.map(({cssData, base}) => {\n            const uniqueIcons = Array.from(new Map(cssData.map(icon => {\n                const alias = icon.names.join(',');\n                const id = `${base}_${alias}`;\n                return [id, { ...icon, alias, id }];\n            })).values());\n            return { base, icons: uniqueIcons };\n        });\n        return allFonts;\n    }\n}\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport weUtils from '@web_editor/js/common/utils';\nimport { Attachment, FileSelector, IMAGE_MIMETYPES, IMAGE_EXTENSIONS } from './file_selector';\nimport { KeepLast } from \"@web/core/utils/concurrency\";\n\nimport { useRef, useState, useEffect } from \"@odoo/owl\";\n\nexport class AutoResizeImage extends Attachment {\n    static template = \"web_editor.AutoResizeImage\";\n    setup() {\n        super.setup();\n\n        this.image = useRef('auto-resize-image');\n        this.container = useRef('auto-resize-image-container');\n\n        this.state = useState({\n            loaded: false,\n        });\n\n        useEffect(() => {\n            this.image.el.addEventListener('load', () => this.onImageLoaded());\n            return this.image.el.removeEventListener('load', () => this.onImageLoaded());\n        }, () => []);\n    }\n\n    async onImageLoaded() {\n        if (!this.image.el) {\n            // Do not fail if already removed.\n            return;\n        }\n        if (this.props.onLoaded) {\n            await this.props.onLoaded(this.image.el);\n            if (!this.image.el) {\n                // If replaced by colored version, aspect ratio will be\n                // computed on it instead.\n                return;\n            }\n        }\n        const aspectRatio = this.image.el.offsetWidth / this.image.el.offsetHeight;\n        const width = aspectRatio * this.props.minRowHeight;\n        this.container.el.style.flexGrow = width;\n        this.container.el.style.flexBasis = `${width}px`;\n        this.state.loaded = true;\n    }\n}\nconst newLocal = \"img-fluid\";\nexport class ImageSelector extends FileSelector {\n    static mediaSpecificClasses = [\"img\", newLocal, \"o_we_custom_image\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [\n        \"rounded-circle\",\n        \"rounded\",\n        \"img-thumbnail\",\n        \"shadow\",\n        \"w-25\",\n        \"w-50\",\n        \"w-75\",\n        \"w-100\",\n    ];\n    static tagNames = [\"IMG\"];\n    static attachmentsListTemplate = \"web_editor.ImagesListTemplate\";\n    static components = {\n        ...FileSelector.components,\n        AutoResizeImage,\n    };\n\n    setup() {\n        super.setup();\n\n        this.keepLastLibraryMedia = new KeepLast();\n\n        this.state.libraryMedia = [];\n        this.state.libraryResults = null;\n        this.state.isFetchingLibrary = false;\n        this.state.searchService = 'all';\n        this.state.showOptimized = false;\n        this.NUMBER_OF_MEDIA_TO_DISPLAY = 10;\n\n        this.uploadText = _t(\"Upload an image\");\n        this.urlPlaceholder = \"https://www.odoo.com/logo.png\";\n        this.addText = _t(\"Add URL\");\n        this.searchPlaceholder = _t(\"Search an image\");\n        this.urlWarningTitle = _t(\"Uploaded image's format is not supported. Try with: \" + IMAGE_EXTENSIONS.join(', '));\n        this.allLoadedText = _t(\"All images have been loaded\");\n        this.showOptimizedOption = this.env.debug;\n        this.MIN_ROW_HEIGHT = 128;\n\n        this.fileMimetypes = IMAGE_MIMETYPES.join(',');\n        this.isImageField = !!(this.props.media && this.props.media.closest(\"[data-oe-type=image]\")) || !!this.env.addFieldImage;\n    }\n\n    get canLoadMore() {\n        // The user can load more library media only when the filter is set.\n        if (this.state.searchService === 'media-library') {\n            return this.state.libraryResults && this.state.libraryMedia.length < this.state.libraryResults;\n        }\n        return super.canLoadMore;\n    }\n\n    get hasContent() {\n        if (this.state.searchService === 'all') {\n            return super.hasContent || !!this.state.libraryMedia.length;\n        } else if (this.state.searchService === 'media-library') {\n            return !!this.state.libraryMedia.length;\n        }\n        return super.hasContent;\n    }\n\n    get isFetching() {\n        return super.isFetching || this.state.isFetchingLibrary;\n    }\n\n    get selectedMediaIds() {\n        return this.props.selectedMedia[this.props.id].filter(media => media.mediaType === 'libraryMedia').map(({ id }) => id);\n    }\n\n    get allAttachments() {\n        return [...super.allAttachments, ...this.state.libraryMedia];\n    }\n\n    get attachmentsDomain() {\n        const domain = super.attachmentsDomain;\n        domain.push(['mimetype', 'in', IMAGE_MIMETYPES]);\n        if (!this.props.useMediaLibrary) {\n            domain.push(\"|\", [\"url\", \"=\", false],\n                \"!\", \"|\", [\"url\", \"=ilike\", \"/html_editor/shape/%\"], [\"url\", \"=ilike\", \"/web_editor/shape/%\"],\n            );\n        }\n        domain.push('!', ['name', '=like', '%.crop']);\n        domain.push('|', ['type', '=', 'binary'], '!', ['url', '=like', '/%/static/%']);\n\n        // Optimized images (meaning they are related to an `original_id`) can\n        // only be shown in debug mode as the toggler to make those images\n        // appear is hidden when not in debug mode.\n        // There is thus no point to fetch those optimized images outside debug\n        // mode. Worst, it leads to bugs: it might fetch only optimized images\n        // when clicking on \"load more\" which will look like it's bugged as no\n        // images will appear on screen (they all will be hidden).\n        if (!this.env.debug) {\n            const subDomain = [false];\n\n            // Particular exception: if the edited image is an optimized\n            // image, we need to fetch it too so it's displayed as the\n            // selected image when opening the media dialog.\n            // We might get a few more optimized image than necessary if the\n            // original image has multiple optimized images but it's not a\n            // big deal.\n            const originalId = this.props.media && this.props.media.dataset.originalId;\n            if (originalId) {\n                subDomain.push(originalId);\n            }\n\n            domain.push(['original_id', 'in', subDomain]);\n        }\n\n        return domain;\n    }\n\n    async uploadFiles(files) {\n        await this.uploadService.uploadFiles(files, { resModel: this.props.resModel, resId: this.props.resId, isImage: true }, (attachment) => this.onUploaded(attachment));\n    }\n\n    async uploadUrl(url) {\n        await fetch(url).then(async result => {\n            const blob = await result.blob();\n            blob.id = new Date().getTime();\n            blob.name = new URL(url, window.location.href).pathname.split(\"/\").findLast(s => s);\n            await this.uploadFiles([blob]);\n        }).catch(async () => {\n            await new Promise(resolve => {\n                // If it works from an image, use URL.\n                const imageEl = document.createElement(\"img\");\n                imageEl.onerror = () => {\n                    // This message is about the blob fetch failure.\n                    // It is only displayed if the fallback did not work.\n                    this.notificationService.add(_t(\"An error occurred while fetching the entered URL.\"), {\n                        title: _t(\"Error\"),\n                        sticky: true,\n                    });\n                    resolve();\n                };\n                imageEl.onload = () => {\n                    const urlPathname = new URL(url, window.location.href).pathname;\n                    const imageExtension = IMAGE_EXTENSIONS.find(format => urlPathname.endsWith(format));\n                    if (this.isImageField && imageExtension === \".webp\") {\n                        // Do not allow the user to replace an image field by a\n                        // webp CORS protected image as we are not currently\n                        // able to manage the report creation if such images are\n                        // in there (as the equivalent jpeg can not be\n                        // generated). It also causes a problem for resize\n                        // operations as 'libwep' can not be used.\n                        this.notificationService.add(_t(\n                            \"You can not replace a field by this image. If you want to use this image, first save it on your computer and then upload it here.\"\n                        ), {\n                            title: _t(\"Error\"),\n                            sticky: true,\n                        });\n                        return resolve();\n                    }\n                    super.uploadUrl(url).then(resolve);\n                };\n                imageEl.src = url;\n            });\n        });\n    }\n\n    async validateUrl(...args) {\n        const { isValidUrl, path } = super.validateUrl(...args);\n        const isValidFileFormat = isValidUrl && await new Promise(resolve => {\n            const img = new Image();\n            img.src = path;\n            img.onload = () => resolve(true);\n            img.onerror = () => resolve(false);\n        });\n        return { isValidUrl, isValidFileFormat };\n    }\n\n    isInitialMedia(attachment) {\n        if (this.props.media.dataset.originalSrc) {\n            return this.props.media.dataset.originalSrc === attachment.image_src;\n        }\n        return this.props.media.getAttribute('src') === attachment.image_src;\n    }\n\n    async fetchAttachments(limit, offset) {\n        const attachments = await super.fetchAttachments(limit, offset);\n        if (this.isImageField) {\n            // The image is a field; mark the attachments if they are linked to\n            // a webp CORS protected image. Indeed, in this case, they should\n            // not be selectable on the media dialog (due to a problem of image\n            // resize and report creation).\n            for (const attachment of attachments) {\n                if (attachment.mimetype === \"image/webp\" && await weUtils.isSrcCorsProtected(attachment.image_src)) {\n                    attachment.unselectable = true;\n                }\n            }\n        }\n        // Color-substitution for dynamic SVG attachment\n        const primaryColors = {};\n        for (let color = 1; color <= 5; color++) {\n            primaryColors[color] = weUtils.getCSSVariableValue('o-color-' + color);\n        }\n        return attachments.map(attachment => {\n            if (attachment.image_src.startsWith('/')) {\n                const newURL = new URL(attachment.image_src, window.location.origin);\n                // Set the main colors of dynamic SVGs to o-color-1~5\n                if (attachment.image_src.startsWith('/html_editor/shape/') ||\n                    attachment.image_src.startsWith('/web_editor/shape/')\n                ) {\n                    newURL.searchParams.forEach((value, key) => {\n                        const match = key.match(/^c([1-5])$/);\n                        if (match) {\n                            newURL.searchParams.set(key, primaryColors[match[1]]);\n                        }\n                    });\n                } else {\n                    // Set height so that db images load faster\n                    newURL.searchParams.set('height', 2 * this.MIN_ROW_HEIGHT);\n                }\n                attachment.thumbnail_src = newURL.pathname + newURL.search;\n            }\n            if (this.selectInitialMedia() && this.isInitialMedia(attachment)) {\n                this.selectAttachment(attachment);\n            }\n            return attachment;\n        });\n    }\n\n    async fetchLibraryMedia(offset) {\n        if (!this.state.needle) {\n            return { media: [], results: null };\n        }\n\n        this.state.isFetchingLibrary = true;\n        try {\n            const response = await rpc(\n                '/web_editor/media_library_search',\n                {\n                    'query': this.state.needle,\n                    'offset': offset,\n                },\n                {\n                    silent: true,\n                }\n            );\n            this.state.isFetchingLibrary = false;\n            const media = (response.media || []).slice(0, this.NUMBER_OF_MEDIA_TO_DISPLAY);\n            media.forEach(record => record.mediaType = 'libraryMedia');\n            return { media, results: response.results };\n        } catch {\n            // Either API endpoint doesn't exist or is misconfigured.\n            console.error(`Couldn't reach API endpoint.`);\n            this.state.isFetchingLibrary = false;\n            return { media: [], results: null };\n        }\n    }\n\n    async loadMore(...args) {\n        await super.loadMore(...args);\n        if (!this.props.useMediaLibrary\n            // The user can load more library media only when the filter is set.\n            || this.state.searchService !== 'media-library'\n        ) {\n            return;\n        }\n        return this.keepLastLibraryMedia.add(this.fetchLibraryMedia(this.state.libraryMedia.length)).then(({ media }) => {\n            // This is never reached if another search or loadMore occurred.\n            this.state.libraryMedia.push(...media);\n        });\n    }\n\n    async search(...args) {\n        await super.search(...args);\n        if (!this.props.useMediaLibrary) {\n            return;\n        }\n        if (!this.state.needle) {\n            this.state.searchService = 'all';\n        }\n        this.state.libraryMedia = [];\n        this.state.libraryResults = 0;\n        return this.keepLastLibraryMedia.add(this.fetchLibraryMedia(0)).then(({ media, results }) => {\n            // This is never reached if a new search occurred.\n            this.state.libraryMedia = media;\n            this.state.libraryResults = results;\n        });\n    }\n\n    async onClickAttachment(attachment) {\n        if (attachment.unselectable) {\n            this.notificationService.add(_t(\n                \"You can not replace a field by this image. If you want to use this image, first save it on your computer and then upload it here.\"\n            ), {\n                title: _t(\"Error\"),\n                sticky: true,\n            });\n            return;\n        }\n        this.selectAttachment(attachment);\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    }\n\n    async onClickMedia(media) {\n        this.props.selectMedia({ ...media, mediaType: 'libraryMedia' });\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    }\n\n    /**\n     * Utility method used by the MediaDialog component.\n     */\n    static async createElements(selectedMedia, { orm }) {\n        // Create all media-library attachments.\n        const toSave = Object.fromEntries(selectedMedia.filter(media => media.mediaType === 'libraryMedia').map(media => [\n            media.id, {\n                query: media.query || '',\n                is_dynamic_svg: !!media.isDynamicSVG,\n                dynamic_colors: media.dynamicColors,\n            }\n        ]));\n        let savedMedia = [];\n        if (Object.keys(toSave).length !== 0) {\n            savedMedia = await rpc('/web_editor/save_library_media', { media: toSave });\n        }\n        const selected = selectedMedia.filter(media => media.mediaType === 'attachment').concat(savedMedia).map(attachment => {\n            // Color-customize dynamic SVGs with the theme colors\n            if (attachment.image_src && (\n                attachment.image_src.startsWith('/html_editor/shape/') ||\n                attachment.image_src.startsWith('/web_editor/shape/')\n            )) {\n                const colorCustomizedURL = new URL(attachment.image_src, window.location.origin);\n                colorCustomizedURL.searchParams.forEach((value, key) => {\n                    const match = key.match(/^c([1-5])$/);\n                    if (match) {\n                        colorCustomizedURL.searchParams.set(key, weUtils.getCSSVariableValue(`o-color-${match[1]}`));\n                    }\n                });\n                attachment.image_src = colorCustomizedURL.pathname + colorCustomizedURL.search;\n            }\n            return attachment;\n        });\n        return Promise.all(selected.map(async (attachment) => {\n            const imageEl = document.createElement('img');\n            let src = attachment.image_src;\n            if (!attachment.public && !attachment.url) {\n                let accessToken = attachment.access_token;\n                if (!accessToken) {\n                    [accessToken] = await orm.call(\n                        'ir.attachment',\n                        'generate_access_token',\n                        [attachment.id],\n                    );\n                }\n                src += `?access_token=${encodeURIComponent(accessToken)}`;\n            }\n            imageEl.src = src;\n            imageEl.alt = attachment.description || '';\n            return imageEl;\n        }));\n    }\n\n    async onImageLoaded(imgEl, attachment) {\n        this.debouncedScrollUpdate();\n        if (attachment.mediaType === 'libraryMedia' && !imgEl.src.startsWith('blob')) {\n            // This call applies the theme's color palette to the\n            // loaded illustration. Upon replacement of the image,\n            // `onImageLoad` is called again, but the replacement image\n            // has an URL that starts with 'blob'. The condition above\n            // uses this to avoid an infinite loop.\n            await this.onLibraryImageLoaded(imgEl, attachment);\n        }\n    }\n\n    /**\n     * This converts the colors of an svg coming from the media library to\n     * the palette's ones, and make them dynamic.\n     *\n     * @param {HTMLElement} imgEl\n     * @param {Object} media\n     * @returns\n     */\n    async onLibraryImageLoaded(imgEl, media) {\n        const mediaUrl = imgEl.src;\n        try {\n            const response = await fetch(mediaUrl);\n            if (response.headers.get('content-type') === 'image/svg+xml') {\n                let svg = await response.text();\n                const dynamicColors = {};\n                const combinedColorsRegex = new RegExp(Object.values(weUtils.DEFAULT_PALETTE).join('|'), 'gi');\n                svg = svg.replace(combinedColorsRegex, match => {\n                    const colorId = Object.keys(weUtils.DEFAULT_PALETTE).find(key => weUtils.DEFAULT_PALETTE[key] === match.toUpperCase());\n                    const colorKey = 'c' + colorId\n                    dynamicColors[colorKey] = weUtils.getCSSVariableValue('o-color-' + colorId);\n                    return dynamicColors[colorKey];\n                });\n                const fileName = mediaUrl.split('/').pop();\n                const file = new File([svg], fileName, {\n                    type: \"image/svg+xml\",\n                });\n                imgEl.src = URL.createObjectURL(file);\n                if (Object.keys(dynamicColors).length) {\n                    media.isDynamicSVG = true;\n                    media.dynamicColors = dynamicColors;\n                }\n            }\n        } catch {\n            console.error('CORS is misconfigured on the API server, image will be treated as non-dynamic.');\n        }\n    }\n}\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { useService, useChildRef } from '@web/core/utils/hooks';\nimport { Mutex } from \"@web/core/utils/concurrency\";\nimport { Dialog } from '@web/core/dialog/dialog';\nimport { Notebook } from '@web/core/notebook/notebook';\nimport { ImageSelector } from './image_selector';\nimport { DocumentSelector } from './document_selector';\nimport { IconSelector } from './icon_selector';\nimport { VideoSelector } from './video_selector';\n\nimport { Component, useState, useRef, useEffect } from \"@odoo/owl\";\n\nexport const TABS = {\n    IMAGES: {\n        id: 'IMAGES',\n        title: _t(\"Images\"),\n        Component: ImageSelector,\n    },\n    DOCUMENTS: {\n        id: 'DOCUMENTS',\n        title: _t(\"Documents\"),\n        Component: DocumentSelector,\n    },\n    ICONS: {\n        id: 'ICONS',\n        title: _t(\"Icons\"),\n        Component: IconSelector,\n    },\n    VIDEOS: {\n        id: 'VIDEOS',\n        title: _t(\"Videos\"),\n        Component: VideoSelector,\n    },\n};\n\nexport class MediaDialog extends Component {\n    static template = \"web_editor.MediaDialog\";\n    static defaultProps = {\n        useMediaLibrary: true,\n    };\n    static components = {\n        ...Object.keys(TABS).map((key) => TABS[key].Component),\n        Dialog,\n        Notebook,\n    };\n    static props = [\"*\"];\n\n    setup() {\n        this.size = 'xl';\n        this.contentClass = 'o_select_media_dialog h-100';\n        this.modalRef = useChildRef();\n\n        this.orm = useService('orm');\n        this.notificationService = useService('notification');\n        this.mutex = new Mutex();\n\n        this.tabs = [];\n        this.selectedMedia = useState({});\n\n        this.addButtonRef = useRef('add-button');\n\n        this.initialIconClasses = [];\n\n        this.addTabs();\n        this.errorMessages = {};\n\n        this.state = useState({\n            activeTab: this.initialActiveTab,\n        });\n\n        useEffect(\n            (nbSelectedAttachments) => {\n                // Disable/enable the add button depending on whether some media\n                // are selected or not.\n                this.addButtonRef.el.toggleAttribute(\"disabled\", !nbSelectedAttachments);\n            },\n            () => [this.selectedMedia[this.state.activeTab].length]\n        );\n    }\n\n    get initialActiveTab() {\n        if (this.props.activeTab) {\n            return this.props.activeTab;\n        }\n        if (this.props.media) {\n            const correspondingTab = Object.keys(TABS).find(id => TABS[id].Component.tagNames.includes(this.props.media.tagName));\n            if (correspondingTab) {\n                return correspondingTab;\n            }\n        }\n        return this.tabs[0].id;\n    }\n\n    addTab(tab, additionalProps = {}) {\n        this.selectedMedia[tab.id] = [];\n        this.tabs.push({\n            ...tab,\n            props: {\n                ...tab.props,\n                ...additionalProps,\n                id: tab.id,\n                resModel: this.props.resModel,\n                resId: this.props.resId,\n                media: this.props.media,\n                multiImages: this.props.multiImages,\n                selectedMedia: this.selectedMedia,\n                selectMedia: (...args) => this.selectMedia(...args, tab.id, additionalProps.multiSelect),\n                save: this.save.bind(this),\n                onAttachmentChange: this.props.onAttachmentChange,\n                errorMessages: (errorMessage) => this.errorMessages[tab.id] = errorMessage,\n                modalRef: this.modalRef,\n            },\n        });\n    }\n\n    addTabs() {\n        const onlyImages = this.props.onlyImages || this.props.multiImages || (this.props.media && this.props.media.parentElement && (this.props.media.parentElement.dataset.oeField === 'image' || this.props.media.parentElement.dataset.oeType === 'image'));\n        const noDocuments = onlyImages || this.props.noDocuments;\n        const noIcons = onlyImages || this.props.noIcons;\n        const noVideos = onlyImages || this.props.noVideos;\n\n        if (!this.props.noImages) {\n            this.addTab(TABS.IMAGES, {\n                useMediaLibrary: this.props.useMediaLibrary,\n                multiSelect: this.props.multiImages,\n            });\n        }\n        if (!noDocuments) {\n            this.addTab(TABS.DOCUMENTS);\n        }\n        if (!noIcons) {\n            const fonts = TABS.ICONS.Component.initFonts();\n            this.addTab(TABS.ICONS, {\n                fonts,\n            });\n\n            if (this.props.media && TABS.ICONS.Component.tagNames.includes(this.props.media.tagName)) {\n                const classes = this.props.media.className.split(/\\s+/);\n                const mediaFont = fonts.find(font => classes.includes(font.base));\n                if (mediaFont) {\n                    const selectedIcon = mediaFont.icons.find(icon => icon.names.some(name => classes.includes(name)));\n                    if (selectedIcon) {\n                        this.initialIconClasses.push(...selectedIcon.names);\n                        this.selectMedia(selectedIcon, TABS.ICONS.id);\n                    }\n                }\n            }\n        }\n        if (!noVideos) {\n            this.addTab(TABS.VIDEOS, {\n                vimeoPreviewIds: this.props.vimeoPreviewIds,\n                isForBgVideo: this.props.isForBgVideo,\n            });\n        }\n    }\n\n    /**\n     * Render the selected media for insertion in the editor\n     *\n     * @param {Array<Object>} selectedMedia\n     * @returns {Array<HTMLElement>}\n     */\n    async renderMedia(selectedMedia) {\n        // Calling a mutex to make sure RPC calls inside `createElements` are\n        // properly awaited (e.g. avoid creating multiple attachments when\n        // clicking multiple times on the same media). As `createElements` is\n        // static, the mutex has to be set on the media dialog itself to be\n        // destroyed with its instance.\n        const elements = await this.mutex.exec(async() =>\n            await TABS[this.state.activeTab].Component.createElements(selectedMedia, { orm: this.orm })\n        );\n        elements.forEach(element => {\n            if (this.props.media) {\n                element.classList.add(...this.props.media.classList);\n                const style = this.props.media.getAttribute('style');\n                if (style) {\n                    element.setAttribute('style', style);\n                }\n                if (this.state.activeTab === TABS.IMAGES.id) {\n                    if (this.props.media.dataset.shape) {\n                        element.dataset.shape = this.props.media.dataset.shape;\n                    }\n                    if (this.props.media.dataset.shapeColors) {\n                        element.dataset.shapeColors = this.props.media.dataset.shapeColors;\n                    }\n                    if (this.props.media.dataset.shapeFlip) {\n                        element.dataset.shapeFlip = this.props.media.dataset.shapeFlip;\n                    }\n                    if (this.props.media.dataset.shapeRotate) {\n                        element.dataset.shapeRotate = this.props.media.dataset.shapeRotate;\n                    }\n                    if (this.props.media.dataset.hoverEffect) {\n                        element.dataset.hoverEffect = this.props.media.dataset.hoverEffect;\n                    }\n                    if (this.props.media.dataset.hoverEffectColor) {\n                        element.dataset.hoverEffectColor = this.props.media.dataset.hoverEffectColor;\n                    }\n                    if (this.props.media.dataset.hoverEffectStrokeWidth) {\n                        element.dataset.hoverEffectStrokeWidth = this.props.media.dataset.hoverEffectStrokeWidth;\n                    }\n                    if (this.props.media.dataset.hoverEffectIntensity) {\n                        element.dataset.hoverEffectIntensity = this.props.media.dataset.hoverEffectIntensity;\n                    }\n                    if (this.props.media.dataset.shapeAnimationSpeed) {\n                        element.dataset.shapeAnimationSpeed = this.props.media.dataset.shapeAnimationSpeed;\n                    }\n                } else if ([TABS.VIDEOS.id, TABS.DOCUMENTS.id].includes(this.state.activeTab)) {\n                    const parentEl = this.props.media.parentElement;\n                    if (\n                        parentEl &&\n                        parentEl.tagName === \"A\" &&\n                        parentEl.children.length === 1 &&\n                        this.props.media.tagName === \"IMG\"\n                    ) {\n                        // If an image is wrapped in an <a> tag, we remove the link when replacing it with a video or document\n                        parentEl.replaceWith(parentEl.firstElementChild);\n                    }\n                }\n            }\n            for (const otherTab of Object.keys(TABS).filter(key => key !== this.state.activeTab)) {\n                for (const property of TABS[otherTab].Component.mediaSpecificStyles) {\n                    element.style.removeProperty(property);\n                }\n                element.classList.remove(...TABS[otherTab].Component.mediaSpecificClasses);\n                const extraClassesToRemove = [];\n                for (const name of TABS[otherTab].Component.mediaExtraClasses) {\n                    if (typeof(name) === 'string') {\n                        extraClassesToRemove.push(name);\n                    } else { // Regex\n                        for (const className of element.classList) {\n                            if (className.match(name)) {\n                                extraClassesToRemove.push(className);\n                            }\n                        }\n                    }\n                }\n                // Remove classes that do not also exist in the target type.\n                element.classList.remove(...extraClassesToRemove.filter(candidateName => {\n                    for (const name of TABS[this.state.activeTab].Component.mediaExtraClasses) {\n                        if (typeof(name) === 'string') {\n                            if (candidateName === name) {\n                                return false;\n                            }\n                        } else { // Regex\n                            for (const className of element.classList) {\n                                if (className.match(candidateName)) {\n                                    return false;\n                                }\n                            }\n                        }\n                    }\n                    return true;\n                }));\n            }\n            element.classList.remove(...this.initialIconClasses);\n            element.classList.remove('o_modified_image_to_save');\n            element.classList.remove('oe_edited_link');\n            element.classList.add(...TABS[this.state.activeTab].Component.mediaSpecificClasses);\n        });\n        return elements;\n    }\n\n    selectMedia(media, tabId, multiSelect) {\n        if (multiSelect) {\n            const isMediaSelected = this.selectedMedia[tabId].map(({ id }) => id).includes(media.id);\n            if (!isMediaSelected) {\n                this.selectedMedia[tabId].push(media);\n            } else {\n                this.selectedMedia[tabId] = this.selectedMedia[tabId].filter(m => m.id !== media.id);\n            }\n        } else {\n            this.selectedMedia[tabId] = [media];\n        }\n    }\n\n    async save() {\n        if (this.errorMessages[this.state.activeTab]) {\n            this.notificationService.add(this.errorMessages[this.state.activeTab], {\n                type: 'danger',\n            });\n            return;\n        }\n        const selectedMedia = this.selectedMedia[this.state.activeTab];\n        // TODO In master: clean the save method so it performs the specific\n        // adaptation before saving from the active media selector and find a\n        // way to simply close the dialog if the media element remains the same.\n        const saveSelectedMedia = selectedMedia.length\n            && (this.state.activeTab !== TABS.ICONS.id || selectedMedia[0].initialIconChanged || !this.props.media);\n        if (saveSelectedMedia) {\n            const elements = await this.renderMedia(selectedMedia);\n            if (this.props.multiImages) {\n                await this.props.save(elements);\n            } else {\n                await this.props.save(elements[0]);\n            }\n        }\n        this.props.close();\n    }\n\n    onTabChange(tab) {\n        this.state.activeTab = tab;\n    }\n}\n", "/** @odoo-module **/\n\nimport { useDebounced } from '@web/core/utils/timing';\nimport { useAutofocus } from '@web/core/utils/hooks';\n\nimport { Component, xml, useEffect, useState } from \"@odoo/owl\";\n\nexport class SearchMedia extends Component {\n    static template = xml`\n        <div class=\"position-relative mw-lg-25 flex-grow-1 me-auto\">\n            <input type=\"text\" class=\"o_we_search o_input form-control\" t-att-placeholder=\"props.searchPlaceholder.trim()\" t-model=\"state.input\" t-ref=\"autofocus\"/>\n            <i class=\"oi oi-search input-group-text position-absolute end-0 top-50 me-n3 px-2 py-1 translate-middle bg-transparent border-0\" title=\"Search\" role=\"img\" aria-label=\"Search\"/>\n        </div>`;\n    static props = [\"searchPlaceholder\", \"search\", \"needle\"];\n    setup() {\n        useAutofocus();\n        this.debouncedSearch = useDebounced(this.props.search, 1000);\n\n        this.state = useState({\n            input: this.props.needle || '',\n        });\n\n        useEffect((input) => {\n            // Do not trigger a search on the initial render.\n            if (this.hasRendered) {\n                this.debouncedSearch(input);\n            } else {\n                this.hasRendered = true;\n            }\n        }, () => [this.state.input]);\n    }\n}\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useAutofocus, useService } from '@web/core/utils/hooks';\nimport { debounce } from '@web/core/utils/timing';\n\nimport { Component, useState, useRef, onMounted, onWillStart } from \"@odoo/owl\";\n\nclass VideoOption extends Component {\n    static template = \"web_editor.VideoOption\";\n    static props = {\n        description: {type: String, optional: true},\n        label: {type: String, optional: true},\n        onChangeOption: Function,\n        value: {type: Boolean, optional: true},\n    };\n}\n\nclass VideoIframe extends Component {\n    static template = \"web_editor.VideoIframe\";\n    static props = {\n        src: { type: String },\n    };\n}\n\nexport class VideoSelector extends Component {\n    static mediaSpecificClasses = [\"media_iframe_video\"];\n    static mediaSpecificStyles = [];\n    static mediaExtraClasses = [];\n    static tagNames = [\"IFRAME\", \"DIV\"];\n    static template = \"web_editor.VideoSelector\";\n    static components = {\n        VideoIframe,\n        VideoOption,\n    };\n    static props = {\n        selectMedia: Function,\n        errorMessages: Function,\n        vimeoPreviewIds: {type: Array, optional: true},\n        isForBgVideo: {type: Boolean, optional: true},\n        media: {type: Object, optional: true},\n        \"*\": true,\n    };\n    static defaultProps = {\n        vimeoPreviewIds: [],\n        isForBgVideo: false,\n    };\n\n    setup() {\n        this.http = useService('http');\n\n        this.PLATFORMS = {\n            youtube: 'youtube',\n            dailymotion: 'dailymotion',\n            vimeo: 'vimeo',\n            youku: 'youku',\n        };\n\n        this.OPTIONS = {\n            autoplay: {\n                label: _t(\"Autoplay\"),\n                description: _t(\"Videos are muted when autoplay is enabled\"),\n                platforms: [this.PLATFORMS.youtube, this.PLATFORMS.dailymotion, this.PLATFORMS.vimeo],\n                urlParameter: 'autoplay=1',\n            },\n            loop: {\n                label: _t(\"Loop\"),\n                platforms: [this.PLATFORMS.youtube, this.PLATFORMS.vimeo],\n                urlParameter: 'loop=1',\n            },\n            hide_controls: {\n                label: _t(\"Hide player controls\"),\n                platforms: [this.PLATFORMS.youtube, this.PLATFORMS.dailymotion, this.PLATFORMS.vimeo],\n                urlParameter: 'controls=0',\n            },\n            hide_fullscreen: {\n                label: _t(\"Hide fullscreen button\"),\n                platforms: [this.PLATFORMS.youtube],\n                urlParameter: 'fs=0',\n                isHidden: () => this.state.options.filter(option => option.id === 'hide_controls')[0].value,\n            },\n            hide_dm_logo: {\n                label: _t(\"Hide Dailymotion logo\"),\n                platforms: [this.PLATFORMS.dailymotion],\n                urlParameter: 'ui-logo=0',\n            },\n            hide_dm_share: {\n                label: _t(\"Hide sharing button\"),\n                platforms: [this.PLATFORMS.dailymotion],\n                urlParameter: 'sharing-enable=0',\n            },\n        };\n\n        this.state = useState({\n            options: [],\n            src: '',\n            urlInput: '',\n            platform: null,\n            vimeoPreviews: [],\n            errorMessage: '',\n        });\n        this.urlInputRef = useRef('url-input');\n\n        onWillStart(async () => {\n            if (this.props.media) {\n                const src = this.props.media.dataset.oeExpression || this.props.media.dataset.src || (this.props.media.tagName === 'IFRAME' && this.props.media.getAttribute('src')) || '';\n                if (src) {\n                    this.state.urlInput = src;\n                    await this.updateVideo();\n\n                    this.state.options = this.state.options.map((option) => {\n                        const { urlParameter } = this.OPTIONS[option.id];\n                        return { ...option, value: src.indexOf(urlParameter) >= 0 };\n                    });\n                }\n            }\n        });\n\n        onMounted(async () => this.prepareVimeoPreviews());\n\n        useAutofocus();\n\n        this.onChangeUrl = debounce((ev) => this.updateVideo(ev.target.value), 500);\n    }\n\n    get shownOptions() {\n        if (this.props.isForBgVideo) {\n            return [];\n        }\n        return this.state.options.filter(option => !this.OPTIONS[option.id].isHidden || !this.OPTIONS[option.id].isHidden());\n    }\n\n    async onChangeOption(optionId) {\n        this.state.options = this.state.options.map(option => {\n            if (option.id === optionId) {\n                return { ...option, value: !option.value };\n            }\n            return option;\n        });\n        await this.updateVideo();\n    }\n\n    async onClickSuggestion(src) {\n        this.state.urlInput = src;\n        await this.updateVideo();\n    }\n\n    async updateVideo() {\n        if (!this.state.urlInput) {\n            this.state.src = '';\n            this.state.urlInput = '';\n            this.state.options = [];\n            this.state.platform = null;\n            this.state.errorMessage = '';\n            /**\n             * When the url input is emptied, we need to call the `selectMedia`\n             * callback function to notify the other components that the media\n             * has changed.\n             */\n            this.props.selectMedia({});\n            return;\n        }\n\n        // Detect if we have an embed code rather than an URL\n        const embedMatch = this.state.urlInput.match(/(src|href)=[\"']?([^\"']+)?/);\n        if (embedMatch && embedMatch[2].length > 0 && embedMatch[2].indexOf('instagram')) {\n            embedMatch[1] = embedMatch[2]; // Instagram embed code is different\n        }\n        const url = embedMatch ? embedMatch[1] : this.state.urlInput;\n\n        const options = {};\n        if (this.props.isForBgVideo) {\n            Object.keys(this.OPTIONS).forEach(key => {\n                options[key] = true;\n            });\n        } else {\n            for (const option of this.shownOptions) {\n                options[option.id] = option.value;\n            }\n        }\n\n        const {\n            embed_url: src,\n            video_id: videoId,\n            params,\n            platform\n        } = await this._getVideoURLData(url, options);\n\n        if (!src) {\n            this.state.errorMessage = _t(\"The provided url is not valid\");\n        } else if (!platform) {\n            this.state.errorMessage =\n                _t(\"The provided url does not reference any supported video\");\n        } else {\n            this.state.errorMessage = '';\n        }\n        this.props.errorMessages(this.state.errorMessage);\n\n        const newOptions = [];\n        if (platform && platform !== this.state.platform) {\n            Object.keys(this.OPTIONS).forEach(key => {\n                if (this.OPTIONS[key].platforms.includes(platform)) {\n                    const { label, description } = this.OPTIONS[key];\n                    newOptions.push({ id: key, label, description });\n                }\n            });\n        }\n\n        this.state.src = src;\n        this.props.selectMedia({\n            id: src,\n            src,\n            platform,\n            videoId,\n            params\n        });\n        if (platform !== this.state.platform) {\n            this.state.platform = platform;\n            this.state.options = newOptions;\n        }\n    }\n\n    /**\n     * Keep rpc call in distinct method make it patchable by test.\n     */\n    async _getVideoURLData(url, options) {\n        return await rpc('/web_editor/video_url/data', {\n            video_url: url,\n            ...options,\n        });\n    }\n\n    /**\n     * Utility method, called by the MediaDialog component.\n     */\n    static createElements(selectedMedia) {\n        return selectedMedia.map(video => {\n            const div = document.createElement('div');\n            div.dataset.oeExpression = video.src;\n            div.innerHTML = `\n                <div class=\"css_editable_mode_display\"></div>\n                <div class=\"media_iframe_video_size\" contenteditable=\"false\"></div>\n                <iframe loading=\"lazy\" frameborder=\"0\" contenteditable=\"false\" allowfullscreen=\"allowfullscreen\"></iframe>\n            `;\n            div.querySelector('iframe').src = video.src;\n            return div;\n        });\n    }\n\n    /**\n     * Based on the config vimeo ids, prepare the vimeo previews.\n     */\n    async prepareVimeoPreviews() {\n        return Promise.all(this.props.vimeoPreviewIds.map(async (videoId) => {\n            const { thumbnail_url: thumbnailSrc } = await this.http.get(`https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/${encodeURIComponent(videoId)}`);\n            this.state.vimeoPreviews.push({\n                id: videoId,\n                thumbnailSrc,\n                src: `https://player.vimeo.com/video/${encodeURIComponent(videoId)}`\n            });\n        }));\n    }\n}\n", "/** @odoo-module */\nimport { useService } from '@web/core/utils/hooks';\n\nimport { Component, useState } from \"@odoo/owl\";\n\nexport class ProgressBar extends Component {\n    static template = \"web_editor.ProgressBar\";\n    static props = {\n        progress: { type: Number, optional: true },\n        hasError: { type: Boolean, optional: true },\n        uploaded: { type: Boolean, optional: true },\n        name: String,\n        size: { type: String, optional: true },\n        errorMessage: { type: String, optional: true },\n    };\n    static defaultProps = {\n        progress: 0,\n        hasError: false,\n        uploaded: false,\n        size: \"\",\n        errorMessage: \"\",\n    };\n\n    get progress() {\n        return Math.round(this.props.progress);\n    }\n}\n\nexport class UploadProgressToast extends Component {\n    static template = \"web_editor.UploadProgressToast\";\n    static components = {\n        ProgressBar,\n    };\n    static props = {\n        close: Function,\n    };\n\n    setup() {\n        this.uploadService = useService('upload');\n\n        this.state = useState(this.uploadService.progressToast);\n    }\n}\n", "/** @odoo-module **/\n\nimport { rpc } from '@web/core/network/rpc';\nimport { registry } from '@web/core/registry';\nimport { UploadProgressToast } from './upload_progress_toast';\nimport { _t } from \"@web/core/l10n/translation\";\nimport { checkFileSize } from \"@web/core/utils/files\";\nimport { humanNumber } from \"@web/core/utils/numbers\";\nimport { getDataURLFromFile } from \"@web/core/utils/urls\";\nimport { sprintf } from \"@web/core/utils/strings\";\nimport { reactive } from \"@odoo/owl\";\n\nexport const AUTOCLOSE_DELAY = 3000;\nexport const AUTOCLOSE_DELAY_LONG = 8000;\n\nexport const uploadService = {\n    dependencies: ['notification'],\n    start(env, { notification }) {\n        let fileId = 0;\n        const progressToast = reactive({\n            files: {},\n            isVisible: false,\n        });\n\n        registry.category('main_components').add('UploadProgressToast', {\n            Component: UploadProgressToast,\n            props: {\n                close: () => progressToast.isVisible = false,\n            }\n        });\n\n        const addFile = (file) => {\n            progressToast.files[file.id] = file;\n            progressToast.isVisible = true;\n            return progressToast.files[file.id];\n        };\n\n        const deleteFile = (fileId) => {\n            delete progressToast.files[fileId];\n            if (!Object.keys(progressToast.files).length) {\n                progressToast.isVisible = false;\n            }\n        };\n        return {\n            get progressToast() {\n                return progressToast;\n            },\n            get fileId() {\n                return fileId;\n            },\n            addFile,\n            deleteFile,\n            incrementId() {\n                fileId++;\n            },\n            uploadUrl: async (url, { resModel, resId }, onUploaded) => {\n                const attachment = await rpc('/web_editor/attachment/add_url', {\n                    url,\n                    'res_model': resModel,\n                    'res_id': resId,\n                });\n                await onUploaded(attachment);\n            },\n            /**\n             * This takes an array of files (from an input HTMLElement), and\n             * uploads them while managing the UploadProgressToast.\n             *\n             * @param {Array<File>} files\n             * @param {Object} options\n             * @param {Function} onUploaded\n             */\n            uploadFiles: async (files, {resModel, resId, isImage}, onUploaded) => {\n                // Upload the smallest file first to block the user the least possible.\n                const sortedFiles = Array.from(files).sort((a, b) => a.size - b.size);\n                for (const file of sortedFiles) {\n                    let fileSize = file.size;\n                    if (!checkFileSize(fileSize, notification)) {\n                        return null;\n                    }\n                    if (!fileSize) {\n                        fileSize = \"\";\n                    } else {\n                        fileSize = humanNumber(fileSize) + \"B\";\n                    }\n\n                    const id = ++fileId;\n                    file.progressToastId = id;\n                    // This reactive object, built based on the files array,\n                    // is given as a prop to the UploadProgressToast.\n                    addFile({\n                        id,\n                        name: file.name,\n                        size: fileSize,\n                    });\n                }\n\n                // Upload one file at a time: no need to parallel as upload is\n                // limited by bandwidth.\n                for (const sortedFile of sortedFiles) {\n                    const file = progressToast.files[sortedFile.progressToastId];\n                    let dataURL;\n                    try {\n                        dataURL = await getDataURLFromFile(sortedFile);\n                    } catch {\n                        deleteFile(file.id);\n                        env.services.notification.add(\n                            sprintf(\n                                _t('Could not load the file \"%s\".'),\n                                sortedFile.name\n                            ),\n                            { type: 'danger' }\n                        );\n                        continue\n                    }\n                    try {\n                        const xhr = new XMLHttpRequest();\n                        xhr.upload.addEventListener('progress', ev => {\n                            const rpcComplete = ev.loaded / ev.total * 100;\n                            file.progress = rpcComplete;\n                        });\n                        xhr.upload.addEventListener('load', function () {\n                            // Don't show yet success as backend code only starts now\n                            file.progress = 100;\n                        });\n                        const attachment = await rpc('/web_editor/attachment/add_data', {\n                            'name': file.name,\n                            'data': dataURL.split(',')[1],\n                            'res_id': resId,\n                            'res_model': resModel,\n                            'is_image': !!isImage,\n                            'width': 0,\n                            'quality': 0,\n                        }, {xhr});\n                        if (attachment.error) {\n                            file.hasError = true;\n                            file.errorMessage = attachment.error;\n                        } else {\n                            if (attachment.mimetype === 'image/webp') {\n                                // Generate alternate format for reports.\n                                const image = document.createElement('img');\n                                image.src = `data:image/webp;base64,${dataURL.split(',')[1]}`;\n                                await new Promise(resolve => image.addEventListener('load', resolve));\n                                const canvas = document.createElement('canvas');\n                                canvas.width = image.width;\n                                canvas.height = image.height;\n                                const ctx = canvas.getContext('2d');\n                                ctx.fillStyle = 'rgb(255, 255, 255)';\n                                ctx.fillRect(0, 0, canvas.width, canvas.height);\n                                ctx.drawImage(image, 0, 0);\n                                const altDataURL = canvas.toDataURL('image/jpeg', 0.75);\n                                await rpc('/web_editor/attachment/add_data', {\n                                    'name': file.name.replace(/\\.webp$/, '.jpg'),\n                                    'data': altDataURL.split(',')[1],\n                                    'res_id': attachment.id,\n                                    'res_model': 'ir.attachment',\n                                    'is_image': true,\n                                    'width': 0,\n                                    'quality': 0,\n                                }, {xhr});\n                            }\n                            file.uploaded = true;\n                            await onUploaded(attachment);\n                        }\n                        // If there's an error, display the error message for longer\n                        let message_autoclose_delay = file.hasError ? AUTOCLOSE_DELAY_LONG : AUTOCLOSE_DELAY;\n                        setTimeout(() => deleteFile(file.id), message_autoclose_delay);\n                    } catch (error) {\n                        file.hasError = true;\n                        setTimeout(() => deleteFile(file.id), AUTOCLOSE_DELAY_LONG);\n                        throw error;\n                    }\n                }\n            }\n        };\n    },\n};\n\n// registry.category('services').add('upload', uploadService);\n", "import { _t } from \"@web/core/l10n/translation\";\nimport { patch } from \"@web/core/utils/patch\";\nimport { KeepLast } from \"@web/core/utils/concurrency\";\nimport { MediaDialog, TABS } from \"@web_editor/components/media_dialog/media_dialog\";\nimport { ImageSelector } from \"@web_editor/components/media_dialog/image_selector\";\nimport { rpc } from \"@web/core/network/rpc\";\nimport { useService } from \"@web/core/utils/hooks\";\nimport { UnsplashError } from \"../unsplash_error/unsplash_error\";\n\npatch(ImageSelector.prototype, {\n    setup() {\n        super.setup();\n        this.unsplash = useService('unsplash');\n        this.keepLastUnsplash = new KeepLast();\n\n        this.state.unsplashRecords = [];\n        this.state.isFetchingUnsplash = false;\n        this.state.isMaxed = false;\n        this.state.unsplashError = null;\n        this.state.useUnsplash = true;\n        this.NUMBER_OF_RECORDS_TO_DISPLAY = 30;\n\n        this.errorMessages = {\n            'key_not_found': {\n                title: _t(\"Setup Unsplash to access royalty free photos.\"),\n                subtitle: \"\",\n            },\n            401: {\n                title: _t(\"Unauthorized Key\"),\n                subtitle: _t(\"Please check your Unsplash access key and application ID.\"),\n            },\n            403: {\n                title: _t(\"Search is temporarily unavailable\"),\n                subtitle: _t(\"The max number of searches is exceeded. Please retry in an hour or extend to a better account.\"),\n            },\n        };\n    },\n\n    get canLoadMore() {\n        if (this.state.searchService === 'all') {\n            return super.canLoadMore || this.state.needle && !this.state.isMaxed && !this.state.unsplashError;\n        } else if (this.state.searchService === 'unsplash') {\n            return this.state.needle && !this.state.isMaxed && !this.state.unsplashError;\n        }\n        return super.canLoadMore;\n    },\n\n    get hasContent() {\n        if (this.state.searchService === 'all') {\n            return super.hasContent || !!this.state.unsplashRecords.length;\n        } else if (this.state.searchService === 'unsplash') {\n            return !!this.state.unsplashRecords.length;\n        }\n        return super.hasContent;\n    },\n\n    get errorTitle() {\n        if (this.errorMessages[this.state.unsplashError]) {\n            return this.errorMessages[this.state.unsplashError].title;\n        }\n        return _t(\"Something went wrong\");\n    },\n\n    get errorSubtitle() {\n        if (this.errorMessages[this.state.unsplashError]) {\n            return this.errorMessages[this.state.unsplashError].subtitle;\n        }\n        return _t(\"Please check your internet connection or contact administrator.\");\n    },\n\n    get selectedRecordIds() {\n        return this.props.selectedMedia[this.props.id].filter(media => media.mediaType === 'unsplashRecord').map(({ id }) => id);\n    },\n\n    get isFetching() {\n        return super.isFetching || this.state.isFetchingUnsplash;\n    },\n\n    get combinedRecords() {\n        /**\n         * Creates an array with alternating elements from two arrays.\n         *\n         * @param {Array} a\n         * @param {Array} b\n         * @returns {Array} alternating elements from a and b, starting with\n         *     an element of a\n         */\n        function alternate(a, b) {\n            return [\n                a.map((v, i) => i < b.length ? [v, b[i]] : v),\n                b.slice(a.length),\n            ].flat(2);\n        }\n        return alternate(this.state.unsplashRecords, this.state.libraryMedia);\n    },\n\n    get allAttachments() {\n        return [...super.allAttachments, ...this.state.unsplashRecords];\n    },\n\n    // It seems that setters are mandatory when patching a component that\n    // extends another component.\n    set canLoadMore(_) {},\n    set hasContent(_) {},\n    set isFetching(_) {},\n    set selectedMediaIds(_) {},\n    set attachmentsDomain(_) {},\n    set errorTitle(_) {},\n    set errorSubtitle(_) {},\n    set selectedRecordIds(_) {},\n\n    async fetchUnsplashRecords(offset) {\n        if (!this.state.needle) {\n            return { records: [], isMaxed: false };\n        }\n        this.state.isFetchingUnsplash = true;\n        try {\n            const { isMaxed, images } = await this.unsplash.getImages(this.state.needle, offset, this.NUMBER_OF_RECORDS_TO_DISPLAY, this.props.orientation);\n            this.state.isFetchingUnsplash = false;\n            this.state.unsplashError = false;\n            // Ignore duplicates.\n            const existingIds = this.state.unsplashRecords.map(existing => existing.id);\n            const newImages = images.filter(record => !existingIds.includes(record.id));\n            const records = newImages.map(record => {\n                const url = new URL(record.urls.regular);\n                // In small windows, row height could get quite a bit larger than the min, so we keep some leeway.\n                url.searchParams.set('h', 2 * this.MIN_ROW_HEIGHT);\n                url.searchParams.delete('w');\n                return Object.assign({}, record, {\n                    url: url.toString(),\n                    mediaType: 'unsplashRecord',\n                });\n            });\n            return { isMaxed, records };\n        } catch (e) {\n            this.state.isFetchingUnsplash = false;\n            if (e === 'no_access') {\n                this.state.useUnsplash = false;\n            } else {\n                this.state.unsplashError = e;\n            }\n            return { records: [], isMaxed: true };\n        }\n    },\n\n    async loadMore(...args) {\n        await super.loadMore(...args);\n        return this.keepLastUnsplash.add(this.fetchUnsplashRecords(this.state.unsplashRecords.length)).then(({ records, isMaxed }) => {\n            // This is never reached if another search or loadMore occurred.\n            this.state.unsplashRecords.push(...records);\n            this.state.isMaxed = isMaxed;\n        });\n    },\n\n    async search(...args) {\n        await super.search(...args);\n        await this.searchUnsplash();\n    },\n\n    async searchUnsplash() {\n        if (!this.state.needle) {\n            this.state.unsplashError = false;\n            this.state.unsplashRecords = [];\n            this.state.isMaxed = false;\n        }\n        return this.keepLastUnsplash.add(this.fetchUnsplashRecords(0)).then(({ records, isMaxed }) => {\n            // This is never reached if a new search occurred.\n            this.state.unsplashRecords = records;\n            this.state.isMaxed = isMaxed;\n        });\n    },\n\n    async onClickRecord(media) {\n        this.props.selectMedia({ ...media, mediaType: 'unsplashRecord', query: this.state.needle });\n        if (!this.props.multiSelect) {\n            await this.props.save();\n        }\n    },\n\n    async submitCredentials(key, appId) {\n        this.state.unsplashError = null;\n        await rpc('/web_unsplash/save_unsplash', { key, appId });\n        await this.searchUnsplash();\n    },\n});\nImageSelector.components = {\n    ...ImageSelector.components,\n    UnsplashError,\n};\n\npatch(MediaDialog.prototype, {\n    setup() {\n        super.setup();\n\n        this.unsplashService = useService('unsplash');\n    },\n\n    async save() {\n        const selectedImages = this.selectedMedia[TABS.IMAGES.id];\n        if (selectedImages) {\n            const unsplashRecords = selectedImages.filter(media => media.mediaType === 'unsplashRecord');\n            if (unsplashRecords.length) {\n                await this.unsplashService.uploadUnsplashRecords(unsplashRecords, { resModel: this.props.resModel, resId: this.props.resId }, (attachments) => {\n                    this.selectedMedia[TABS.IMAGES.id] = this.selectedMedia[TABS.IMAGES.id].filter(media => media.mediaType !== 'unsplashRecord');\n                    this.selectedMedia[TABS.IMAGES.id] = this.selectedMedia[TABS.IMAGES.id].concat(attachments.map(attachment => ({...attachment, mediaType: 'attachment'})));\n                });\n            }\n        }\n        return super.save(...arguments);\n    },\n});\n", "/** @odoo-module **/\n\n// Redefine the getRangeAt function in order to avoid an error appearing\n// sometimes when an input element is focused on Firefox.\n// The error happens because the range returned by getRangeAt is \"restricted\".\n// Ex: Range { commonAncestorContainer: Restricted, startContainer: Restricted,\n// startOffset: 0, endContainer: Restricted, endOffset: 0, collapsed: true }\n// The solution consists in detecting when the range is restricted and then\n// redefining it manually based on the current selection.\nconst originalGetRangeAt = Selection.prototype.getRangeAt;\nSelection.prototype.getRangeAt = function () {\n    let range = originalGetRangeAt.apply(this, arguments);\n    // Check if the range is restricted\n    if (range.startContainer && !Object.getPrototypeOf(range.startContainer)) {\n        // Define the range manually based on the selection\n        range = document.createRange();\n        range.setStart(this.anchorNode, 0);\n        range.setEnd(this.focusNode, 0);\n    }\n    return range;\n};\n", "/** @odoo-module **/\n\nexport const ColumnLayoutMixin = {\n    /**\n     * Calculates the number of columns for the mobile or desktop version.\n     * If all elements don't have the same size, returns \"custom\".\n     *\n     * @private\n     * @param {HTMLCollection} columnEls - elements in the .row container\n     * @param {boolean} isMobile\n     * @returns {integer|string} number of columns or \"custom\"\n     */\n    _getNbColumns(columnEls, isMobile) {\n        if (!columnEls) {\n            return 0;\n        }\n        if (this._areColsCustomized(columnEls, isMobile)) {\n            return \"custom\";\n        }\n\n        const resolutionModifier = isMobile ? \"\" : \"lg-\";\n        const colRegex = new RegExp(`(?:^|\\\\s+)col-${resolutionModifier}(\\\\d{1,2})(?!\\\\S)`);\n        const colSize = parseInt(columnEls[0].className.match(colRegex)?.[1] || 12);\n        const offsetSize = this._getFirstItem(columnEls, isMobile).classList\n            .contains(`offset-${resolutionModifier}1`) ? 1 : 0;\n\n        return Math.floor((12 - offsetSize) / colSize);\n    },\n    /**\n     * Gets the first item, whether it has a mobile order or not.\n     *\n     * @private\n     * @param {HTMLCollection} columnEls - elements in the .row container\n     * @param {boolean} isMobile\n     * @returns {HTMLElement} first HTMLElement in order\n     */\n    _getFirstItem(columnEls, isMobile) {\n        return isMobile && [...columnEls].find(el => el.style.order === \"0\") || columnEls[0];\n    },\n    /**\n     * Adds mobile order and the reset class for large screens.\n     *\n     * @private\n     * @param {HTMLCollection} columnEls - elements in the .row container\n     */\n    _addMobileOrders(columnEls) {\n        for (let i = 0; i < columnEls.length; i++) {\n            columnEls[i].style.order = i;\n            columnEls[i].classList.add(\"order-lg-0\");\n        }\n    },\n    /**\n     * Removes mobile orders and the reset class for large screens.\n     *\n     * @private\n     * @param {HTMLCollection} columnEls - elements in the .row container\n     */\n    _removeMobileOrders(columnEls) {\n        for (const el of columnEls) {\n            el.style.order = \"\";\n            el.classList.remove(\"order-lg-0\");\n        }\n    },\n    /**\n     * Checks whether some columns were resized or were added offsets manually.\n     *\n     * @private\n     * @param {HTMLElement} columnEls\n     * @param {boolean} isMobile\n     * @returns {boolean}\n     */\n    _areColsCustomized(columnEls, isMobile) {\n        const resolutionModifier = isMobile ? \"\" : \"lg-\";\n        const colRegex = new RegExp(`(?:^|\\\\s+)col-${resolutionModifier}(\\\\d{1,2})(?!\\\\S)`);\n        const colSize = parseInt(columnEls[0].className.match(colRegex)?.[1] || 12);\n\n        // Cases where we know the columns sizes and/or offsets are NOT custom:\n        // - if all columns have an equal size AND\n        //     - if there are no offsets OR\n        //     - if, with 5 columns, there is exactly one offset-1 and it's on\n        //       the 1st item\n        // Any other case is custom.\n        const allColsSizesEqual = [...columnEls].every((columnEl) =>\n            parseInt(columnEl.className.match(colRegex)?.[1] || 12) === colSize);\n        if (!allColsSizesEqual) {\n            return true;\n        }\n        const offsetRegex = new RegExp(`(?:^|\\\\s+)offset-${resolutionModifier}[1-9][0-1]?(?!\\\\S)`);\n        const nbOffsets = [...columnEls]\n            .filter((columnEl) => columnEl.className.match(offsetRegex)).length;\n        if (nbOffsets === 0) {\n            return false;\n        }\n        if (nbOffsets === 1 && colSize === 2 && this._getFirstItem(columnEls, isMobile).className\n                .match(`offset-${resolutionModifier}1`)) {\n            return false;\n        }\n        return true;\n    },\n    /**\n     * Fill in the gap left by a removed item having a mobile order class.\n     *\n     * @param {HTMLElement} parentEl the removed item parent\n     * @param {Number} itemOrder the removed item mobile order\n     */\n    _fillRemovedItemGap(parentEl, itemOrder) {\n        [...parentEl.children].forEach(el => {\n            const elOrder = parseInt(el.style.order);\n            if (elOrder > itemOrder) {\n                el.style.order = elOrder - 1;\n            }\n        });\n    },\n};\n", "/** @odoo-module **/\n\nimport { renderToElement } from \"@web/core/utils/render\";\nimport {descendants, preserveCursor} from \"@web_editor/js/editor/odoo-editor/src/utils/utils\";\nexport const rowSize = 50; // 50px.\n// Maximum number of rows that can be added when dragging a grid item.\nexport const additionalRowLimit = 10;\nconst defaultGridPadding = 10; // 10px (see `--grid-item-padding-(x|y)` CSS variables).\n\n/**\n * Returns the grid properties: rowGap, rowSize, columnGap and columnSize.\n *\n * @private\n * @param {Element} rowEl the grid element\n * @returns {Object}\n */\nexport function _getGridProperties(rowEl) {\n    const style = window.getComputedStyle(rowEl);\n    const rowGap = parseFloat(style.rowGap);\n    const columnGap = parseFloat(style.columnGap);\n    const columnSize = (rowEl.clientWidth - 11 * columnGap) / 12;\n    return {rowGap: rowGap, rowSize: rowSize, columnGap: columnGap, columnSize: columnSize};\n}\n/**\n * Sets the z-index property of the element to the maximum z-index present in\n * the grid increased by one (so it is in front of all the other elements).\n *\n * @private\n * @param {Element} element the element of which we want to set the z-index\n * @param {Element} rowEl the parent grid element of the element\n */\nexport function _setElementToMaxZindex(element, rowEl) {\n    const childrenEls = [...rowEl.children].filter(el => el !== element\n        && !el.classList.contains(\"o_we_grid_preview\"));\n    element.style.zIndex = Math.max(...childrenEls.map(el => el.style.zIndex)) + 1;\n}\n/**\n * Creates the background grid appearing everytime a change occurs in a grid.\n *\n * @private\n * @param {Element} rowEl\n * @param {Number} gridHeight\n */\nexport function _addBackgroundGrid(rowEl, gridHeight) {\n    const gridProp = _getGridProperties(rowEl);\n    const rowCount = Math.max(rowEl.dataset.rowCount, gridHeight);\n\n    const backgroundGrid = renderToElement('web_editor.background_grid', {\n        rowCount: rowCount + 1, rowGap: gridProp.rowGap, rowSize: gridProp.rowSize,\n        columnGap: gridProp.columnGap, columnSize: gridProp.columnSize,\n    });\n    rowEl.prepend(backgroundGrid);\n    return rowEl.firstElementChild;\n}\n/**\n * Updates the number of rows in the grid to the end of the lowest column\n * present in it.\n *\n * @private\n * @param {Element} rowEl\n */\nexport function _resizeGrid(rowEl) {\n    const columnEls = [...rowEl.children].filter(c => c.classList.contains('o_grid_item'));\n    rowEl.dataset.rowCount = Math.max(...columnEls.map(el => el.style.gridRowEnd)) - 1;\n}\n/**\n * Removes the properties and elements added to make the drag work.\n *\n * @private\n * @param {Element} rowEl\n * @param {Element} column\n */\nexport function _gridCleanUp(rowEl, columnEl) {\n    columnEl.style.removeProperty('position');\n    columnEl.style.removeProperty('top');\n    columnEl.style.removeProperty('left');\n    columnEl.style.removeProperty('height');\n    columnEl.style.removeProperty('width');\n    rowEl.style.removeProperty('position');\n}\n/**\n * Toggles the row (= child element of containerEl) in grid mode.\n *\n * @private\n * @param {Element} containerEl element with the class \"container\"\n */\nexport function _toggleGridMode(containerEl) {\n    let rowEl = containerEl.querySelector(':scope > .row');\n    const outOfRowEls = [...containerEl.children].filter(el => !el.classList.contains('row'));\n    // Avoid an unwanted rollback that prevents from deleting the text.\n    const avoidRollback = (el) => {\n        for (const node of descendants(el)) {\n            node.ouid = undefined;\n        }\n    };\n    // Keep the text selection.\n    const restoreCursor = !rowEl || outOfRowEls.length > 0 ?\n        preserveCursor(containerEl.ownerDocument) : () => {};\n\n    // For the snippets having elements outside of the row (and therefore not in\n    // a column), create a column and put these elements in it so they can also\n    // be placed in the grid.\n    if (rowEl && outOfRowEls.length > 0) {\n        const columnEl = document.createElement('div');\n        columnEl.classList.add('col-lg-12');\n        for (let i = outOfRowEls.length - 1; i >= 0; i--) {\n            columnEl.prepend(outOfRowEls[i]);\n        }\n        avoidRollback(columnEl);\n        rowEl.prepend(columnEl);\n    }\n\n    // If the number of columns is \"None\", create a column with the content.\n    if (!rowEl) {\n        rowEl = document.createElement('div');\n        rowEl.classList.add('row');\n\n        const columnEl = document.createElement('div');\n        columnEl.classList.add('col-lg-12');\n\n        const containerChildren = containerEl.children;\n        // Looping backwards because elements are removed, so the indexes are\n        // not lost.\n        for (let i = containerChildren.length - 1; i >= 0; i--) {\n            columnEl.prepend(containerChildren[i]);\n        }\n        avoidRollback(columnEl);\n        rowEl.appendChild(columnEl);\n        containerEl.appendChild(rowEl);\n    }\n    restoreCursor();\n\n    // Converting the columns to grid and getting back the number of rows.\n    const columnEls = rowEl.children;\n    const columnSize = (rowEl.clientWidth) / 12;\n    rowEl.style.position = 'relative';\n    const rowCount = _placeColumns(columnEls, rowSize, 0, columnSize, 0) - 1;\n    rowEl.style.removeProperty('position');\n    rowEl.dataset.rowCount = rowCount;\n\n    // Removing the classes that break the grid.\n    const classesToRemove = [...rowEl.classList].filter(c => {\n        return /^align-items/.test(c);\n    });\n    rowEl.classList.remove(...classesToRemove);\n\n    rowEl.classList.add('o_grid_mode');\n}\n/**\n * Places each column in the grid based on their position and returns the\n * lowest row end.\n *\n * @private\n * @param {HTMLCollection} columnEls\n *      The children of the row element we are toggling in grid mode.\n * @param {Number} rowSize\n * @param {Number} rowGap\n * @param {Number} columnSize\n * @param {Number} columnGap\n * @returns {Number}\n */\nfunction _placeColumns(columnEls, rowSize, rowGap, columnSize, columnGap) {\n    let maxRowEnd = 0;\n    const columnSpans = [];\n    let zIndex = 1;\n    const imageColumns = []; // array of boolean telling if it is a column with only an image.\n\n    for (const columnEl of columnEls) {\n        // Finding out if the images are alone in their column.\n        let isImageColumn = _checkIfImageColumn(columnEl);\n        const imageEl = columnEl.querySelector('img');\n        // Checking if the column has a background color to take that into\n        // account when computing its size and padding (to make it look good).\n        const hasBackgroundColor = columnEl.classList.contains(\"o_cc\");\n        const isImageWithoutPadding = isImageColumn && !hasBackgroundColor;\n\n        // Placing the column.\n        const style = window.getComputedStyle(columnEl);\n        // Horizontal placement.\n        const borderLeft = parseFloat(style.borderLeft);\n        const columnLeft = isImageWithoutPadding && !borderLeft ? imageEl.offsetLeft : columnEl.offsetLeft;\n        // Getting the width of the column.\n        const paddingLeft = parseFloat(style.paddingLeft);\n        let width = isImageWithoutPadding ? parseFloat(imageEl.scrollWidth)\n            : parseFloat(columnEl.scrollWidth) - (hasBackgroundColor ? 0 : 2 * paddingLeft);\n        const borderX = borderLeft + parseFloat(style.borderRight);\n        width += borderX + (hasBackgroundColor || isImageColumn ? 0 : 2 * defaultGridPadding);\n        let columnSpan = Math.round((width + columnGap) / (columnSize + columnGap));\n        if (columnSpan < 1) {\n            columnSpan = 1;\n        }\n        const columnStart = Math.round(columnLeft / (columnSize + columnGap)) + 1;\n        const columnEnd = columnStart + columnSpan;\n\n        // Vertical placement.\n        const borderTop = parseFloat(style.borderTop);\n        const columnTop = isImageWithoutPadding && !borderTop ? imageEl.offsetTop : columnEl.offsetTop;\n        // Getting the top and bottom paddings and computing the row offset.\n        const paddingTop = parseFloat(style.paddingTop);\n        const paddingBottom = parseFloat(style.paddingBottom);\n        const rowOffsetTop = Math.floor((paddingTop + rowGap) / (rowSize + rowGap));\n        // Getting the height of the column.\n        let height = isImageWithoutPadding ? parseFloat(imageEl.scrollHeight)\n            : parseFloat(columnEl.scrollHeight) - (hasBackgroundColor ? 0 : paddingTop + paddingBottom);\n        const borderY = borderTop + parseFloat(style.borderBottom);\n        height += borderY + (hasBackgroundColor || isImageColumn ? 0 : 2 * defaultGridPadding);\n        const rowSpan = Math.ceil((height + rowGap) / (rowSize + rowGap));\n        const rowStart = Math.round(columnTop / (rowSize + rowGap)) + 1 + (hasBackgroundColor || isImageWithoutPadding ? 0 : rowOffsetTop);\n        const rowEnd = rowStart + rowSpan;\n\n        columnEl.style.gridArea = `${rowStart} / ${columnStart} / ${rowEnd} / ${columnEnd}`;\n        columnEl.classList.add('o_grid_item');\n\n        // Adding the grid classes.\n        columnEl.classList.add('g-col-lg-' + columnSpan, 'g-height-' + rowSpan);\n        // Setting the initial z-index.\n        columnEl.style.zIndex = zIndex++;\n        // Setting the paddings.\n        if (hasBackgroundColor) {\n            columnEl.style.setProperty(\"--grid-item-padding-y\", `${paddingTop}px`);\n            columnEl.style.setProperty(\"--grid-item-padding-x\", `${paddingLeft}px`);\n        }\n        // Reload the images.\n        _reloadLazyImages(columnEl);\n\n        maxRowEnd = Math.max(rowEnd, maxRowEnd);\n        columnSpans.push(columnSpan);\n        imageColumns.push(isImageColumn);\n    }\n\n    for (const [i, columnEl] of [...columnEls].entries()) {\n        // Removing padding and offset classes.\n        const regex = /^(((pt|pb)\\d{1,3}$)|col-lg-|offset-lg-)/;\n        const toRemove = [...columnEl.classList].filter(c => {\n            return regex.test(c);\n        });\n        columnEl.classList.remove(...toRemove);\n        columnEl.classList.add('col-lg-' + columnSpans[i]);\n\n        // If the column only has an image, convert it.\n        if (imageColumns[i]) {\n            _convertImageColumn(columnEl);\n        }\n    }\n\n    return maxRowEnd;\n}\n/**\n * Removes and sets back the 'src' attribute of the images inside a column.\n * (To avoid the disappearing image problem in Chrome).\n *\n * @private\n * @param {Element} columnEl\n */\nexport function _reloadLazyImages(columnEl) {\n    const imageEls = columnEl.querySelectorAll('img');\n    for (const imageEl of imageEls) {\n        const src = imageEl.getAttribute(\"src\");\n        imageEl.src = '';\n        imageEl.src = src;\n    }\n}\n/**\n * Computes the column and row spans of the column thanks to its width and\n * height and returns them. Also adds the grid classes to the column.\n *\n * @private\n * @param {Element} rowEl\n * @param {Element} columnEl\n * @param {Number} columnWidth the width in pixels of the column.\n * @param {Number} columnHeight the height in pixels of the column.\n * @returns {Object}\n */\nexport function _convertColumnToGrid(rowEl, columnEl, columnWidth, columnHeight) {\n    // First, checking if the column only contains an image and if it is the\n    // case, converting it.\n    if (_checkIfImageColumn(columnEl)) {\n        _convertImageColumn(columnEl);\n    }\n\n    // Taking the grid padding into account.\n    const paddingX = parseFloat(rowEl.style.getPropertyValue(\"--grid-item-padding-x\")) || defaultGridPadding;\n    const paddingY = parseFloat(rowEl.style.getPropertyValue(\"--grid-item-padding-y\")) || defaultGridPadding;\n    columnWidth += 2 * paddingX;\n    columnHeight += 2 * paddingY;\n\n    // Computing the column and row spans.\n    const gridProp = _getGridProperties(rowEl);\n    const columnColCount = Math.round((columnWidth + gridProp.columnGap) / (gridProp.columnSize + gridProp.columnGap));\n    const columnRowCount = Math.ceil((columnHeight + gridProp.rowGap) / (gridProp.rowSize + gridProp.rowGap));\n\n    // Removing the padding and offset classes.\n    const regex = /^(pt|pb|col-|offset-)/;\n    const toRemove = [...columnEl.classList].filter(c => regex.test(c));\n    columnEl.classList.remove(...toRemove);\n\n    // Adding the grid classes.\n    columnEl.classList.add('g-col-lg-' + columnColCount, 'g-height-' + columnRowCount, 'col-lg-' + columnColCount);\n    columnEl.classList.add('o_grid_item');\n\n    return {columnColCount: columnColCount, columnRowCount: columnRowCount};\n}\n/**\n * Removes the grid properties from the grid column when it becomes a normal\n * column.\n *\n * @param {Element} columnEl\n */\nexport function _convertToNormalColumn(columnEl) {\n    const gridSizeClasses = columnEl.className.match(/(g-col-lg|g-height)-[0-9]+/g);\n    columnEl.classList.remove(\"o_grid_item\", \"o_grid_item_image\", \"o_grid_item_image_contain\", ...gridSizeClasses);\n    columnEl.style.removeProperty(\"z-index\");\n    columnEl.style.removeProperty(\"--grid-item-padding-x\");\n    columnEl.style.removeProperty(\"--grid-item-padding-y\");\n    columnEl.style.removeProperty(\"grid-area\");\n}\n/**\n * Checks whether the column only contains an image or not. An image is\n * considered alone if the column only contains empty textnodes and line breaks\n * in addition to the image. Note that \"image\" also refers to an image link\n * (i.e. `a > img`).\n *\n * @private\n * @param {Element} columnEl\n * @returns {Boolean}\n */\nexport function _checkIfImageColumn(columnEl) {\n    let isImageColumn = false;\n    const imageEls = columnEl.querySelectorAll(\":scope > img, :scope > a > img\");\n    const columnChildrenEls = [...columnEl.children].filter(el => el.nodeName !== 'BR');\n    if (imageEls.length === 1 && columnChildrenEls.length === 1) {\n        // If there is only one image and if this image is the only \"real\"\n        // child of the column, we need to check if there is text in it.\n        const textNodeEls = [...columnEl.childNodes].filter(el => el.nodeType === Node.TEXT_NODE);\n        const areTextNodesEmpty = [...textNodeEls].every(textNodeEl => textNodeEl.nodeValue.trim() === '');\n        isImageColumn = areTextNodesEmpty;\n    }\n    return isImageColumn;\n}\n/**\n * Removes the line breaks and textnodes of the column, adds the grid class and\n * sets the image width to default so it can be displayed as expected.\n *\n * @private\n * @param {Element} columnEl a column containing only an image.\n */\nfunction _convertImageColumn(columnEl) {\n    columnEl.querySelectorAll('br').forEach(el => el.remove());\n    const textNodeEls = [...columnEl.childNodes].filter(el => el.nodeType === Node.TEXT_NODE);\n    textNodeEls.forEach(el => el.remove());\n    const imageEl = columnEl.querySelector('img');\n    columnEl.classList.add('o_grid_item_image');\n    imageEl.style.removeProperty('width');\n}\n", "// Scrolling util functions needed by the frontend apps and sub-modules. These\n// functions indeed take into account all frontend-specific concepts (like the\n// header at the top of the page, the wrapwrap,...) which are not considered in\n// the `@web/core/utils/scrolling` utils.\n\nimport { getScrollingElement } from \"@web/core/utils/scrolling\";\n\n/**\n * Determines if an element is scrollable.\n *\n * @param {Element} element - the element to check\n * @returns {Boolean}\n */\nfunction isScrollable(element) {\n    if (!element) {\n        return false;\n    }\n    const overflowY = window.getComputedStyle(element).overflowY;\n    return overflowY === 'auto' || overflowY === 'scroll' ||\n        (overflowY === 'visible' && element === element.ownerDocument.scrollingElement);\n}\n\n/**\n * Finds the closest scrollable element for the given element.\n *\n * @param {Element} element - The element to find the closest scrollable element for.\n * @returns {Element} The closest scrollable element.\n */\nexport function closestScrollable(element) {\n    const document = element.ownerDocument || window.document;\n\n    while (element && element !== document.scrollingElement) {\n        if (element instanceof Document) {\n            return null;\n        }\n        if (isScrollable(element)) {\n            return element;\n        }\n        element = element.parentElement;\n    }\n    return element || document.scrollingElement;\n}\n\n/**\n * Computes the size by which a scrolling point should be decreased so that\n * the top fixed elements of the page appear above that scrolling point.\n *\n * @param {Document} [doc=document]\n * @returns {number}\n */\nfunction scrollFixedOffset(doc = document) {\n    let size = 0;\n    const elements = doc.querySelectorAll('.o_top_fixed_element');\n\n    elements.forEach(el => {\n        size += el.offsetHeight;\n    });\n\n    return size;\n}\n\n/**\n * @param {HTMLElement|string} el - the element to scroll to. If \"el\" is a\n *      string, it must be a valid selector of an element in the DOM or\n *      '#top' or '#bottom'. If it is an HTML element, it must be present\n *      in the DOM.\n *      Limitation: if the element is using a fixed position, this\n *      function cannot work except if is the header (el is then either a\n *      string set to '#top' or an HTML element with the \"top\" id) or the\n *      footer (el is then a string set to '#bottom' or an HTML element\n *      with the \"bottom\" id) for which exceptions have been made.\n * @param {number} [options] - options for the scroll behavior\n * @param {number} [options.extraOffset=0]\n *      extra offset to add on top of the automatic one (the automatic one\n *      being computed based on fixed header sizes)\n * @param {number} [options.forcedOffset]\n *      offset used instead of the automatic one (extraOffset will be\n *      ignored too)\n * @param {HTMLElement} [options.scrollable] the element to scroll\n * @param {number} [options.duration] the scroll duration in ms\n * @return {Promise}\n */\nexport function scrollTo(el, options = {}) {\n    if (!el) {\n        throw new Error(\"The scrollTo function was called without any given element\");\n    }\n    if (typeof el === 'string') {\n        el = document.querySelector(el);\n    }\n    const isTopOrBottomHidden = (el === \"top\" || el === \"bottom\");\n    const scrollable = isTopOrBottomHidden ? document.scrollingElement : (options.scrollable || closestScrollable(el.parentElement));\n    const scrollDocument = scrollable.ownerDocument;\n    const isInOneDocument = isTopOrBottomHidden || scrollDocument === el.ownerDocument;\n    const iframe = !isInOneDocument && Array.from(scrollable.querySelectorAll('iframe')).find(node => node.contentDocument.contains(el));\n    const topLevelScrollable = getScrollingElement(scrollDocument);\n\n    function _computeScrollTop() {\n        if (el === '#top' || el.id === 'top') {\n            return 0;\n        }\n        if (el === '#bottom' || el.id === 'bottom') {\n            return scrollable.scrollHeight - scrollable.clientHeight;\n        }\n\n        el.classList.add(\"o_check_scroll_position\");\n        let offsetTop = el.getBoundingClientRect().top + window.scrollY;\n        el.classList.remove(\"o_check_scroll_position\");\n        if (el.classList.contains('d-none')) {\n            el.classList.remove('d-none');\n            offsetTop = el.getBoundingClientRect().top + window.scrollY;\n            el.classList.add('d-none');\n        }\n        const isDocScrollingEl = scrollable === el.ownerDocument.scrollingElement;\n        let elPosition = offsetTop - (scrollable.getBoundingClientRect().top + window.scrollY - (isDocScrollingEl ? 0 : scrollable.scrollTop));\n        if (!isInOneDocument && iframe) {\n            elPosition += iframe.getBoundingClientRect().top + window.scrollY;\n        }\n        let offset = options.forcedOffset;\n        if (offset === undefined) {\n            offset = (scrollable === topLevelScrollable ? scrollFixedOffset(scrollDocument) : 0) + (options.extraOffset || 0);\n        }\n        return Math.max(0, elPosition - offset);\n    }\n\n    return new Promise(resolve => {\n        const start = scrollable.scrollTop;\n        const duration = options.duration || 600;\n        const startTime = performance.now();\n\n        function animateScroll(currentTime) {\n            const elapsedTime = currentTime - startTime;\n            const progress = Math.min(elapsedTime / duration, 1);\n            const easeInOutQuad = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;\n            // Recompute the scroll destination every time, to adapt to any\n            // occurring change that would modify the scroll offset.\n            const change = _computeScrollTop() - start;\n            const newScrollTop = start + change * easeInOutQuad;\n\n            scrollable.scrollTop = newScrollTop;\n\n            if (elapsedTime < duration) {\n                requestAnimationFrame(animateScroll);\n            } else {\n                resolve();\n            }\n        }\n\n        requestAnimationFrame(animateScroll);\n    });\n}\n", "/** @odoo-module **/\n\nimport {SIZES, MEDIAS_BREAKPOINTS} from \"@web/core/ui/ui_service\";\nimport {\n    normalizeCSSColor,\n    isCSSColor,\n} from '@web/core/utils/colors';\n\nlet editableWindow = window;\nconst _setEditableWindow = (ew) => editableWindow = ew;\nlet editableDocument = document;\nconst _setEditableDocument = (ed) => editableDocument = ed;\n\nconst COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES = ['primary', 'secondary', 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'success', 'info', 'warning', 'danger'];\n\n/**\n * These constants are colors that can be edited by the user when using\n * web_editor in a website context. We keep track of them so that color\n * palettes and their preview elements can always have the right colors\n * displayed even if website has redefined the colors during an editing\n * session.\n *\n * @type {string[]}\n */\nconst EDITOR_COLOR_CSS_VARIABLES = [...COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES];\n// o-cc and o-colors\nfor (let i = 1; i <= 5; i++) {\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-color-${i}`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-bg`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-bg-gradient`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-headings`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary-text`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-primary-border`);\n    EDITOR_COLOR_CSS_VARIABLES.push(`o-cc${i}-btn-secondary-border`);\n}\n// Grays\nfor (let i = 100; i <= 900; i += 100) {\n    EDITOR_COLOR_CSS_VARIABLES.push(`${i}`);\n}\n/**\n * window.getComputedStyle cannot work properly with CSS shortcuts (like\n * 'border-width' which is a shortcut for the top + right + bottom + left border\n * widths. If an option wants to customize such a shortcut, it should be listed\n * here with the non-shortcuts property it stands for, in order.\n *\n * @type {Object<string[]>}\n */\nconst CSS_SHORTHANDS = {\n    'border-width': ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],\n    'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],\n    'border-color': ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'],\n    'border-style': ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'],\n    'padding': ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'],\n};\n/**\n * Key-value mapping to list converters from an unit A to an unit B.\n * - The key is a string in the format '$1-$2' where $1 is the CSS symbol of\n *   unit A and $2 is the CSS symbol of unit B.\n * - The value is a function that converts the received value (expressed in\n *   unit A) to another value expressed in unit B. Two other parameters is\n *   received: the css property on which the unit applies and the jQuery element\n *   on which that css property may change.\n */\nconst CSS_UNITS_CONVERSION = {\n    's-ms': () => 1000,\n    'ms-s': () => 0.001,\n    'rem-px': () => _computePxByRem(),\n    'px-rem': () => _computePxByRem(true),\n    '%-px': () => -1, // Not implemented but should simply be ignored for now\n    'px-%': () => -1, // Not implemented but should simply be ignored for now\n};\n/**\n * Colors of the default palette, used for substitution in shapes/illustrations.\n * key: number of the color in the palette (ie, o-color-<1-5>)\n * value: color hex code\n */\nconst DEFAULT_PALETTE = {\n    '1': '#3AADAA',\n    '2': '#7C6576',\n    '3': '#F6F6F6',\n    '4': '#FFFFFF',\n    '5': '#383E45',\n};\n/**\n * Set of all the data attributes relative to the background images.\n */\nconst BACKGROUND_IMAGE_ATTRIBUTES = new Set([\n    \"originalId\", \"originalSrc\", \"mimetype\", \"resizeWidth\", \"glFilter\", \"quality\", \"bgSrc\",\n    \"filterOptions\",\n    \"mimetypeBeforeConversion\",\n]);\n\n/**\n * Computes the number of \"px\" needed to make a \"rem\" unit. Subsequent calls\n * returns the cached computed value.\n *\n * @param {boolean} [toRem=false]\n * @returns {float} - number of px by rem if 'toRem' is false\n *                  - the inverse otherwise\n */\nfunction _computePxByRem(toRem) {\n    if (editableDocument.PX_BY_REM === undefined) {\n        const htmlStyle = editableWindow.getComputedStyle(editableDocument.documentElement);\n        editableDocument.PX_BY_REM = parseFloat(htmlStyle['font-size']);\n    }\n    return toRem ? (1 / editableDocument.PX_BY_REM) : editableDocument.PX_BY_REM;\n}\n/**\n * Converts the given (value + unit) string to a numeric value expressed in\n * the other given css unit.\n *\n * e.g. fct('400ms', 's') -> 0.4\n *\n * @param {string} value\n * @param {string} unitTo\n * @param {string} [cssProp] - the css property on which the unit applies\n * @param {jQuery} [$target] - the jQuery element on which that css property\n *                             may change\n * @returns {number}\n */\nfunction _convertValueToUnit(value, unitTo, cssProp, $target) {\n    const m = _getNumericAndUnit(value);\n    if (!m) {\n        return NaN;\n    }\n    const numValue = parseFloat(m[0]);\n    const valueUnit = m[1];\n    return _convertNumericToUnit(numValue, valueUnit, unitTo, cssProp, $target);\n}\n/**\n * Converts the given numeric value expressed in the given css unit into\n * the corresponding numeric value expressed in the other given css unit.\n *\n * e.g. fct(400, 'ms', 's') -> 0.4\n *\n * @param {number} value\n * @param {string} unitFrom\n * @param {string} unitTo\n * @param {string} [cssProp] - the css property on which the unit applies\n * @param {jQuery} [$target] - the jQuery element on which that css property\n *                             may change\n * @returns {number}\n */\nfunction _convertNumericToUnit(value, unitFrom, unitTo, cssProp, $target) {\n    if (Math.abs(value) < Number.EPSILON || unitFrom === unitTo) {\n        return value;\n    }\n    const converter = CSS_UNITS_CONVERSION[`${unitFrom}-${unitTo}`];\n    if (converter === undefined) {\n        throw new Error(`Cannot convert '${unitFrom}' units into '${unitTo}' units !`);\n    }\n    return value * converter(cssProp, $target);\n}\n/**\n * Returns the numeric value and unit of a css value.\n *\n * e.g. fct('400ms') -> [400, 'ms']\n *\n * @param {string} value\n * @returns {Array|null}\n */\nfunction _getNumericAndUnit(value) {\n    const m = value.trim().match(/^(-?[0-9.]+(?:e[+|-]?[0-9]+)?)\\s*([^\\s]*)$/);\n    if (!m) {\n        return null;\n    }\n    return [m[1].trim(), m[2].trim()];\n}\n/**\n * Checks if two css values are equal.\n *\n * @param {string} value1\n * @param {string} value2\n * @param {string} [cssProp] - the css property on which the unit applies\n * @param {Node} [target] - the element on which that css property\n * @returns {boolean}\n */\nfunction _areCssValuesEqual(value1, value2, cssProp, target) {\n    const $target = $(target);\n    // String comparison first\n    if (value1 === value2) {\n        return true;\n    }\n\n    // In case the values are a size, they might be made of two parts.\n    if (cssProp && cssProp.endsWith('-size')) {\n        // Avoid re-splitting each part during their individual comparison.\n        const pseudoPartProp = cssProp + '-part';\n        const re = /-?[0-9.]+(?:e[+|-]?[0-9]+)?\\s*[A-Za-z%-]+|auto/g;\n        const parts1 = value1.match(re);\n        const parts2 = value2.match(re);\n        for (const index of [0, 1]) {\n            const part1 = parts1 && parts1.length > index ? parts1[index] : 'auto';\n            const part2 = parts2 && parts2.length > index ? parts2[index] : 'auto';\n            if (!_areCssValuesEqual(part1, part2, pseudoPartProp, $target)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    // It could be a CSS variable, in that case the actual value has to be\n    // retrieved before comparing.\n    if (value1.startsWith('var(--')) {\n        value1 = _getCSSVariableValue(value1.substring(6, value1.length - 1));\n    }\n    if (value2.startsWith('var(--')) {\n        value2 = _getCSSVariableValue(value2.substring(6, value2.length - 1));\n    }\n    if (value1 === value2) {\n        return true;\n    }\n\n    // They may be colors, normalize then re-compare the resulting string\n    const color1 = normalizeCSSColor(value1);\n    const color2 = normalizeCSSColor(value2);\n    if (color1 === color2) {\n        return true;\n    }\n\n    // They may be gradients\n    const value1IsGradient = _isColorGradient(value1);\n    const value2IsGradient = _isColorGradient(value2);\n    if (value1IsGradient !== value2IsGradient) {\n        return false;\n    }\n    if (value1IsGradient) {\n        // Kinda hacky and probably inneficient but probably the easiest way:\n        // applied the value as background-image of two fakes elements and\n        // compare their computed value.\n        const temp1El = document.createElement('div');\n        temp1El.style.backgroundImage = value1;\n        document.body.appendChild(temp1El);\n        value1 = getComputedStyle(temp1El).backgroundImage;\n        document.body.removeChild(temp1El);\n\n        const temp2El = document.createElement('div');\n        temp2El.style.backgroundImage = value2;\n        document.body.appendChild(temp2El);\n        value2 = getComputedStyle(temp2El).backgroundImage;\n        document.body.removeChild(temp2El);\n\n        return value1 === value2;\n    }\n\n    // In case the values are meant as box-shadow, this is difficult to compare.\n    // In this case we use the kinda hacky and probably inneficient but probably\n    // easiest way: applying the value as box-shadow of two fakes elements and\n    // compare their computed value.\n    if (cssProp === 'box-shadow') {\n        const temp1El = document.createElement('div');\n        temp1El.style.boxShadow = value1;\n        document.body.appendChild(temp1El);\n        value1 = getComputedStyle(temp1El).boxShadow;\n        document.body.removeChild(temp1El);\n\n        const temp2El = document.createElement('div');\n        temp2El.style.boxShadow = value2;\n        document.body.appendChild(temp2El);\n        value2 = getComputedStyle(temp2El).boxShadow;\n        document.body.removeChild(temp2El);\n\n        return value1 === value2;\n    }\n\n    // Convert the second value in the unit of the first one and compare\n    // floating values\n    const data = _getNumericAndUnit(value1);\n    if (!data) {\n        return false;\n    }\n    const numValue1 = data[0];\n    const numValue2 = _convertValueToUnit(value2, data[1], cssProp, $target);\n    return (Math.abs(numValue1 - numValue2) < Number.EPSILON);\n}\n/**\n * @param {string|number} name\n * @returns {boolean}\n */\nfunction _isColorCombinationName(name) {\n    const number = parseInt(name);\n    return (!isNaN(number) && number % 100 !== 0);\n}\n/**\n * @param {string[]} colorNames\n * @param {string} [prefix='bg-']\n * @returns {string[]}\n */\nfunction _computeColorClasses(colorNames, prefix = 'bg-') {\n    let hasCCClasses = false;\n    const isBgPrefix = (prefix === 'bg-');\n    const classes = colorNames.map(c => {\n        if (isBgPrefix && _isColorCombinationName(c)) {\n            hasCCClasses = true;\n            return `o_cc${c}`;\n        }\n        return (prefix + c);\n    });\n    if (hasCCClasses) {\n        classes.push('o_cc');\n    }\n    return classes;\n}\n/**\n * @param {string} key\n * @param {CSSStyleDeclaration} [htmlStyle] if not provided, it is computed\n * @returns {string}\n */\nfunction _getCSSVariableValue(key, htmlStyle) {\n    if (htmlStyle === undefined) {\n        htmlStyle = editableWindow.getComputedStyle(editableWindow.document.documentElement);\n    }\n    // Get trimmed value from the HTML element\n    let value = htmlStyle.getPropertyValue(`--${key}`).trim();\n    // If it is a color value, it needs to be normalized\n    value = normalizeCSSColor(value);\n    // Normally scss-string values are \"printed\" single-quoted. That way no\n    // magic conversation is needed when customizing a variable: either save it\n    // quoted for strings or non quoted for colors, numbers, etc. However,\n    // Chrome has the annoying behavior of changing the single-quotes to\n    // double-quotes when reading them through getPropertyValue...\n    return value.replace(/\"/g, \"'\");\n}\n/**\n * Normalize a color in case it is a variable name so it can be used outside of\n * css.\n *\n * @param {string} color the color to normalize into a css value\n * @returns {string} the normalized color\n */\nfunction _normalizeColor(color) {\n    if (isCSSColor(color)) {\n        return color;\n    }\n    return _getCSSVariableValue(color);\n}\n/**\n * Parse an element's background-image's url.\n *\n * @param {string} string a css value in the form 'url(\"...\")'\n * @returns {string|false} the src of the image or false if not parsable\n */\nfunction _getBgImageURL(el) {\n    const parts = _backgroundImageCssToParts($(el).css('background-image'));\n    const string = parts.url || '';\n    const match = string.match(/^url\\((['\"])(.*?)\\1\\)$/);\n    if (!match) {\n        return '';\n    }\n    const matchedURL = match[2];\n    // Make URL relative if possible\n    const fullURL = new URL(matchedURL, window.location.origin);\n    if (fullURL.origin === window.location.origin) {\n        return fullURL.href.slice(fullURL.origin.length);\n    }\n    return matchedURL;\n}\n/**\n * Extracts url and gradient parts from the background-image CSS property.\n *\n * @param {string} CSS 'background-image' property value\n * @returns {Object} contains the separated 'url' and 'gradient' parts\n */\nfunction _backgroundImageCssToParts(css) {\n    const parts = {};\n    css = css || '';\n    if (css.startsWith('url(')) {\n        const urlEnd = css.indexOf(')') + 1;\n        parts.url = css.substring(0, urlEnd).trim();\n        const commaPos = css.indexOf(',', urlEnd);\n        css = commaPos > 0 ? css.substring(commaPos + 1) : '';\n    }\n    if (_isColorGradient(css)) {\n        parts.gradient = css.trim();\n    }\n    return parts;\n}\n/**\n * Combines url and gradient parts into a background-image CSS property value\n *\n * @param {Object} contains the separated 'url' and 'gradient' parts\n * @returns {string} CSS 'background-image' property value\n */\nfunction _backgroundImagePartsToCss(parts) {\n    let css = parts.url || '';\n    if (parts.gradient) {\n        css += (css ? ', ' : '') + parts.gradient;\n    }\n    return css || 'none';\n}\n/**\n * @param {string} [value]\n * @returns {boolean}\n */\nfunction _isColorGradient(value) {\n    // FIXME duplicated in odoo-editor/utils.js\n    return value && value.includes('-gradient(');\n}\n/**\n * Generates a string ID.\n *\n * @private\n * @returns {string}\n */\nfunction _generateHTMLId() {\n    return `o${Math.random().toString(36).substring(2, 15)}`;\n}\n/**\n * Returns the class of the element that matches the specified prefix.\n *\n * @private\n * @param {Element} el element from which to recover the color class\n * @param {string[]} colorNames\n * @param {string} prefix prefix of the color class to recover\n * @returns {string} color class matching the prefix or an empty string\n */\nfunction _getColorClass(el, colorNames, prefix) {\n    const prefixedColorNames = _computeColorClasses(colorNames, prefix);\n    return el.classList.value.split(' ').filter(cl => prefixedColorNames.includes(cl)).join(' ');\n}\n/**\n * Add one or more new attributes related to background images in the\n * BACKGROUND_IMAGE_ATTRIBUTES set.\n *\n * @param {...string} newAttributes The new attributes to add in the\n * BACKGROUND_IMAGE_ATTRIBUTES set.\n */\nfunction _addBackgroundImageAttributes(...newAttributes) {\n    BACKGROUND_IMAGE_ATTRIBUTES.add(...newAttributes);\n}\n/**\n * Check if an attribute is in the BACKGROUND_IMAGE_ATTRIBUTES set.\n *\n * @param {string} attribute The attribute that has to be checked.\n */\nfunction _isBackgroundImageAttribute(attribute) {\n    return BACKGROUND_IMAGE_ATTRIBUTES.has(attribute);\n}\n/**\n * Checks if an element supposedly marked with the o_editable_media class should\n * in fact be editable (checks if its environment looks like a non editable\n * environment whose media should be editable).\n *\n * TODO: the name of this function is voluntarily bad to reflect the fact that\n * this system should be improved. The combination of o_not_editable,\n * o_editable, getContentEditableAreas, getReadOnlyAreas and other concepts\n * related to what should be editable or not should be reviewed.\n *\n * @returns {boolean}\n */\nfunction _shouldEditableMediaBeEditable(mediaEl) {\n    // Some sections of the DOM are contenteditable=\"false\" (for\n    // example with the help of the o_not_editable class) but have\n    // inner media that should be editable (the fact the container\n    // is not is to prevent adding text in between those medias).\n    // This case is complex and the solution to support it is not\n    // perfect: we mark those media with a class and check that the\n    // first non editable ancestor is in fact in an editable parent.\n    const parentEl = mediaEl.parentElement;\n    const nonEditableAncestorRootEl = parentEl && parentEl.closest('[contenteditable=\"false\"]');\n    return nonEditableAncestorRootEl\n        && nonEditableAncestorRootEl.parentElement\n        && nonEditableAncestorRootEl.parentElement.isContentEditable;\n}\n/**\n * Checks if the view of the targeted element is mobile.\n *\n * @param {HTMLElement} targetEl - target of the editor\n * @returns {boolean}\n */\nfunction _isMobileView(targetEl) {\n    const mobileViewThreshold = MEDIAS_BREAKPOINTS[SIZES.LG].minWidth;\n    const clientWidth = targetEl.ownerDocument.defaultView?.frameElement?.clientWidth ||\n        targetEl.ownerDocument.documentElement.clientWidth;\n    return clientWidth && clientWidth < mobileViewThreshold;\n}\n/**\n * Returns the label of a link element.\n *\n * @param {HTMLElement} linkEl\n * @returns {string}\n */\nfunction _getLinkLabel(linkEl) {\n    return linkEl.textContent.replaceAll(\"\\u200B\", \"\").replaceAll(\"\\uFEFF\", \"\");\n}\n/**\n * Forwards an image source to its carousel thumbnail.\n * @param {HTMLElement} imgEl\n */\nfunction _forwardToThumbnail(imgEl) {\n    const carouselEl = imgEl.closest(\".carousel\");\n    if (carouselEl) {\n        const carouselInnerEl = imgEl.closest(\".carousel-inner\");\n        const carouselItemEl = imgEl.closest(\".carousel-item\");\n        if (carouselInnerEl && carouselItemEl) {\n            const imageIndex = [...carouselInnerEl.children].indexOf(carouselItemEl);\n            const miniatureEl = carouselEl.querySelector(`.carousel-indicators [data-bs-slide-to=\"${imageIndex}\"]`);\n            if (miniatureEl && miniatureEl.style.backgroundImage) {\n                miniatureEl.style.backgroundImage = `url(${imgEl.getAttribute(\"src\")})`;\n            }\n        }\n    }\n}\n\n/**\n * @param {HTMLImageElement} img\n * @returns {Promise<Boolean>}\n */\nasync function _isImageCorsProtected(img) {\n    const src = img.getAttribute(\"src\");\n    if (!src) {\n        return false;\n    }\n    let isCorsProtected = false;\n    if (!src.startsWith(\"/\") || /\\/web\\/image\\/\\d+-redirect\\//.test(src)) {\n        // The `fetch()` used later in the code might fail if the image is\n        // CORS protected. We check upfront if it's the case.\n        // Two possible cases:\n        // 1. the `src` is an absolute URL from another domain.\n        //    For instance, abc.odoo.com vs abc.com which are actually the\n        //    same database behind.\n        // 2. A \"attachment-url\" which is just a redirect to the real image\n        //    which could be hosted on another website.\n        isCorsProtected = await fetch(src, { method: \"HEAD\" })\n            .then(() => false)\n            .catch(() => true);\n    }\n    return isCorsProtected;\n}\n\n/**\n * @param {string} src\n * @returns {Promise<Boolean>}\n */\nasync function _isSrcCorsProtected(src) {\n    const dummyImg = document.createElement(\"img\");\n    dummyImg.src = src;\n    return _isImageCorsProtected(dummyImg);\n}\n\nexport default {\n    COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES: COLOR_PALETTE_COMPATIBILITY_COLOR_NAMES,\n    CSS_SHORTHANDS: CSS_SHORTHANDS,\n    CSS_UNITS_CONVERSION: CSS_UNITS_CONVERSION,\n    DEFAULT_PALETTE: DEFAULT_PALETTE,\n    EDITOR_COLOR_CSS_VARIABLES: EDITOR_COLOR_CSS_VARIABLES,\n    computePxByRem: _computePxByRem,\n    convertValueToUnit: _convertValueToUnit,\n    convertNumericToUnit: _convertNumericToUnit,\n    getNumericAndUnit: _getNumericAndUnit,\n    areCssValuesEqual: _areCssValuesEqual,\n    isColorCombinationName: _isColorCombinationName,\n    isColorGradient: _isColorGradient,\n    computeColorClasses: _computeColorClasses,\n    getCSSVariableValue: _getCSSVariableValue,\n    normalizeColor: _normalizeColor,\n    getBgImageURL: _getBgImageURL,\n    backgroundImageCssToParts: _backgroundImageCssToParts,\n    backgroundImagePartsToCss: _backgroundImagePartsToCss,\n    generateHTMLId: _generateHTMLId,\n    getColorClass: _getColorClass,\n    setEditableWindow: _setEditableWindow,\n    setEditableDocument: _setEditableDocument,\n    addBackgroundImageAttributes: _addBackgroundImageAttributes,\n    isBackgroundImageAttribute: _isBackgroundImageAttribute,\n    shouldEditableMediaBeEditable: _shouldEditableMediaBeEditable,\n    isMobileView: _isMobileView,\n    getLinkLabel: _getLinkLabel,\n    forwardToThumbnail: _forwardToThumbnail,\n    isImageCorsProtected: _isImageCorsProtected,\n    isSrcCorsProtected: _isSrcCorsProtected,\n};\n", "/** @odoo-module **/\n\nexport function isImg(node) {\n    return (node && (node.nodeName === \"IMG\" || (node.className && node.className.match(/(^|\\s)(media_iframe_video|o_image|fa)(\\s|$)/i))));\n}\n\n/**\n * Returns a list of all the ancestors nodes of the provided node.\n *\n * @param {Node} node\n * @param {Node} [stopElement] include to prevent bubbling up further than the stopElement.\n * @returns {HTMLElement[]}\n */\nexport function ancestors(node, stopElement) {\n    if (!node || !node.parentElement || node === stopElement) return [];\n    return [node.parentElement, ...ancestors(node.parentElement, stopElement)];\n}", "import { App, Component, useState, xml } from \"@odoo/owl\";\nimport { getTemplate } from \"@web/core/templates\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nconst rootTemplate = xml`<SubComp t-props=\"state\"/>`;\nexport async function attachComponent(parent, element, componentClass, props = {}) {\n    class Root extends Component {\n        static template = rootTemplate;\n        static components = { SubComp: componentClass };\n        static props = [\"*\"];\n        state = useState(props);\n    }\n\n    const env = Component.env;\n    const app = new App(Root, {\n        env,\n        getTemplate,\n        dev: env.debug,\n        translatableAttributes: [\"data-tooltip\"],\n        translateFn: _t,\n    });\n\n    if (parent.__parentedMixin) {\n        parent.__parentedChildren.push({\n            get $el() {\n                return $(app.root.el);\n            },\n            destroy() {\n                app.destroy();\n            },\n        });\n    }\n\n    const originalValidateTarget = App.validateTarget;\n    App.validateTarget = () => {};\n    const mountPromise = app.mount(element);\n    App.validateTarget = originalValidateTarget;\n    const component = await mountPromise;\n    const subComp = Object.values(component.__owl__.children)[0].component;\n    return {\n        component: subComp,\n        destroy() {\n            app.destroy();\n        },\n        update(props) {\n            Object.assign(component.state, props);\n        },\n    };\n}\n", "/** @odoo-module **/\n\nexport const DIRECTIONS = {\n    LEFT: false,\n    RIGHT: true,\n};\nexport const CTYPES = {\n    // Short for CONTENT_TYPES\n    // Inline group\n    CONTENT: 1,\n    SPACE: 2,\n\n    // Block group\n    BLOCK_OUTSIDE: 4,\n    BLOCK_INSIDE: 8,\n\n    // Br group\n    BR: 16,\n};\nexport function ctypeToString(ctype) {\n    return Object.keys(CTYPES).find((key) => CTYPES[key] === ctype);\n}\nexport const CTGROUPS = {\n    // Short for CONTENT_TYPE_GROUPS\n    INLINE: CTYPES.CONTENT | CTYPES.SPACE,\n    BLOCK: CTYPES.BLOCK_OUTSIDE | CTYPES.BLOCK_INSIDE,\n    BR: CTYPES.BR,\n};\nconst tldWhitelist = [\n    'com', 'net', 'org', 'ac', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'an',\n    'ao', 'aq', 'ar', 'as', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd',\n    'be', 'bf', 'bg', 'bh', 'bi', 'bj', 'bl', 'bm', 'bn', 'bo', 'br', 'bq',\n    'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch',\n    'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cr', 'cs', 'cu', 'cv', 'cw', 'cx',\n    'cy', 'cz', 'dd', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'ee', 'eg',\n    'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga',\n    'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gp', 'gq',\n    'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu',\n    'id', 'ie', 'il', 'im', 'in', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm',\n    'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky',\n    'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly',\n    'ma', 'mc', 'md', 'me', 'mf', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo',\n    'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na',\n    'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om',\n    'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt',\n    'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd',\n    'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'ss',\n    'st', 'su', 'sv', 'sx', 'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj',\n    'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua',\n    'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn',\n    'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm', 'zr', 'zw', 'co\\\\.uk'];\n\nconst urlRegexBase = `|(?:www.))[-a-zA-Z0-9@:%._\\\\+~#=]{2,256}\\\\.[a-zA-Z][a-zA-Z0-9]{1,62}|(?:[-a-zA-Z0-9@:%._\\\\+~#=]{2,256}\\\\.(?:${tldWhitelist.join('|')})\\\\b))(?:(?:[/?#])[^\\\\s]*[^!.,})\\\\]'\"\\\\s]|(?:[^!(){}.,[\\\\]'\"\\\\s]+))?`;\nconst httpCapturedRegex= `(https?:\\\\/\\\\/)`;\n\nexport const URL_REGEX = new RegExp(`((?:(?:${httpCapturedRegex}${urlRegexBase})`, 'i');\nexport const YOUTUBE_URL_GET_VIDEO_ID =\n    /^(?:(?:https?:)?\\/\\/)?(?:(?:www|m)\\.)?(?:youtube\\.com|youtu\\.be)(?:\\/(?:[\\w-]+\\?v=|embed\\/|v\\/)?)([^\\s?&#]+)(?:\\S+)?$/i;\nexport const EMAIL_REGEX = /^(mailto:)?[\\w-.]+@(?:[\\w-]+\\.)+[\\w-]{2,4}$/i;\nexport const PHONE_REGEX = /^(tel:(?:\\/\\/)?)?\\+?[\\d\\s.\\-()\\/]{3,25}$/;\n\nexport const PROTECTED_BLOCK_TAG = ['TR','TD','TABLE','TBODY','UL','OL','LI'];\n\n/**\n * Array of all the classes used by the editor to change the font size.\n *\n * Note: the Bootstrap \"small\" class is an exception, the editor does not allow\n * to set it but it did in the past and we want to remove it when applying an\n * override of the font-size.\n */\nexport const FONT_SIZE_CLASSES = [\"display-1-fs\", \"display-2-fs\", \"display-3-fs\", \"display-4-fs\", \"h1-fs\",\n    \"h2-fs\", \"h3-fs\", \"h4-fs\", \"h5-fs\", \"h6-fs\", \"base-fs\", \"o_small-fs\", \"small\"];\n\n/**\n * Array of all the classes used by the editor to change the text style.\n *\n * Note: the Bootstrap \"small\" class was actually part of \"text style\"\n * configuration in the past... but also of the \"font size\" configuration (see\n * FONT_SIZE_CLASSES). It should be mentioned here too.\n */\nexport const TEXT_STYLE_CLASSES = [\"display-1\", \"display-2\", \"display-3\", \"display-4\", \"lead\", \"o_small\", \"small\"];\n\nconst ZWNBSP_CHAR = '\\ufeff';\nexport const ZERO_WIDTH_CHARS = ['\\u200b', ZWNBSP_CHAR];\nexport const ZERO_WIDTH_CHARS_REGEX = new RegExp(`[${ZERO_WIDTH_CHARS.join('')}]`, 'g');\n\n//------------------------------------------------------------------------------\n// Position and sizes\n//------------------------------------------------------------------------------\n\n/**\n * @param {Node} node\n * @returns {Array.<HTMLElement, number>}\n */\nexport function leftPos(node) {\n    return [node.parentNode, childNodeIndex(node)];\n}\n/**\n * @param {Node} node\n * @returns {Array.<HTMLElement, number>}\n */\nexport function rightPos(node) {\n    return [node.parentNode, childNodeIndex(node) + 1];\n}\n/**\n * @param {Node} node\n * @returns {Array.<HTMLElement, number, HTMLElement, number>}\n */\nexport function boundariesOut(node) {\n    const index = childNodeIndex(node);\n    return [node.parentNode, index, node.parentNode, index + 1];\n}\n/**\n * @param {Node} node\n * @returns {Array.<Node, number>}\n */\nexport function startPos(node) {\n    return [node, 0];\n}\n/**\n * @param {Node} node\n * @returns {Array.<Node, number>}\n */\nexport function endPos(node) {\n    return [node, nodeSize(node)];\n}\n/**\n * @param {Node} node\n * @returns {Array.<node, number, node, number>}\n */\nexport function boundariesIn(node) {\n    return [node, 0, node, nodeSize(node)];\n}\n/**\n * Returns the given node's position relative to its parent (= its index in the\n * child nodes of its parent).\n *\n * @param {Node} node\n * @returns {number}\n */\nexport function childNodeIndex(node) {\n    let i = 0;\n    while (node.previousSibling) {\n        i++;\n        node = node.previousSibling;\n    }\n    return i;\n}\n/**\n * Returns the size of the node = the number of characters for text nodes and\n * the number of child nodes for element nodes.\n *\n * @param {Node} node\n * @returns {number}\n */\nexport function nodeSize(node) {\n    const isTextNode = node.nodeType === Node.TEXT_NODE;\n    return isTextNode ? node.length : node.childNodes.length;\n}\n\n//------------------------------------------------------------------------------\n// DOM Path and node search functions\n//------------------------------------------------------------------------------\n\nexport const closestPath = function* (node) {\n    while (node) {\n        yield node;\n        node = node.parentNode;\n    }\n};\n\n/**\n * Values which can be returned while browsing the DOM which gives information\n * to why the path ended.\n */\nconst PATH_END_REASONS = {\n    NO_NODE: 0,\n    BLOCK_OUT: 1,\n    BLOCK_HIT: 2,\n    OUT_OF_SCOPE: 3,\n};\n/**\n * Creates a generator function according to the given parameters. Pre-made\n * generators to traverse the DOM are made using this function:\n *\n * @see leftLeafFirstPath\n * @see leftLeafOnlyNotBlockPath\n * @see leftLeafOnlyInScopeNotBlockEditablePath\n * @see rightLeafOnlyNotBlockPath\n * @see rightLeafOnlyPathNotBlockNotEditablePath\n * @see rightLeafOnlyInScopeNotBlockEditablePath\n * @see rightLeafOnlyNotBlockNotEditablePath\n *\n * @param {number} direction\n * @param {boolean} [options.leafOnly] if true, do not yield any non-leaf node\n * @param {boolean} [options.inScope] if true, stop the generator as soon as a node is not\n *                      a descendant of `node` provided when traversing the\n *                      generated function.\n * @param {Function} [options.stopTraverseFunction] a function that takes a node\n *                      and should return true when a node descendant should not\n *                      be traversed.\n * @param {Function} [options.stopFunction] function that makes the generator stop when a\n *                      node is encountered.\n */\nexport function createDOMPathGenerator(\n    direction,\n    { leafOnly = false, inScope = false, stopTraverseFunction, stopFunction } = {},\n) {\n    const nextDeepest =\n        direction === DIRECTIONS.LEFT\n            ? node => lastLeaf(node.previousSibling, stopTraverseFunction)\n            : node => firstLeaf(node.nextSibling, stopTraverseFunction);\n\n    const firstNode =\n        direction === DIRECTIONS.LEFT\n            ? (node, offset) => lastLeaf(node.childNodes[offset - 1], stopTraverseFunction)\n            : (node, offset) => firstLeaf(node.childNodes[offset], stopTraverseFunction);\n\n    // Note \"reasons\" is a way for the caller to be able to know why the\n    // generator ended yielding values.\n    return function* (node, offset, reasons = []) {\n        let movedUp = false;\n\n        let currentNode = firstNode(node, offset);\n        if (!currentNode) {\n            movedUp = true;\n            currentNode = node;\n        }\n\n        while (currentNode) {\n            if (stopFunction && stopFunction(currentNode)) {\n                reasons.push(movedUp ? PATH_END_REASONS.BLOCK_OUT : PATH_END_REASONS.BLOCK_HIT);\n                break;\n            }\n            if (inScope && currentNode === node) {\n                reasons.push(PATH_END_REASONS.OUT_OF_SCOPE);\n                break;\n            }\n            if (!(leafOnly && movedUp)) {\n                yield currentNode;\n            }\n\n            movedUp = false;\n            let nextNode = nextDeepest(currentNode);\n            if (!nextNode) {\n                movedUp = true;\n                nextNode = currentNode.parentNode;\n            }\n            currentNode = nextNode;\n        }\n\n        reasons.push(PATH_END_REASONS.NO_NODE);\n    };\n}\n\n/**\n * Find a node.\n * @param {findCallback} findCallback - This callback check if this function\n *      should return `node`.\n * @param {findCallback} stopCallback - This callback check if this function\n *      should stop when it receive `node`.\n */\nexport function findNode(domPath, findCallback = () => true, stopCallback = () => false) {\n    for (const node of domPath) {\n        if (findCallback(node)) {\n            return node;\n        }\n        if (stopCallback(node)) {\n            break;\n        }\n    }\n    return null;\n}\n/**\n * This callback check if findNode should return `node`.\n * @callback findCallback\n * @param {Node} node\n * @return {Boolean}\n */\n/**\n * This callback check if findNode should stop when it receive `node`.\n * @callback stopCallback\n * @param {Node} node\n */\n\n/**\n * Return the furthest uneditable parent of node contained within parentLimit.\n * @see deleteRange Used to guarantee that uneditables are fully contained in\n * the range (so that it is not possible to partially remove them)\n *\n * @param {Node} node\n * @param {Node} [parentLimit=undefined] non-inclusive furthest parent allowed\n * @returns {Node} uneditable parent if it exists\n */\nexport function getFurthestUneditableParent(node, parentLimit) {\n    if (node === parentLimit || (parentLimit && !parentLimit.contains(node))) {\n        return undefined;\n    }\n    let parent = node && node.parentElement;\n    let nonEditableElement;\n    while (parent && (!parentLimit || parent !== parentLimit)) {\n        if (!parent.isContentEditable) {\n            nonEditableElement = parent;\n        }\n        if (parent.oid === \"root\") {\n            break;\n        }\n        parent = parent.parentElement;\n    }\n    return nonEditableElement;\n}\n/**\n * Returns the closest HTMLElement of the provided Node. If the predicate is a\n * string, returns the closest HTMLElement that match the predicate selector. If\n * the predicate is a function, returns the closest element that matches the\n * predicate. Any returned element will be contained within the editable.\n *\n * @param {Node} node\n * @param {string | Function} [predicate='*']\n * @returns {HTMLElement|null}\n */\nexport function closestElement(node, predicate = \"*\") {\n    if (!node) return null;\n    let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n    if (typeof predicate === 'function') {\n        while (element && !predicate(element)) {\n            element = element.parentElement;\n        }\n    } else {\n        element = element?.closest(predicate);\n    }\n\n    return element?.closest('.odoo-editor-editable') && element;\n}\n\n/**\n * Returns a list of all the ancestors nodes of the provided node.\n *\n * @param {Node} node\n * @param {Node} [editable] include to prevent bubbling up further than the editable.\n * @returns {HTMLElement[]}\n */\nexport function ancestors(node, editable) {\n    if (!node || !node.parentElement || node === editable) return [];\n    return [node.parentElement, ...ancestors(node.parentElement, editable)];\n}\n\n/**\n * Take a node, return all of its descendants, in depth-first order.\n *\n * @param {Node} node\n * @returns {Node[]}\n */\nexport function descendants(node) {\n    const posterity = [];\n    for (const child of (node.childNodes || [])) {\n        posterity.push(child, ...descendants(child));\n    }\n    return posterity;\n}\n\nexport function closestBlock(node) {\n    return findNode(closestPath(node), node => isBlock(node));\n}\n/**\n * Returns the deepest child in last position.\n *\n * @param {Node} node\n * @param {Function} [stopTraverseFunction]\n * @returns {Node}\n */\nexport function lastLeaf(node, stopTraverseFunction) {\n    while (node && node.lastChild && !(stopTraverseFunction && stopTraverseFunction(node))) {\n        node = node.lastChild;\n    }\n    return node;\n}\n/**\n * Returns the deepest child in first position.\n *\n * @param {Node} node\n * @param {Function} [stopTraverseFunction]\n * @returns {Node}\n */\nexport function firstLeaf(node, stopTraverseFunction) {\n    while (node && node.firstChild && !(stopTraverseFunction && stopTraverseFunction(node))) {\n        node = node.firstChild;\n    }\n    return node;\n}\nexport function previousLeaf(node, editable, skipInvisible = false) {\n    let ancestor = node;\n    while (ancestor && !ancestor.previousSibling && ancestor !== editable) {\n        ancestor = ancestor.parentElement;\n    }\n    if (ancestor && ancestor !== editable) {\n        if (skipInvisible && !isVisible(ancestor.previousSibling)) {\n            return previousLeaf(ancestor.previousSibling, editable, skipInvisible);\n        } else {\n            const last = lastLeaf(ancestor.previousSibling);\n            if (skipInvisible && !isVisible(last)) {\n                return previousLeaf(last, editable, skipInvisible);\n            } else {\n                return last;\n            }\n        }\n    }\n}\nexport function nextLeaf(node, editable, skipInvisible = false) {\n    let ancestor = node;\n    while (ancestor && !ancestor.nextSibling && ancestor !== editable) {\n        ancestor = ancestor.parentElement;\n    }\n    if (ancestor && ancestor !== editable) {\n        if (skipInvisible && ancestor.nextSibling && !isVisible(ancestor.nextSibling)) {\n            return nextLeaf(ancestor.nextSibling, editable, skipInvisible);\n        } else {\n            const first = firstLeaf(ancestor.nextSibling);\n            if (skipInvisible && !isVisible(first)) {\n                return nextLeaf(first, editable, skipInvisible);\n            } else {\n                return first;\n            }\n        }\n    }\n}\n/**\n * Returns all the previous siblings of the given node until the first\n * sibling that does not satisfy the predicate, in lookup order.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacentPreviousSiblings(node, predicate = n => !!n) {\n    let previous = node.previousSibling;\n    const list = [];\n    while (previous && predicate(previous)) {\n        list.push(previous);\n        previous = previous.previousSibling;\n    }\n    return list;\n}\n/**\n * Returns all the next siblings of the given node until the first\n * sibling that does not satisfy the predicate, in lookup order.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacentNextSiblings(node, predicate = n => !!n) {\n    let next = node.nextSibling;\n    const list = [];\n    while (next && predicate(next)) {\n        list.push(next);\n        next = next.nextSibling;\n    }\n    return list;\n}\n/**\n * Returns all the adjacent siblings of the given node until the first sibling\n * (in both directions) that does not satisfy the predicate, in index order. If\n * the given node does not satisfy the predicate, an empty array is returned.\n *\n * @param {Node} node\n * @param {Function} [predicate] (node: Node) => boolean\n */\nexport function getAdjacents(node, predicate = n => !!n) {\n    const previous = getAdjacentPreviousSiblings(node, predicate);\n    const next = getAdjacentNextSiblings(node, predicate);\n    return predicate(node) ? [...previous.reverse(), node, ...next] : [];\n}\n\n//------------------------------------------------------------------------------\n// Cursor management\n//------------------------------------------------------------------------------\n\n/**\n * Returns true if the given editable area contains a table with selected cells.\n *\n * @param {Element} editable\n * @returns {boolean}\n */\nexport function hasTableSelection(editable) {\n    return !!editable.querySelector('.o_selected_table');\n}\n/**\n * Returns true if the given editable area contains a \"valid\" selection, by\n * which we mean a browser selection whose elements are defined, or a table with\n * selected cells.\n *\n * @param {Element} editable\n * @returns {boolean}\n */\nexport function hasValidSelection(editable) {\n    return hasTableSelection(editable) || editable.ownerDocument.getSelection().rangeCount > 0;\n}\n/**\n * From a given position, returns the normalized version.\n *\n * E.g. <b>abc</b>[]def -> <b>abc[]</b>def\n *\n * @param {Node} node\n * @param {number} offset\n * @param {boolean} [full=true] (if not full, it means we only normalize\n *     positions which are not possible, like the cursor inside an image).\n */\nexport function getNormalizedCursorPosition(node, offset, full = true) {\n    const editable = closestElement(node, '.odoo-editor-editable');\n    let closest = closestElement(node);\n    while (\n        closest &&\n        closest !== editable &&\n        (isSelfClosingElement(node) || !closest.isContentEditable)\n    ) {\n        // Cannot put the cursor inside those elements, put it before if the\n        // offset is 0 and the node is not empty, else after instead.\n        [node, offset] = offset || !nodeSize(node) ? rightPos(node) : leftPos(node);\n        closest = closestElement(node);\n    }\n\n    // Be permissive about the received offset.\n    offset = Math.min(Math.max(offset, 0), nodeSize(node));\n\n    if (full) {\n        // Put the cursor in deepest inline node around the given position if\n        // possible.\n        let el;\n        let elOffset;\n        if (node.nodeType === Node.ELEMENT_NODE) {\n            el = node;\n            elOffset = offset;\n        } else if (node.nodeType === Node.TEXT_NODE) {\n            if (offset === 0) {\n                el = node.parentNode;\n                elOffset = childNodeIndex(node);\n            } else if (offset === node.length) {\n                el = node.parentNode;\n                elOffset = childNodeIndex(node) + 1;\n            }\n        }\n        if (el) {\n            const leftInlineNode = leftLeafOnlyInScopeNotBlockEditablePath(el, elOffset).next().value;\n            let leftVisibleEmpty = false;\n            if (leftInlineNode) {\n                leftVisibleEmpty =\n                    isSelfClosingElement(leftInlineNode) ||\n                    !closestElement(leftInlineNode).isContentEditable;\n                [node, offset] = leftVisibleEmpty\n                    ? rightPos(leftInlineNode)\n                    : endPos(leftInlineNode);\n            }\n            if (!leftInlineNode || leftVisibleEmpty) {\n                const rightInlineNode = rightLeafOnlyInScopeNotBlockEditablePath(el, elOffset).next().value;\n                if (rightInlineNode) {\n                    const closest = closestElement(rightInlineNode);\n                    const rightVisibleEmpty =\n                        isSelfClosingElement(rightInlineNode) ||\n                        !closest ||\n                        !closest.isContentEditable;\n                    if (!(leftVisibleEmpty && rightVisibleEmpty)) {\n                        [node, offset] = rightVisibleEmpty\n                            ? leftPos(rightInlineNode)\n                            : startPos(rightInlineNode);\n                    }\n                }\n            }\n        }\n    }\n\n    const prevNode = node.nodeType === Node.ELEMENT_NODE && node.childNodes[offset - 1];\n    if (prevNode && prevNode.nodeName === 'BR' && isFakeLineBreak(prevNode)) {\n        // If trying to put the cursor on the right of a fake line break, put\n        // it before instead.\n        offset--;\n    }\n\n    return [node, offset];\n}\nexport function insertSelectionChars(anchorNode, anchorOffset, focusNode, focusOffset, startChar='[', endChar=']') {\n    // If the range characters have to be inserted within the same parent and\n    // the anchor range character has to be before the focus range character,\n    // the focus offset needs to be adapted to account for the first insertion.\n    if (anchorNode === focusNode && anchorOffset <= focusOffset) {\n        focusOffset += (focusNode.nodeType === Node.TEXT_NODE ? startChar.length : 1);\n    }\n    insertCharsAt(startChar, anchorNode, anchorOffset);\n    insertCharsAt(endChar, focusNode, focusOffset);\n}\n/**\n * Log the contents of the given root, with the characters \"[\" and \"]\" around\n * the selection.\n *\n * @param {Element} root\n * @param {Object} [options={}]\n * @param {Selection} [options.selection] if undefined, the current selection is used.\n * @param {boolean} [options.doFormat] if true, the HTML is formatted.\n * @param {boolean} [options.includeOids] if true, the HTML is formatted.\n */\nexport function logSelection(root, options = {}) {\n    const sel = options.selection || root.ownerDocument.getSelection();\n    if (!root.contains(sel.anchorNode) || !root.contains(sel.focusNode)) {\n        console.warn('The selection is not contained in the root.');\n        return;\n    }\n\n    // Clone the root and its contents.\n    let anchorClone, focusClone;\n    const cloneTree = node => {\n        const clone = node.cloneNode();\n        if (options.includeOids) {\n            clone.oid = node.oid;\n        }\n        anchorClone = anchorClone || (node === sel.anchorNode && clone);\n        focusClone = focusClone || (node === sel.focusNode && clone);\n        for (const child of node.childNodes || []) {\n            clone.append(cloneTree(child));\n        }\n        return clone;\n    }\n    const rootClone = cloneTree(root);\n\n    // Insert the selection characters.\n    insertSelectionChars(anchorClone, sel.anchorOffset, focusClone, sel.focusOffset, '%c[%c', '%c]%c');\n\n    // Remove information that is not useful for the log.\n    rootClone.removeAttribute('data-last-history-steps');\n\n    // Format the HTML by splitting and indenting to highlight the structure.\n    if (options.doFormat) {\n        const formatHtml = (node, spaces = 0) => {\n            node.before(document.createTextNode('\\n' + ' '.repeat(spaces)));\n            for (const child of [...node.childNodes]) {\n                formatHtml(child, spaces + 4);\n            }\n            if (node.nodeType !== Node.TEXT_NODE) {\n                node.appendChild(document.createTextNode('\\n' + ' '.repeat(spaces)));\n            }\n            if (options.includeOids) {\n                if (node.nodeType === Node.TEXT_NODE) {\n                    node.textContent += ` (${node.oid})`;\n                } else {\n                    node.setAttribute('oid', node.oid);\n                }\n            }\n        }\n        formatHtml(rootClone);\n    }\n\n    // Style and log the result.\n    const selectionCharacterStyle = 'color: #75bfff; font-weight: 700;';\n    const defaultStyle = 'color: inherit; font-weight: inherit;';\n    console.log(\n        makeZeroWidthCharactersVisible(rootClone.outerHTML),\n        selectionCharacterStyle, defaultStyle, selectionCharacterStyle, defaultStyle,\n    );\n}\n/**\n * Guarantee that the focus is on element or one of its children.\n *\n * A simple call to element.focus will change the editable context\n * if one of the parents of the current activeElement is not editable,\n * and the caret position will not be preserved, even if activeElement is\n * one of the subchildren of element. This is why the (re)focus is\n * only called when the current activeElement is not one of the\n * (sub)children of element.\n *\n * @param {Element} element should have the focus or a child with the focus\n */\n export function ensureFocus(element) {\n    const activeElement = element.ownerDocument.activeElement;\n    if (activeElement !== element && (!element.contains(activeElement) || !activeElement.isContentEditable)) {\n        element.focus();\n    }\n}\n/**\n * @param {Node} anchorNode\n * @param {number} anchorOffset\n * @param {Node} focusNode\n * @param {number} focusOffset\n * @param {boolean} [normalize=true]\n * @returns {?Array.<Node, number}\n */\nexport function setSelection(\n    anchorNode,\n    anchorOffset,\n    focusNode = anchorNode,\n    focusOffset = anchorOffset,\n    normalize = true,\n) {\n    if (\n        !anchorNode ||\n        !anchorNode.parentElement ||\n        !anchorNode.parentElement.closest('body') ||\n        !focusNode ||\n        !focusNode.parentElement ||\n        !focusNode.parentElement.closest('body')\n    ) {\n        return null;\n    }\n    const document = anchorNode.ownerDocument;\n\n    const seemsCollapsed = anchorNode === focusNode && anchorOffset === focusOffset;\n    [anchorNode, anchorOffset] = getNormalizedCursorPosition(anchorNode, anchorOffset, normalize);\n    [focusNode, focusOffset] = seemsCollapsed\n        ? [anchorNode, anchorOffset]\n        : getNormalizedCursorPosition(focusNode, focusOffset, normalize);\n\n    const direction = getCursorDirection(anchorNode, anchorOffset, focusNode, focusOffset);\n    const sel = document.getSelection();\n    if (!sel) {\n        return null;\n    }\n    try {\n        const range = new Range();\n        if (direction === DIRECTIONS.RIGHT) {\n            range.setStart(anchorNode, anchorOffset);\n            range.collapse(true);\n        } else {\n            range.setEnd(anchorNode, anchorOffset);\n            range.collapse(false);\n        }\n        sel.removeAllRanges();\n        sel.addRange(range);\n        sel.extend(focusNode, focusOffset);\n    } catch (e) {\n        // Firefox throws NS_ERROR_FAILURE when setting selection on element\n        // with contentEditable=false for no valid reason since non-editable\n        // content are selectable by the user anyway.\n        if (e.name !== 'NS_ERROR_FAILURE') {\n            throw e;\n        }\n    }\n\n    return [anchorNode, anchorOffset, focusNode, focusOffset];\n}\n/**\n * @param {Node} node\n * @param {boolean} [normalize=true]\n * @returns {?Array.<Node, number}\n */\nexport function setCursorStart(node, normalize = true) {\n    const pos = startPos(node);\n    return setSelection(...pos, ...pos, normalize);\n}\n/**\n * @param {Node} node\n * @param {boolean} [normalize=true]\n * @returns {?Array.<Node, number}\n */\nexport function setCursorEnd(node, normalize = true) {\n    const pos = endPos(node);\n    return setSelection(...pos, ...pos, normalize);\n}\n/**\n * From selection position, checks if it is left-to-right or right-to-left.\n *\n * @param {Node} anchorNode\n * @param {number} anchorOffset\n * @param {Node} focusNode\n * @param {number} focusOffset\n * @returns {boolean} the direction of the current range if the selection not is collapsed | false\n */\nexport function getCursorDirection(anchorNode, anchorOffset, focusNode, focusOffset) {\n    if (anchorNode === focusNode) {\n        if (anchorOffset === focusOffset) return false;\n        return anchorOffset < focusOffset ? DIRECTIONS.RIGHT : DIRECTIONS.LEFT;\n    }\n    return anchorNode.compareDocumentPosition(focusNode) & Node.DOCUMENT_POSITION_FOLLOWING\n        ? DIRECTIONS.RIGHT\n        : DIRECTIONS.LEFT;\n}\n/**\n * Returns an array containing all the nodes traversed when walking the\n * selection.\n *\n * @param {Node} editable\n * @returns {Node[]}\n */\nexport function getTraversedNodes(editable, range = getDeepRange(editable)) {\n    const selectedTableCells = editable.querySelectorAll('.o_selected_td');\n    const document = editable.ownerDocument;\n    if (!range) return [];\n    const iterator = document.createNodeIterator(range.commonAncestorContainer);\n    let node;\n    do {\n        node = iterator.nextNode();\n    } while (node && node !== range.startContainer && !(selectedTableCells.length && node === selectedTableCells[0]));\n    if (\n        node &&\n        !(selectedTableCells.length && node === selectedTableCells[0]) &&\n        !range.collapsed &&\n        node.nodeType === Node.ELEMENT_NODE &&\n        node.childNodes.length &&\n        range.startOffset &&\n        node.childNodes[range.startOffset - 1].nodeName === \"BR\"\n    ) {\n        // Handle the cases:\n        // <p>ab<br>[</p><p>cd</p>] => [p2, cd]\n        // <p>ab<br>[<br>cd</p><p>ef</p>] => [br2, cd, p2, ef]\n        const targetBr = node.childNodes[range.startOffset - 1];\n        while (node != targetBr) {\n            node = iterator.nextNode();\n        }\n        node = iterator.nextNode();\n    }\n    if (\n        node &&\n        !range.collapsed &&\n        node === range.startContainer &&\n        range.startOffset === nodeSize(node) &&\n        node.nextSibling &&\n        node.nextSibling.nodeName === \"BR\"\n    ) {\n        // Handle the case: <p>ab[<br>cd</p><p>ef</p>] => [br, cd, p2, ef]\n        node = iterator.nextNode();\n    }\n    const traversedNodes = new Set([node, ...descendants(node)]);\n    while (node && node !== range.endContainer) {\n        node = iterator.nextNode();\n        if (node) {\n            const selectedTable = closestElement(node, '.o_selected_table');\n            if (selectedTable) {\n                for (const selectedTd of selectedTable.querySelectorAll('.o_selected_td')) {\n                    traversedNodes.add(selectedTd);\n                    descendants(selectedTd).forEach(descendant => traversedNodes.add(descendant));\n                }\n            } else if (\n                !(\n                    // Handle the case: [<p>ab</p><p>cd<br>]ef</p> => [ab, p2, cd, br]\n                    node === range.endContainer &&\n                    range.endOffset === 0 &&\n                    !range.collapsed &&\n                    node.previousSibling &&\n                    node.previousSibling.nodeName === \"BR\"\n                )\n            ) {\n                traversedNodes.add(node);\n            }\n        }\n    }\n    if (node) {\n        // Handle the cases:\n        // [<p>ab</p><p>cd<br>]</p> => [ab, p2, cd, br]\n        // [<p>ab</p><p>cd<br>]<br>ef</p> => [ab, p2, cd, br1]\n        for (const descendant of descendants(node)) {\n            if (\n                descendant.parentElement === node &&\n                childNodeIndex(descendant) >= range.endOffset\n            ) {\n                break;\n            }\n            traversedNodes.add(descendant);\n        }\n    }\n    return [...traversedNodes];\n}\n/**\n * Returns an array containing all the nodes fully contained in the selection.\n *\n * @param {Node} editable\n * @returns {Node[]}\n */\nexport function getSelectedNodes(editable) {\n    const selectedTableCells = editable.querySelectorAll('.o_selected_td');\n    const document = editable.ownerDocument;\n    const sel = document.getSelection();\n    if (!sel.rangeCount && !selectedTableCells.length) {\n        return [];\n    }\n    const range = sel.getRangeAt(0);\n    return [...new Set(getTraversedNodes(editable).flatMap(\n        node => {\n            const td = closestElement(node, '.o_selected_td');\n            if (td) {\n                return descendants(td);\n            } else if (range.isPointInRange(node, 0) && range.isPointInRange(node, nodeSize(node))) {\n                return node;\n            } else {\n                return [];\n            }\n        },\n    ))];\n}\n\n/**\n * Returns the current range (if any), adapted to target the deepest\n * descendants.\n *\n * @param {Node} editable\n * @param {object} [options]\n * @param {Selection} [options.range] the range to use.\n * @param {Selection} [options.sel] the selection to use.\n * @param {boolean} [options.splitText] split the targeted text nodes at offset.\n * @param {boolean} [options.select] select the new range if it changed (via splitText).\n * @param {boolean} [options.correctTripleClick] adapt the range if it was a triple click.\n * @returns {Range}\n */\nexport function getDeepRange(editable, { range, sel, splitText, select, correctTripleClick } = {}) {\n    sel = sel || editable.parentElement && editable.ownerDocument.getSelection();\n    if (sel && sel.isCollapsed && sel.anchorNode && sel.anchorNode.nodeName === \"BR\") {\n        setSelection(sel.anchorNode.parentElement, childNodeIndex(sel.anchorNode));\n    }\n    range = range ? range.cloneRange() : sel && sel.rangeCount && sel.getRangeAt(0).cloneRange();\n    if (!range) return;\n    let start = range.startContainer;\n    let startOffset = range.startOffset;\n    let end = range.endContainer;\n    let endOffset = range.endOffset;\n\n    const isBackwards =\n        !range.collapsed && start === sel.focusNode && startOffset === sel.focusOffset;\n\n    // Target the deepest descendant of the range nodes.\n    [start, startOffset] = getDeepestPosition(start, startOffset);\n    [end, endOffset] = getDeepestPosition(end, endOffset);\n\n    // Split text nodes if that was requested.\n    if (splitText) {\n        const isInSingleContainer = start === end;\n        if (\n            end.nodeType === Node.TEXT_NODE &&\n            endOffset !== 0 &&\n            endOffset !== end.textContent.length\n        ) {\n            const endParent = end.parentNode;\n            const splitOffset = splitTextNode(end, endOffset);\n            end = endParent.childNodes[splitOffset - 1] || endParent.firstChild;\n            if (isInSingleContainer) {\n                start = end;\n            }\n            endOffset = end.textContent.length;\n        }\n        if (\n            start.nodeType === Node.TEXT_NODE &&\n            startOffset !== 0 &&\n            startOffset !== start.textContent.length\n        ) {\n            splitTextNode(start, startOffset);\n            startOffset = 0;\n            if (isInSingleContainer) {\n                endOffset = start.textContent.length;\n            }\n        }\n    }\n    // A selection spanning multiple nodes and ending at position 0 of a node,\n    // like the one resulting from a triple click, is corrected so that it ends\n    // at the last position of the previous node instead.\n    const endLeaf = firstLeaf(end);\n    const beforeEnd = endLeaf.previousSibling;\n    const isInsideColumn = closestElement(end, '.o_text_columns')\n    if (\n        correctTripleClick &&\n        !endOffset &&\n        (start !== end || startOffset !== endOffset) &&\n        (!beforeEnd ||\n            (beforeEnd.nodeType === Node.TEXT_NODE &&\n                !isVisibleTextNode(beforeEnd) &&\n                !isZWS(beforeEnd))) &&\n        !closestElement(endLeaf, 'table') &&\n        !isInsideColumn\n    ) {\n        const previous = previousLeaf(endLeaf, editable, true);\n        if (previous && closestElement(previous).isContentEditable) {\n            [end, endOffset] = [previous, nodeSize(previous)];\n        }\n    }\n\n    if (select) {\n        if (isBackwards) {\n            [start, end, startOffset, endOffset] = [end, start, endOffset, startOffset];\n            range.setEnd(start, startOffset);\n            range.collapse(false);\n        } else {\n            range.setStart(start, startOffset);\n            range.collapse(true);\n        }\n        sel.removeAllRanges();\n        sel.addRange(range);\n        try {\n            sel.extend(end, endOffset);\n        } catch {\n            // Firefox yells not happy when setting selection on elem with contentEditable=false.\n        }\n        range = sel.getRangeAt(0);\n    } else {\n        range.setStart(start, startOffset);\n        range.setEnd(end, endOffset);\n    }\n    return range;\n}\n\nexport function getAdjacentCharacter(editable, side) {\n    let { focusNode, focusOffset } = editable.ownerDocument.getSelection();\n    const originalBlock = closestBlock(focusNode);\n    let adjacentCharacter;\n    while (!adjacentCharacter && focusNode) {\n        if (side === 'previous') {\n            adjacentCharacter = focusOffset > 0 && focusNode.textContent[focusOffset - 1];\n        } else {\n            adjacentCharacter = focusNode.textContent[focusOffset];\n        }\n        if (!adjacentCharacter) {\n            if (side === 'previous') {\n                focusNode = previousLeaf(focusNode, editable);\n                focusOffset = focusNode && nodeSize(focusNode);\n            } else {\n                focusNode = nextLeaf(focusNode, editable);\n                focusOffset = 0;\n            }\n            const characterIndex = side === 'previous' ? focusOffset - 1 : focusOffset;\n            adjacentCharacter = focusNode && focusNode.textContent[characterIndex];\n        }\n    }\n    return closestBlock(focusNode) === originalBlock ? adjacentCharacter : undefined;\n}\n\nfunction isZwnbsp(node) {\n    return node.nodeType === Node.TEXT_NODE && node.textContent === '\\ufeff';\n}\n\nfunction isTangible(node) {\n    return isVisible(node) || isZwnbsp(node) || hasTangibleContent(node);\n}\n\nfunction hasTangibleContent(node) {\n    return [...(node?.childNodes || [])].some(n => isTangible(n));\n}\n\nexport function getDeepestPosition(node, offset) {\n    let direction = DIRECTIONS.RIGHT;\n    let next = node;\n    while (next) {\n        if (isTangible(next) || isZWS(next)) {\n            // Valid node: update position then try to go deeper.\n            if (next !== node) {\n                [node, offset] = [next, direction ? 0 : nodeSize(next)];\n            }\n            // First switch direction to left if offset is at the end.\n            direction = offset < node.childNodes.length;\n            next = node.childNodes[direction ? offset : offset - 1];\n        } else if (\n            direction &&\n            next.nextSibling &&\n            closestBlock(node)?.contains(next.nextSibling)\n        ) {\n            // Invalid node: skip to next sibling (without crossing blocks).\n            next = next.nextSibling;\n        } else {\n            // Invalid node: skip to previous sibling (without crossing blocks).\n            direction = DIRECTIONS.LEFT;\n            next = closestBlock(node)?.contains(next.previousSibling) && next.previousSibling;\n        }\n        // Avoid too-deep ranges inside self-closing elements like [BR, 0].\n        next = !isSelfClosingElement(next) && next;\n    }\n    return [node, offset];\n}\n\nexport function getCursors(document) {\n    const sel = document.getSelection();\n    if (\n        getCursorDirection(sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset) ===\n        DIRECTIONS.LEFT\n    )\n        return [\n            [sel.focusNode, sel.focusOffset],\n            [sel.anchorNode, sel.anchorOffset],\n        ];\n    return [\n        [sel.anchorNode, sel.anchorOffset],\n        [sel.focusNode, sel.focusOffset],\n    ];\n}\n\nexport function preserveCursor(document) {\n    const sel = document.getSelection();\n    const cursorPos = [sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset];\n    return replace => {\n        replace = replace || new Map();\n        cursorPos[0] = replace.get(cursorPos[0]) || cursorPos[0];\n        cursorPos[2] = replace.get(cursorPos[2]) || cursorPos[2];\n        return setSelection(...cursorPos, false);\n    };\n}\n\n/**\n * Check if the selection starts inside given selector. This function can be\n * used as the `isDisabled` property of a command of the PowerBox to disable\n * a command in the given selectors.\n * @param {string}: comma separated string with all the desired selectors\n * @returns {boolean} true selector is within one of the selector\n * (if the command should be filtered)\n */\nexport function isSelectionInSelectors(selector) {\n    let anchor = document.getSelection().anchorNode;\n    if (anchor && anchor.nodeType && anchor.nodeType !== Node.ELEMENT_NODE) {\n        anchor = anchor.parentElement;\n    }\n    if (anchor && closestElement(anchor, selector)) {\n        return true;\n    }\n    return false;\n}\n\nexport function getOffsetAndCharSize(nodeValue, offset, direction) {\n    //We get the correct offset which corresponds to this offset\n    // If direction is left it means we are coming from the right and\n    // we want to get the end offset of the first element to the left\n    // Example with LEFT direction:\n    // <p>a \\uD83D[offset]\\uDE0D b</p> -> <p>a \\uD83D\\uDE0D[offset] b</p> and\n    // size = 2 so delete backward will delete the whole emoji.\n    // Example with Right direction:\n    // <p>a \\uD83D[offset]\\uDE0D b</p> -> <p>a [offset]\\uD83D\\uDE0D b</p> and\n    // size = 2 so delete forward will delete the whole emoji.\n    const splittedNodeValue = [...nodeValue];\n    let charSize = 1;\n    let newOffset = offset;\n    let currentSize = 0;\n    for (const item of splittedNodeValue) {\n        currentSize += item.length;\n        if (currentSize >= offset) {\n            newOffset = direction == DIRECTIONS.LEFT ? currentSize : currentSize - item.length;\n            charSize = item.length;\n            break;\n        }\n    }\n    return [newOffset, charSize];\n}\n\n//------------------------------------------------------------------------------\n// Format utils\n//------------------------------------------------------------------------------\n\nexport const formatsSpecs = {\n    italic: {\n        tagName: 'em',\n        isFormatted: isItalic,\n        isTag: (node) => ['EM', 'I'].includes(node.tagName),\n        hasStyle: (node) => Boolean(node.style && node.style['font-style']),\n        addStyle: (node) => node.style['font-style'] = 'italic',\n        addNeutralStyle: (node) => node.style['font-style'] = 'normal',\n        removeStyle: (node) => removeStyle(node, 'font-style'),\n    },\n    bold: {\n        tagName: 'strong',\n        isFormatted: isBold,\n        isTag: (node) => ['STRONG', 'B'].includes(node.tagName),\n        hasStyle: (node) => Boolean(node.style && node.style['font-weight']),\n        addStyle: (node) => node.style['font-weight'] = 'bolder',\n        addNeutralStyle: (node) => {\n            node.style['font-weight'] = 'normal'\n        },\n        removeStyle: (node) => removeStyle(node, 'font-weight'),\n    },\n    underline: {\n        tagName: 'u',\n        isFormatted: isUnderline,\n        isTag: (node) => node.tagName === 'U',\n        hasStyle: (node) => node.style && node.style['text-decoration-line'].includes('underline'),\n        addStyle: (node) => node.style['text-decoration-line'] += ' underline',\n        removeStyle: (node) => removeStyle(node, 'text-decoration-line', 'underline'),\n    },\n    strikeThrough: {\n        tagName: 's',\n        isFormatted: isStrikeThrough,\n        isTag: (node) => node.tagName === 'S',\n        hasStyle: (node) => node.style && node.style['text-decoration-line'].includes('line-through'),\n        addStyle: (node) => node.style['text-decoration-line'] += ' line-through',\n        removeStyle: (node) => removeStyle(node, 'text-decoration-line', 'line-through'),\n    },\n    fontSize: {\n        isFormatted: isFontSize,\n        hasStyle: (node) => node.style && node.style['font-size'],\n        addStyle: (node, props) => {\n            node.style['font-size'] = props.size;\n            node.classList.remove(...FONT_SIZE_CLASSES);\n        },\n        removeStyle: (node) => removeStyle(node, 'font-size'),\n    },\n    setFontSizeClassName: {\n        isFormatted: hasClass,\n        hasStyle: (node, props) => FONT_SIZE_CLASSES\n            .find(cls => node.classList.contains(cls)),\n        addStyle: (node, props) => node.classList.add(props.className),\n        removeStyle: (node) => {\n            node.classList.remove(...FONT_SIZE_CLASSES, ...TEXT_STYLE_CLASSES);\n            if (node.classList.length === 0) {\n                node.removeAttribute(\"class\");\n            }\n        },\n    },\n    switchDirection: {\n        isFormatted: isDirectionSwitched,\n    }\n}\n\nconst removeStyle = (node, styleName, item) => {\n    if (item) {\n        const newStyle = node.style[styleName].split(' ').filter(x => x !== item).join(' ');\n        node.style[styleName] = newStyle || null;\n    } else {\n        node.style[styleName] = null;\n    }\n    if (node.getAttribute('style') === '') {\n        node.removeAttribute('style');\n    }\n};\nconst getOrCreateSpan = (node, ancestors) => {\n    const span = ancestors.find((element) => element.tagName === 'SPAN' && element.isConnected);\n    if (span) {\n        return span;\n    } else {\n        const span = document.createElement('span');\n        node.after(span);\n        span.append(node);\n        return span;\n    }\n}\nconst removeFormat = (node, formatSpec) => {\n    node = closestElement(node);\n    if (formatSpec.hasStyle(node)) {\n        formatSpec.removeStyle(node);\n        if (['SPAN', 'FONT'].includes(node.tagName) && !node.getAttributeNames().length) {\n            return unwrapContents(node);\n        }\n    }\n\n    if (formatSpec.isTag && formatSpec.isTag(node)) {\n        const attributesNames = node.getAttributeNames().filter((name)=> {\n            return name !== 'data-oe-zws-empty-inline';\n        });\n        if (attributesNames.length) {\n            // Change tag name\n            const newNode = document.createElement('span');\n            while (node.firstChild) {\n                newNode.appendChild(node.firstChild);\n            }\n            for (let index = node.attributes.length - 1; index >= 0; --index) {\n                newNode.attributes.setNamedItem(node.attributes[index].cloneNode());\n            }\n            node.parentNode.replaceChild(newNode, node);\n        } else {\n            unwrapContents(node);\n        }\n    }\n}\n\nexport const formatSelection = (editor, formatName, {applyStyle, formatProps} = {}) => {\n    const selection = editor.document.getSelection();\n    let direction\n    let wasCollapsed;\n    if (editor.editable.querySelector('.o_selected_td')) {\n        direction = DIRECTIONS.RIGHT;\n    } else {\n        if (!selection.rangeCount) return;\n        wasCollapsed = selection.getRangeAt(0).collapsed;\n\n        direction = getCursorDirection(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset);\n    }\n    getDeepRange(editor.editable, { splitText: true, select: true, correctTripleClick: true });\n\n    if (typeof applyStyle === 'undefined') {\n        applyStyle = !isSelectionFormat(editor.editable, formatName);\n    }\n\n    let zws;\n    if (wasCollapsed) {\n        if (selection.anchorNode.nodeType === Node.TEXT_NODE && selection.anchorNode.textContent === '\\u200b') {\n            zws = selection.anchorNode;\n            selection.getRangeAt(0).selectNode(zws);\n        } else {\n            zws = insertAndSelectZws(selection);\n        }\n        getDeepRange(editor.editable, { splitText: true, select: true, correctTripleClick: true });\n    }\n\n    const selectedNodes = getSelectedNodes(editor.editable).filter(\n        (n) =>\n            ((n.nodeType === Node.TEXT_NODE && (isVisibleTextNode(n) || isZWS(n))) ||\n                n.nodeName === \"BR\") &&\n            closestElement(n).isContentEditable\n    );\n\n    const selectedFieldNodes = new Set(getSelectedNodes(editor.editable)\n            .map(n =>closestElement(n, \"*[t-field],*[t-out],*[t-esc]\"))\n            .filter(Boolean));\n\n    const formatSpec = formatsSpecs[formatName];\n    for (const node of selectedNodes) {\n        const inlineAncestors = [];\n        let currentNode = node;\n        let parentNode = node.parentElement;\n\n        // Remove the format on all inline ancestors until a block or an element\n        // with a class that is not related to font size (in case the formatting\n        // comes from the class).\n        while (\n            parentNode && !isBlock(parentNode) &&\n            !isUnbreakable(parentNode) && !isUnbreakable(currentNode) &&\n            (parentNode.classList.length === 0 ||\n                [...parentNode.classList].every(cls => FONT_SIZE_CLASSES.includes(cls)))\n        ) {\n            const isUselessZws = parentNode.tagName === 'SPAN' &&\n                parentNode.hasAttribute('data-oe-zws-empty-inline') &&\n                parentNode.getAttributeNames().length === 1;\n\n            if (isUselessZws) {\n                unwrapContents(parentNode);\n            } else {\n                const newLastAncestorInlineFormat = splitAroundUntil(currentNode, parentNode);\n                removeFormat(newLastAncestorInlineFormat, formatSpec);\n                if (newLastAncestorInlineFormat.isConnected) {\n                    inlineAncestors.push(newLastAncestorInlineFormat);\n                    currentNode = newLastAncestorInlineFormat;\n                }\n            }\n\n            parentNode = currentNode.parentElement;\n        }\n\n        const firstBlockOrClassHasFormat = formatSpec.isFormatted(parentNode, formatProps);\n        if (firstBlockOrClassHasFormat && !applyStyle) {\n            formatSpec.addNeutralStyle && formatSpec.addNeutralStyle(getOrCreateSpan(node, inlineAncestors));\n        } else if (!firstBlockOrClassHasFormat && applyStyle) {\n            const tag = formatSpec.tagName && document.createElement(formatSpec.tagName);\n            if (tag) {\n                node.after(tag);\n                tag.append(node);\n\n                if (!formatSpec.isFormatted(tag, formatProps)) {\n                    tag.after(node);\n                    tag.remove();\n                    formatSpec.addStyle(getOrCreateSpan(node, inlineAncestors), formatProps);\n                }\n            } else if (formatName !== 'fontSize' || formatProps.size !== undefined) {\n                formatSpec.addStyle(getOrCreateSpan(node, inlineAncestors), formatProps);\n            }\n        }\n    }\n\n    for (const selectedFieldNode of selectedFieldNodes) {\n        if (applyStyle) {\n            formatSpec.addStyle(selectedFieldNode, formatProps);\n        } else {\n            formatSpec.removeStyle(selectedFieldNode);\n        }\n    }\n\n    if (zws) {\n        const siblings = [...zws.parentElement.childNodes];\n        if (\n            !isBlock(zws.parentElement) &&\n            selectedNodes.includes(siblings[0]) &&\n            selectedNodes.includes(siblings[siblings.length - 1])\n        ) {\n            zws.parentElement.setAttribute('data-oe-zws-empty-inline', '');\n        } else {\n            const span = document.createElement('span');\n            span.setAttribute('data-oe-zws-empty-inline', '');\n            zws.before(span);\n            span.append(zws);\n        }\n    }\n    if (selectedNodes.length === 1 && selectedNodes[0].textContent === '\\u200B') {\n        setSelection(selectedNodes[0], 0);\n    } else if (selectedNodes.length) {\n        const firstNode = selectedNodes[0];\n        const lastNode = selectedNodes[selectedNodes.length - 1];\n        if (direction === DIRECTIONS.RIGHT) {\n            setSelection(firstNode, 0, lastNode, lastNode.length, false);\n        } else {\n            setSelection(lastNode, lastNode.length, firstNode, 0, false);\n        }\n    }\n}\nexport const isLinkEligibleForZwnbsp = (editable, link) => {\n    return link.isContentEditable && editable.contains(link) && !(\n        [link, ...link.querySelectorAll('*')].some(el => el.nodeName === 'IMG' || isBlock(el)) ||\n        link.matches('nav a, a.nav-link')\n    );\n}\n/**\n * Take a link and pad it with non-break zero-width spaces to ensure that it is\n * always possible to place the cursor at its inner and outer edges.\n *\n * @param {HTMLElement} editable\n * @param {HTMLAnchorElement} link\n */\nexport const padLinkWithZws = (editable, link) => {\n    if (!isLinkEligibleForZwnbsp(editable, link)) {\n        // Only add the ZWNBSP for simple (possibly styled) text links, and\n        // never in a nav.\n        return;\n    }\n    const selection = editable.ownerDocument.getSelection() || {};\n    const { anchorOffset, focusOffset } = selection;\n    let extraAnchorOffset = 0;\n    let extraFocusOffset = 0;\n    if (!link.textContent.startsWith('\\uFEFF')) {\n        if (selection.anchorNode === link && anchorOffset) {\n            extraAnchorOffset += 1;\n        }\n        if (selection.focusNode === link && focusOffset) {\n            extraFocusOffset += 1;\n        }\n        link.prepend(document.createTextNode('\\uFEFF'));\n    }\n    if (!link.textContent.endsWith('\\uFEFF')) {\n        if (selection.anchorNode === link && anchorOffset + extraAnchorOffset === nodeSize(link)) {\n            extraAnchorOffset += 1;\n        }\n        if (selection.focusNode === link && focusOffset + extraFocusOffset === nodeSize(link)) {\n            extraFocusOffset += 1;\n        }\n        link.append(document.createTextNode('\\uFEFF'));\n    }\n    const linkIndex = childNodeIndex(link);\n    if (!(link.previousSibling && link.previousSibling.textContent.endsWith('\\uFEFF'))) {\n        if (selection.anchorNode === link.parentElement && anchorOffset + extraAnchorOffset > linkIndex) {\n            extraAnchorOffset += 1;\n        }\n        if (selection.focusNode === link.parentElement && focusOffset + extraFocusOffset > linkIndex) {\n            extraFocusOffset += 1;\n        }\n        link.before(document.createTextNode('\\uFEFF'));\n    }\n    if (!(link.nextSibling && link.nextSibling.textContent.startsWith('\\uFEFF'))) {\n        if (selection.anchorNode === link.parentElement && anchorOffset + extraAnchorOffset > linkIndex + 1) {\n            extraAnchorOffset += 1;\n        }\n        if (selection.focusNode === link.parentElement && focusOffset + extraFocusOffset > linkIndex + 1) {\n            extraFocusOffset += 1;\n        }\n        link.after(document.createTextNode('\\uFEFF'));\n    }\n    if (extraAnchorOffset || extraFocusOffset) {\n        setSelection(\n            selection.anchorNode, anchorOffset + extraAnchorOffset,\n            selection.focusNode, focusOffset + extraFocusOffset,\n        );\n    }\n}\n\n//------------------------------------------------------------------------------\n// DOM Info utils\n//------------------------------------------------------------------------------\n\n/**\n * The following is a complete list of all HTML \"block-level\" elements.\n *\n * Source:\n * https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements\n *\n **/\nconst blockTagNames = [\n    'ADDRESS',\n    'ARTICLE',\n    'ASIDE',\n    'BLOCKQUOTE',\n    'DETAILS',\n    'DIALOG',\n    'DD',\n    'DIV',\n    'DL',\n    'DT',\n    'FIELDSET',\n    'FIGCAPTION',\n    'FIGURE',\n    'FOOTER',\n    'FORM',\n    'H1',\n    'H2',\n    'H3',\n    'H4',\n    'H5',\n    'H6',\n    'HEADER',\n    'HGROUP',\n    'HR',\n    'LI',\n    'MAIN',\n    'NAV',\n    'OL',\n    'P',\n    'PRE',\n    'SECTION',\n    'TABLE',\n    'UL',\n    // The following elements are not in the W3C list, for some reason.\n    'SELECT',\n    'OPTION',\n    'TR',\n    'TD',\n    'TBODY',\n    'THEAD',\n    'TH',\n];\nconst computedStyles = new WeakMap();\n/**\n * Return true if the given node is a block-level element, false otherwise.\n *\n * @param node\n */\nexport function isBlock(node) {\n    if (!node || node.nodeType !== Node.ELEMENT_NODE) {\n        return false;\n    }\n    const tagName = node.nodeName.toUpperCase();\n    // Every custom jw-* node will be considered as blocks.\n    if (\n        tagName.startsWith('JW-') ||\n        (tagName === 'T' &&\n            node.getAttribute('t-esc') === null &&\n            node.getAttribute('t-out') === null &&\n            node.getAttribute('t-raw') === null)\n    ) {\n        return true;\n    }\n    if (tagName === 'BR') {\n        // A <br> is always inline but getComputedStyle(br).display mistakenly\n        // returns 'block' if its parent is display:flex (at least on Chrome and\n        // Firefox (Linux)). Browsers normally support setting a <br>'s display\n        // property to 'none' but any other change is not supported. Therefore\n        // it is safe to simply declare that a <br> is never supposed to be a\n        // block.\n        return false;\n    }\n    // The node might not be in the DOM, in which case it has no CSS values.\n    if (!node.isConnected) {\n        return blockTagNames.includes(tagName);\n    }\n    // We won't call `getComputedStyle` more than once per node.\n    let style = computedStyles.get(node);\n    if (!style) {\n        style = node.ownerDocument.defaultView?.getComputedStyle(node);\n        computedStyles.set(node, style);\n    }\n    if (style?.display) {\n        return !style.display.includes('inline') && style.display !== 'contents';\n    }\n    return blockTagNames.includes(tagName);\n}\n\n/**\n * Return true if the given node appears bold. The node is considered to appear\n * bold if its font weight is bigger than 500 (eg.: Heading 1), or if its font\n * weight is bigger than that of its closest block.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isBold(node) {\n    const fontWeight = +getComputedStyle(closestElement(node)).fontWeight;\n    return fontWeight > 500 || fontWeight > +getComputedStyle(closestBlock(node)).fontWeight;\n}\n/**\n * Return true if the given node appears italic.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isItalic(node) {\n    return getComputedStyle(closestElement(node)).fontStyle === 'italic';\n}\n/**\n * Return true if the given node appears underlined.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isUnderline(node) {\n    let parent = closestElement(node);\n    while (parent) {\n        if (getComputedStyle(parent).textDecorationLine.includes('underline')) {\n            return true;\n        }\n        parent = parent.parentElement;\n    }\n    return false;\n}\n/**\n * Return true if the given node appears struck through.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isStrikeThrough(node) {\n    let parent = closestElement(node);\n    while (parent) {\n        if (getComputedStyle(parent).textDecorationLine.includes('line-through')) {\n            return true;\n        }\n        parent = parent.parentElement;\n    }\n    return false;\n}\n/**\n * Return true if the given node font-size is equal to `props.size`.\n *\n * @param {Object} props\n * @param {Node} props.node A node to compare the font-size against.\n * @param {String} props.size The font-size value of the node that will be\n *     checked against.\n * @returns {boolean}\n */\nexport function isFontSize(node, props) {\n    const element = closestElement(node);\n    return getComputedStyle(element)['font-size'] === props.size;\n}\n/**\n * Return true if the given node classlist contains `props.className`.\n *\n * @param {Object} props\n * @param {Node} node A node to compare the font-size against.\n * @param {String} props.className The name of the class.\n * @returns {boolean}\n */\nexport function hasClass(node, props) {\n    const element = closestElement(node);\n    return element.classList.contains(props.className);\n}\n/**\n * Return true if the given node appears in a different direction than that of\n * the editable ('ltr' or 'rtl').\n *\n * Note: The direction of the editable is set on its \"dir\" attribute, to the\n * value of the \"direction\" option on instantiation of the editor.\n *\n * @param {Node} node\n * @param {Element} editable\n * @returns {boolean}\n */\n export function isDirectionSwitched(node, editable) {\n    const defaultDirection = editable.getAttribute('dir');\n    return getComputedStyle(closestElement(node)).direction !== defaultDirection;\n}\n/**\n * Return true if the current selection on the editable appears as the given\n * format. The selection is considered to appear as that format if every text\n * node in it appears as that format.\n *\n * @param {Element} editable\n * @param {String} format 'bold'|'italic'|'underline'|'strikeThrough'|'switchDirection'\n * @returns {boolean}\n */\nexport function isSelectionFormat(editable, format) {\n    const selectedNodes = getTraversedNodes(editable)\n        .filter((n) => n.nodeType === Node.TEXT_NODE && n.nodeValue.replaceAll(ZWNBSP_CHAR, '').length);\n    const isFormatted = formatsSpecs[format].isFormatted;\n    return selectedNodes.length && selectedNodes.every(n => isFormatted(n, editable));\n}\n\nexport function isUnbreakable(node) {\n    if (!node || node.nodeType === Node.TEXT_NODE) {\n        return false;\n    }\n    if (node.nodeType !== Node.ELEMENT_NODE) {\n        return true;\n    }\n    return (\n        isUnremovable(node) || // An unremovable node is always unbreakable.\n        ['TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'SECTION', 'DIV'].includes(node.tagName) ||\n        node.hasAttribute('t') ||\n        (node.nodeType === Node.ELEMENT_NODE &&\n            (node.nodeName === 'T' ||\n                node.getAttribute('t-if') ||\n                node.getAttribute('t-esc') ||\n                node.getAttribute('t-elif') ||\n                node.getAttribute('t-else') ||\n                node.getAttribute('t-foreach') ||\n                node.getAttribute('t-value') ||\n                node.getAttribute('t-out') ||\n                node.getAttribute('t-raw')) ||\n                node.getAttribute('t-field')) ||\n        node.matches(\".oe_unbreakable, a.btn, a[role='tab'], a[role='button']\")\n    );\n}\n\nexport function isUnremovable(node) {\n    return (\n        (node.nodeType !== Node.COMMENT_NODE && node.nodeType !== Node.ELEMENT_NODE && node.nodeType !== Node.TEXT_NODE) ||\n        node.oid === 'root' ||\n        (node.nodeType === Node.ELEMENT_NODE &&\n            (node.classList.contains('o_editable') || node.getAttribute('t-set') || node.getAttribute('t-call'))) ||\n        (node.classList && node.classList.contains('oe_unremovable')) ||\n        (node.nodeName === 'SPAN' && node.parentElement && node.parentElement.getAttribute('data-oe-type') === 'monetary') ||\n        (node.ownerDocument && node.ownerDocument.defaultWindow && !ancestors(node).find(ancestor => ancestor.oid === 'root')) // Node is in DOM but not in editable.\n    );\n}\n\nexport function containsUnbreakable(node) {\n    if (!node) {\n        return false;\n    }\n    return isUnbreakable(node) || containsUnbreakable(node.firstChild);\n}\n\nconst iconTags = ['I', 'SPAN'];\nconst iconClasses = ['fa', 'fab', 'fad', 'far', 'oi'];\n/**\n * Indicates if the given node is an icon element.\n *\n * @see ICON_SELECTOR\n * @param {?Node} [node]\n * @returns {boolean}\n */\nexport function isIconElement(node) {\n    return !!(\n        node &&\n        iconTags.includes(node.nodeName) &&\n        iconClasses.some(cls => node.classList.contains(cls))\n    );\n}\nexport const ICON_SELECTOR = iconTags.map(tag => {\n    return iconClasses.map(cls => {\n        return `${tag}.${cls}`;\n    }).join(', ');\n}).join(', ');\n\n/**\n * Return true if the given node is a zero-width breaking space (200b), false\n * otherwise. Note that this will return false for a zero-width NON-BREAK space\n * (feff)!\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isZWS(node) {\n    return (\n        node &&\n        node.textContent === '\\u200B'\n    );\n}\nexport function isEditorTab(node) {\n    return (\n        node &&\n        (node.nodeName === 'SPAN') &&\n        node.classList.contains('oe-tabs')\n    );\n}\nexport function isMediaElement(node) {\n    return (\n        isIconElement(node) ||\n        (node.classList &&\n            (node.classList.contains('o_image') || node.classList.contains('media_iframe_video')))\n    );\n}\n/**\n * A \"protected\" node will have its mutations filtered and not be registered\n * in an history step. Some editor features like selection handling, command\n * hint, toolbar, tooltip, etc. are also disabled. Protected roots have their\n * data-oe-protected attribute set to either \"\" or \"true\". If the closest parent\n * with a data-oe-protected attribute has the value \"false\", it is not\n * protected. Unknown values are ignored.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isProtected(node) {\n    const closestProtectedElement = closestElement(node, '[data-oe-protected]');\n    if (closestProtectedElement) {\n        return [\"\", \"true\"].includes(closestProtectedElement.dataset.oeProtected);\n    }\n    return false;\n}\n\n// https://developer.mozilla.org/en-US/docs/Glossary/Void_element\nconst VOID_ELEMENT_NAMES = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG',\n    'INPUT', 'KEYGEN', 'LINK', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR'];\n\nexport function isArtificialVoidElement(node) {\n    return isMediaElement(node) || node.nodeName === 'HR';\n}\n\nexport function isNotAllowedContent(node) {\n    return isArtificialVoidElement(node) || VOID_ELEMENT_NAMES.includes(node.nodeName);\n}\n\nexport function containsUnremovable(node) {\n    if (!node) {\n        return false;\n    }\n    return isUnremovable(node) || containsUnremovable(node.firstChild);\n}\n\nexport function getInSelection(document, selector) {\n    const selection = document.getSelection();\n    const range = selection && !!selection.rangeCount && selection.getRangeAt(0);\n    if (range) {\n        const selectorInStartAncestors = closestElement(range.startContainer, selector);\n        if (selectorInStartAncestors) {\n            return selectorInStartAncestors;\n        } else {\n            const commonElementAncestor = closestElement(range.commonAncestorContainer);\n            return commonElementAncestor && [...commonElementAncestor.querySelectorAll(selector)].find(\n                node => range.intersectsNode(node),\n            );\n        }\n    }\n}\n\n/**\n * Get the index of the given table row/cell.\n *\n * @private\n * @param {HTMLTableRowElement|HTMLTableCellElement} trOrTd\n * @returns {number}\n */\nexport function getRowIndex(trOrTd) {\n    const tr = closestElement(trOrTd, 'tr');\n    const trParent = tr && tr.parentElement;\n    if (!trParent) {\n        return -1;\n    }\n    const trSiblings = [...trParent.children].filter(child => child.nodeName === 'TR');\n    return trSiblings.findIndex(child => child === tr);\n}\n\n/**\n * Get the index of the given table cell.\n *\n * @private\n * @param {HTMLTableCellElement} td\n * @returns {number}\n */\nexport function getColumnIndex(td) {\n    const tdParent = td.parentElement;\n    if (!tdParent) {\n        return -1;\n    }\n    const tdSiblings = [...tdParent.children].filter(child => child.nodeName === 'TD' || child.nodeName === 'TH');\n    return tdSiblings.findIndex(child => child === td);\n}\n\n// This is a list of \"paragraph-related elements\", defined as elements that\n// behave like paragraphs.\nexport const paragraphRelatedElements = [\n    'P',\n    'H1',\n    'H2',\n    'H3',\n    'H4',\n    'H5',\n    'H6',\n    'PRE',\n    'BLOCKQUOTE',\n];\n\n/**\n * Return true if the given node allows \"paragraph-related elements\".\n *\n * @see paragraphRelatedElements\n * @param {Node} node\n * @returns {boolean}\n */\nexport function allowsParagraphRelatedElements(node) {\n    return isBlock(node) && !['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(node.nodeName);\n}\n\n/**\n * Take a node and unwrap all of its block contents recursively. All blocks\n * (except for firstChilds) are preceded by a <br> in order to preserve the line\n * breaks.\n *\n * @param {Node} node\n */\nexport function makeContentsInline(node) {\n    let childIndex = 0;\n    for (const child of node.childNodes) {\n        if (isBlock(child)) {\n            if (childIndex && paragraphRelatedElements.includes(child.nodeName)) {\n                child.before(document.createElement('br'));\n            }\n            for (const grandChild of child.childNodes) {\n                child.before(grandChild);\n                makeContentsInline(grandChild);\n            }\n            child.remove();\n        }\n        childIndex += 1;\n    }\n}\n\n// optimize: use the parent Oid to speed up detection\nexport function getOuid(node, optimize = false) {\n    while (node && !isUnbreakable(node)) {\n        if (node.ouid && optimize) return node.ouid;\n        node = node.parentNode;\n    }\n    return node && node.oid;\n}\n/**\n * Returns true if the provided node can suport html content.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isHtmlContentSupported(node) {\n    return !closestElement(node, '[data-oe-model]:not([data-oe-field=\"arch\"]):not([data-oe-type=\"html\"]),[data-oe-translation-id]', true);\n}\n/**\n * Returns whether the given node is a element that could be considered to be\n * removed by itself = self closing tags.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nconst selfClosingElementTags = ['BR', 'IMG', 'INPUT'];\nexport function isSelfClosingElement(node) {\n    return node && selfClosingElementTags.includes(node.nodeName);\n}\n/**\n * Returns true if the given node is in a PRE context for whitespace handling.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isInPre(node) {\n    const element = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;\n    return (\n        !!element &&\n        (!!element.closest('pre') ||\n            getComputedStyle(element).getPropertyValue('white-space') === 'pre')\n    );\n}\nconst whitespace = `[^\\\\S\\\\u00A0\\\\u0009\\\\ufeff]`; // for formatting (no \"real\" content) (TODO: 0009 shouldn't be included)\nconst whitespaceRegex = new RegExp(`^${whitespace}*$`);\nexport function isWhitespace(value) {\n    const str = typeof value === 'string' ? value : value.nodeValue;\n    return whitespaceRegex.test(str);\n}\n/**\n * Returns whether removing the given node from the DOM will have a visible\n * effect or not.\n *\n * Note: TODO this is not handling all cases right now, just the ones the\n * caller needs at the moment. For example a space text node between two inlines\n * will always return 'true' while it is sometimes invisible.\n *\n * @param {Node} node\n * @returns {boolean}\n */\nexport function isVisible(node) {\n    return !!node && (\n        (node.nodeType === Node.TEXT_NODE && isVisibleTextNode(node)) ||\n        (node.nodeType === Node.ELEMENT_NODE &&\n            (node.getAttribute(\"t-esc\") || node.getAttribute(\"t-out\"))) ||\n        isSelfClosingElement(node) ||\n        isIconElement(node) ||\n        hasVisibleContent(node)\n    );\n}\nexport function hasVisibleContent(node) {\n    return [...(node?.childNodes || [])].some(n => isVisible(n));\n}\nconst visibleCharRegex = /[^\\s\\u200b]|[\\u00A0\\u0009]$/; // contains at least a char that is always visible (TODO: 0009 shouldn't be included)\nexport function isVisibleTextNode(testedNode) {\n    if (!testedNode || !testedNode.length || testedNode.nodeType !== Node.TEXT_NODE) {\n        return false;\n    }\n    if (visibleCharRegex.test(testedNode.textContent) || (isInPre(testedNode) && isWhitespace(testedNode))) {\n        return true;\n    }\n    if (ZERO_WIDTH_CHARS.includes(testedNode.textContent)) {\n        return false; // a ZW(NB)SP is always invisible, regardless of context.\n    }\n    // The following assumes node is made entirely of whitespace and is not\n    // preceded of followed by a block.\n    // Find out contiguous preceding and following text nodes\n    let preceding;\n    let following;\n    // Control variable to know whether the current node has been found\n    let foundTestedNode;\n    const currentNodeParentBlock = closestBlock(testedNode);\n    if (!currentNodeParentBlock) {\n        return false;\n    }\n    const nodeIterator = document.createNodeIterator(currentNodeParentBlock);\n    for (let node = nodeIterator.nextNode(); node; node = nodeIterator.nextNode()) {\n        if (node.nodeType === Node.TEXT_NODE) {\n            // If we already found the tested node, the current node is the\n            // contiguous following, and we can stop looping\n            // If the current node is the tested node, mark it as found and\n            // continue.\n            // If we haven't reached the tested node, overwrite the preceding\n            // node.\n            if (foundTestedNode) {\n                following = node;\n                break;\n            } else if (testedNode === node) {\n                foundTestedNode = true;\n            } else {\n                preceding = node;\n            }\n        } else if (isBlock(node)) {\n            // If we found the tested node, then the following node is irrelevant\n            // If we didn't, then the current preceding node is irrelevant\n            if (foundTestedNode) {\n                break;\n            } else {\n                preceding = null;\n            }\n        } else if (foundTestedNode && !isWhitespace(node)) {\n            // <block>space<inline>text</inline></block> -> space is visible\n            following = node;\n            break;\n        }\n    }\n    while (following && !visibleCharRegex.test(following.textContent)) {\n        following = following.nextSibling;\n    }\n    // Missing preceding or following: invisible.\n    // Preceding or following not in the same block as tested node: invisible.\n    if (\n        !(preceding && following) ||\n        currentNodeParentBlock !== closestBlock(preceding) ||\n        currentNodeParentBlock !== closestBlock(following)\n    ) {\n        return false;\n    }\n    // Preceding is whitespace or following is whitespace: invisible\n    return visibleCharRegex.test(preceding.textContent);\n}\n\nexport function parentsGet(node, root = undefined) {\n    const parents = [];\n    while (node) {\n        parents.unshift(node);\n        if (node === root) {\n            break;\n        }\n        node = node.parentNode;\n    }\n    return parents;\n}\n\nexport function commonParentGet(node1, node2, root = undefined) {\n    if (!node1 || !node2) {\n        return null;\n    }\n    const n1p = parentsGet(node1, root);\n    const n2p = parentsGet(node2, root);\n    while (n1p.length > 1 && n1p[1] === n2p[1]) {\n        n1p.shift();\n        n2p.shift();\n    }\n    // Check  in case at least one of them is not in the DOM.\n    return n1p[0] === n2p[0] ? n1p[0] : null;\n}\n\nexport function getListMode(pnode) {\n    if (![\"UL\", \"OL\"].includes(pnode.tagName)) return;\n    if (pnode.tagName == 'OL') return 'OL';\n    return pnode.classList.contains('o_checklist') ? 'CL' : 'UL';\n}\n\nexport function createList(mode) {\n    const node = document.createElement(mode == 'OL' ? 'OL' : 'UL');\n    if (mode == 'CL') {\n        node.classList.add('o_checklist');\n    }\n    return node;\n}\n\nexport function insertListAfter(afterNode, mode, content = []) {\n    const list = createList(mode);\n    afterNode.after(list);\n    list.append(\n        ...content.map(c => {\n            const li = document.createElement('LI');\n            li.append(...[].concat(c));\n            return li;\n        }),\n    );\n    return list;\n}\n\nexport function toggleList(node, mode, offset = 0) {\n    let pnode = node.closest('ul, ol');\n    if (!pnode) return;\n    const listMode = getListMode(pnode) + mode;\n    if (['OLCL', 'ULCL'].includes(listMode)) {\n        pnode.classList.add('o_checklist');\n        for (let li = pnode.firstElementChild; li !== null; li = li.nextElementSibling) {\n            if (li.style.listStyle !== 'none') {\n                li.style.listStyle = null;\n                if (!li.style.all) li.removeAttribute('style');\n            }\n        }\n        pnode = setTagName(pnode, 'UL');\n    } else if (['CLOL', 'CLUL'].includes(listMode)) {\n        toggleClass(pnode, 'o_checklist');\n        pnode = setTagName(pnode, mode);\n    } else if (['OLUL', 'ULOL'].includes(listMode)) {\n        pnode = setTagName(pnode, mode);\n    } else {\n        // toggle => remove list\n        let currNode = node;\n        while (currNode) {\n            currNode = currNode.oShiftTab(offset);\n        }\n        return;\n    }\n    return pnode;\n}\n\n/**\n * Converts a list element and its nested elements to the specified list mode.\n *\n * @param {HTMLUListElement|HTMLOListElement|HTMLLIElement} node - HTML element\n * representing a list or list item.\n * @param {string} toMode - Target list mode\n * @returns {HTMLUListElement|HTMLOListElement|HTMLLIElement} node - Modified\n * list element after conversion.\n */\nexport function convertList(node, toMode) {\n    if (![\"UL\", \"OL\", \"LI\"].includes(node.nodeName)) return;\n    const listMode = getListMode(node);\n    if (listMode && toMode !== listMode) {\n        node = toggleList(node, toMode);\n    }\n    for (const child of node.childNodes) {\n        convertList(child, toMode);\n    }\n\n    return node;\n}\n\nexport function toggleClass(node, className) {\n    node.classList.toggle(className);\n    if (!node.className) {\n        node.removeAttribute('class');\n    }\n}\n\nexport function makeZeroWidthCharactersVisible(text) {\n    return text.replaceAll('\\u200B', '//ZWSP//').replaceAll('\\uFEFF', '//ZWNBSP//');\n}\n\n/**\n * Returns whether or not the given node is a BR element which does not really\n * act as a line break, but as a placeholder for the cursor or to make some left\n * element (like a space) visible.\n *\n * @param {HTMLBRElement} brEl\n * @returns {boolean}\n */\nexport function isFakeLineBreak(brEl) {\n    return !(getState(...rightPos(brEl), DIRECTIONS.RIGHT).cType & (CTYPES.CONTENT | CTGROUPS.BR));\n}\n/**\n * Checks whether or not the given block has any visible content, except for\n * a placeholder BR.\n *\n * @param {HTMLElement} blockEl\n * @returns {boolean}\n */\nexport function isEmptyBlock(blockEl) {\n    if (!blockEl || blockEl.nodeType !== Node.ELEMENT_NODE) {\n        return false;\n    }\n    if (isIconElement(blockEl) || visibleCharRegex.test(blockEl.textContent)) {\n        return false;\n    }\n    if (blockEl.querySelectorAll('br').length >= 2) {\n        return false;\n    }\n    const nodes = blockEl.querySelectorAll('*');\n    for (const node of nodes) {\n        // There is no text and no double BR, the only thing that could make\n        // this visible is a \"visible empty\" node like an image.\n        if (node.nodeName != 'BR' && (isSelfClosingElement(node) || isIconElement(node))) {\n            return false;\n        }\n    }\n    return true;\n}\n/**\n * Checks whether or not the given block element has something to make it have\n * a visible height (except for padding / border).\n *\n * @param {HTMLElement} blockEl\n * @returns {boolean}\n */\nexport function isShrunkBlock(blockEl) {\n    return (\n        isEmptyBlock(blockEl) &&\n        !blockEl.querySelector('br') &&\n        blockEl.nodeName !== \"IMG\"\n    );\n}\n\n/**\n * @param {string} [value]\n * @returns {boolean}\n */\nexport function isColorGradient(value) {\n    // FIXME duplicated in @web_editor/utils.js\n    return value && value.includes('-gradient(');\n}\n\n/**\n * Finds the font size to display for the current selection. We cannot rely\n * on the computed font-size only as font-sizes are responsive and we always\n * want to display the desktop (integer when possible) one.\n *\n * @private\n * @todo probably move `getCSSVariableValue` and `convertNumericToUnit` as\n *       odoo-editor utils.\n * @param {Selection} sel The current selection.\n * @returns {Float} The font size to display.\n */\nexport function getFontSizeDisplayValue(sel, getCSSVariableValue, convertNumericToUnit) {\n    const tagNameRelatedToFontSize = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"];\n    const styleClassesRelatedToFontSize = [\"display-1\", \"display-2\", \"display-3\", \"display-4\"];\n    const closestStartContainerEl = closestElement(sel.getRangeAt(0).startContainer);\n    const closestFontSizedEl = closestStartContainerEl.closest(`\n        [style*='font-size'],\n        ${FONT_SIZE_CLASSES.map(className => `.${className}`)},\n        ${styleClassesRelatedToFontSize.map(className => `.${className}`)},\n        ${tagNameRelatedToFontSize}\n    `);\n    let remValue;\n    if (closestFontSizedEl) {\n        const useFontSizeInput = closestFontSizedEl.style.fontSize;\n        if (useFontSizeInput) {\n            // Use the computed value to always convert to px. However, this\n            // currently does not check that the inline font-size is the one\n            // actually having an effect (there could be an !important CSS rule\n            // forcing something else).\n            // TODO align with the behavior of the rest of the editor snippet\n            // options.\n            return parseFloat(getComputedStyle(closestStartContainerEl).fontSize);\n        }\n        // It's a class font size or a hN tag. We don't return the computed\n        // font size because it can be different from the one displayed in\n        // the toolbar because it's responsive.\n        const fontSizeClass = FONT_SIZE_CLASSES.find(\n            className => closestFontSizedEl.classList.contains(className));\n        let fsName;\n        if (fontSizeClass) {\n            fsName = fontSizeClass.substring(0, fontSizeClass.length - 3); // Without -fs\n        } else {\n            fsName = styleClassesRelatedToFontSize.find(\n                    className => closestFontSizedEl.classList.contains(className))\n                || closestFontSizedEl.tagName.toLowerCase();\n        }\n        remValue = parseFloat(getCSSVariableValue(`${fsName}-font-size`));\n    }\n    // It's default font size (no font size class / style).\n    if (remValue === undefined) {\n        remValue = parseFloat(getCSSVariableValue(\"font-size-base\"));\n    }\n    const pxValue = convertNumericToUnit(remValue, \"rem\", \"px\");\n    return pxValue || parseFloat(getComputedStyle(closestStartContainerEl).fontSize);\n}\n\n//------------------------------------------------------------------------------\n// DOM Modification\n//------------------------------------------------------------------------------\n\n/**\n * Splits a text node in two parts.\n * If the split occurs at the beginning or the end, the text node stays\n * untouched and unsplit. If a split actually occurs, the original text node\n * still exists and become the right part of the split.\n *\n * Note: if split after or before whitespace, that whitespace may become\n * invisible, it is up to the caller to replace it by nbsp if needed.\n *\n * @param {Node} textNode\n * @param {number} offset\n * @param {DIRECTIONS} originalNodeSide Whether the original node ends up on left\n * or right after the split\n * @returns {number} The parentOffset if the cursor was between the two text\n *          node parts after the split.\n */\nexport function splitTextNode(textNode, offset, originalNodeSide = DIRECTIONS.RIGHT) {\n    let parentOffset = childNodeIndex(textNode);\n\n    if (offset > 0) {\n        parentOffset++;\n\n        if (offset < textNode.length) {\n            const left = textNode.nodeValue.substring(0, offset);\n            const right = textNode.nodeValue.substring(offset);\n            if (originalNodeSide === DIRECTIONS.LEFT) {\n                const newTextNode = document.createTextNode(right);\n                textNode.after(newTextNode);\n                textNode.nodeValue = left;\n            } else {\n                const newTextNode = document.createTextNode(left);\n                textNode.before(newTextNode);\n                textNode.nodeValue = right;\n            }\n        }\n    }\n    return parentOffset;\n}\n\n/**\n * Split the given element at the given offset. The element will be removed in\n * the process so caution is advised in dealing with its reference. Returns a\n * tuple containing the new elements on both sides of the split.\n *\n * @param {Element} element\n * @param {number} offset\n * @returns {[Element, Element]}\n */\nexport function splitElement(element, offset) {\n    const before = element.cloneNode();\n    const after = element.cloneNode();\n    let index = 0;\n    for (const child of [...element.childNodes]) {\n        index < offset ? before.appendChild(child) : after.appendChild(child);\n        index++;\n    }\n    // e.g.: <p>Test/banner</p> + ENTER <=> <p>Test</p><div class=\"o_editor_banner>...</div><p><br></p>\n    const blockEl = closestBlock(after);\n    if (blockEl) {\n        fillEmpty(blockEl);\n    }\n    element.before(before);\n    element.after(after);\n    element.remove();\n    return [before, after];\n}\n\n/**\n * Split around the given elements, until a given ancestor (included). Elements\n * will be removed in the process so caution is advised in dealing with their\n * references. Returns the new split root element that is a clone of\n * limitAncestor or the original limitAncestor if no split occured.\n *\n * @see splitElement\n * @param {Node[] | Node} elements\n * @param {Node} limitAncestor\n * @returns {[Node, Node]}\n */\nexport function splitAroundUntil(elements, limitAncestor) {\n    elements = Array.isArray(elements) ? elements : [elements];\n    const firstNode = elements[0];\n    const lastNode = elements[elements.length - 1];\n    if ([firstNode, lastNode].includes(limitAncestor)) {\n        return limitAncestor;\n    }\n    let before = firstNode.previousSibling;\n    let after = lastNode.nextSibling;\n    let beforeSplit, afterSplit;\n    if (!before && !after && elements[0] !== limitAncestor) {\n        return splitAroundUntil(elements[0].parentElement, limitAncestor);\n    }\n    // Split up ancestors up to font\n    while (after && after.parentElement !== limitAncestor) {\n        afterSplit = splitElement(after.parentElement, childNodeIndex(after))[0];\n        after = afterSplit.nextSibling;\n    }\n    if (after) {\n        afterSplit = splitElement(limitAncestor, childNodeIndex(after))[0];\n        limitAncestor = afterSplit;\n    }\n    while (before && before.parentElement !== limitAncestor) {\n        beforeSplit = splitElement(before.parentElement, childNodeIndex(before) + 1)[1];\n        before = beforeSplit.previousSibling;\n    }\n    if (before) {\n        beforeSplit = splitElement(limitAncestor, childNodeIndex(before) + 1)[1];\n    }\n    return beforeSplit || afterSplit || limitAncestor;\n}\n\nexport function insertText(sel, content) {\n    if (!content) {\n        return;\n    }\n    if (sel.anchorNode.nodeType === Node.TEXT_NODE) {\n        const pos = [sel.anchorNode.parentElement, splitTextNode(sel.anchorNode, sel.anchorOffset)];\n        setSelection(...pos, ...pos, false);\n    }\n    const txt = document.createTextNode(content || '#');\n    const restore = prepareUpdate(sel.anchorNode, sel.anchorOffset);\n    sel.getRangeAt(0).insertNode(txt);\n    restore();\n    setSelection(...boundariesOut(txt), false);\n    return txt;\n}\n\n/**\n * Inserts the given characters at the given offset of the given node.\n *\n * @param {string} chars\n * @param {Node} node\n * @param {number} offset\n */\nexport function insertCharsAt(chars, node, offset) {\n    if (node.nodeType === Node.TEXT_NODE) {\n        const startValue = node.nodeValue;\n        if (offset < 0 || offset > startValue.length) {\n            throw new Error(`Invalid ${chars} insertion in text node`);\n        }\n        node.nodeValue = startValue.slice(0, offset) + chars + startValue.slice(offset);\n    } else {\n        if (offset < 0 || offset > node.childNodes.length) {\n            throw new Error(`Invalid ${chars} insertion in non-text node`);\n        }\n        const textNode = document.createTextNode(chars);\n        if (offset < node.childNodes.length) {\n            node.insertBefore(textNode, node.childNodes[offset]);\n        } else {\n            node.appendChild(textNode);\n        }\n    }\n}\n\n/**\n * Remove node from the DOM while preserving their contents if any.\n *\n * @param {Node} node\n * @returns {Node[]}\n */\nexport function unwrapContents(node) {\n    const contents = [...node.childNodes];\n    for (const child of contents) {\n        node.parentNode.insertBefore(child, node);\n    }\n    node.parentNode.removeChild(node);\n    return contents;\n}\n\n/**\n * Add a BR in the given node if its closest ancestor block has nothing to make\n * it visible, and/or add a zero-width space in the given node if it's an empty\n * inline unremovable so the cursor can stay in it.\n *\n * @param {HTMLElement} el\n * @returns {Object} { br: the inserted <br> if any,\n *                     zws: the inserted zero-width space if any }\n */\nexport function fillEmpty(el) {\n    const fillers = {};\n    const blockEl = closestBlock(el);\n    if (isShrunkBlock(blockEl)) {\n        const br = document.createElement('br');\n        blockEl.appendChild(br);\n        fillers.br = br;\n    }\n    if (!isTangible(el) && !el.hasAttribute(\"data-oe-zws-empty-inline\") && !el.hasChildNodes()) {\n        // As soon as there is actual content in the node, the zero-width space\n        // is removed by the sanitize function.\n        const zws = document.createTextNode('\\u200B');\n        el.appendChild(zws);\n        el.setAttribute(\"data-oe-zws-empty-inline\", \"\");\n        fillers.zws = zws;\n        const previousSibling = el.previousSibling;\n        if (previousSibling && previousSibling.nodeName === \"BR\") {\n            previousSibling.remove();\n        }\n        setSelection(zws, 0, zws, 0);\n    }\n    // If the element is empty and inside an <a> tag with 'inline' display,\n    // it's not possible to place the cursor in element even if it contains\n    // ZWSP. To make the element cursor-friendly, change its display to\n    // 'inline-block'.\n    if (\n        !isVisible(el) &&\n        el.nodeName !== 'A' &&\n        closestElement(el, 'a') &&\n        getComputedStyle(el).display === 'inline'\n    ) {\n        el.style.display = 'inline-block';\n    }\n    return fillers;\n}\n/**\n * Takes a selection (assumed to be collapsed) and insert a zero-width space at\n * its anchor point. Then, select that zero-width space.\n *\n * @param {Selection} selection\n * @returns {Node} the inserted zero-width space\n */\nexport function insertAndSelectZws(selection) {\n    const offset = selection.anchorOffset;\n    const zws = insertText(selection, '\\u200B');\n    splitTextNode(zws, offset);\n    selection.getRangeAt(0).selectNode(zws);\n    return zws;\n}\n\nexport function setTagName(el, newTagName) {\n    if (el.tagName === newTagName) {\n        return el;\n    }\n    const n = document.createElement(newTagName);\n    if (el.nodeName !== 'LI') {\n        el.style.removeProperty('list-style');\n        const attributes = el.attributes;\n        for (const attr of attributes) {\n            n.setAttribute(attr.name, attr.value);\n        }\n    }\n    while (el.firstChild) {\n        n.append(el.firstChild);\n    }\n    if (el.tagName === 'LI') {\n        el.append(n);\n    } else {\n        el.parentNode.replaceChild(n, el);\n    }\n    return n;\n}\n/**\n * Moves the given subset of nodes of a source element to the given destination.\n * If the source element is left empty it is removed. This ensures the moved\n * content and its destination surroundings are restored (@see restoreState) to\n * the way there were.\n *\n * It also reposition at the right position on the left of the moved nodes.\n *\n * @param {HTMLElement} destinationEl\n * @param {number} destinationOffset\n * @param {HTMLElement} sourceEl\n * @param {number} [startIndex=0]\n * @param {number} [endIndex=sourceEl.childNodes.length]\n * @returns {Array.<HTMLElement, number} The position at the left of the moved\n *     nodes after the move was done (and where the cursor was returned).\n */\nexport function moveNodes(\n    destinationEl,\n    destinationOffset,\n    sourceEl,\n    startIndex = 0,\n    endIndex = sourceEl.childNodes.length,\n) {\n    if (selfClosingElementTags.includes(destinationEl.nodeName)) {\n        throw new Error(`moveNodes: Invalid destination element ${destinationEl.nodeName}`);\n    }\n\n    const nodes = [];\n    for (let i = startIndex; i < endIndex; i++) {\n        nodes.push(sourceEl.childNodes[i]);\n    }\n\n    if (nodes.length) {\n        const restoreDestination = prepareUpdate(destinationEl, destinationOffset);\n        const restoreMoved = prepareUpdate(\n            ...leftPos(sourceEl.childNodes[startIndex]),\n            ...rightPos(sourceEl.childNodes[endIndex - 1]),\n        );\n        const fragment = document.createDocumentFragment();\n        nodes.forEach(node => fragment.appendChild(node));\n        const posRightNode = destinationEl.childNodes[destinationOffset];\n        if (posRightNode) {\n            destinationEl.insertBefore(fragment, posRightNode);\n        } else {\n            destinationEl.appendChild(fragment);\n        }\n        restoreDestination();\n        restoreMoved();\n    }\n\n    if (!nodeSize(sourceEl)) {\n        const restoreOrigin = prepareUpdate(...boundariesOut(sourceEl));\n        sourceEl.remove();\n        restoreOrigin();\n    }\n\n    // Return cursor position, but don't change it\n    const firstNode = nodes.find(node => !!node.parentNode);\n    return firstNode ? leftPos(firstNode) : [destinationEl, destinationOffset];\n}\n/**\n * Remove ouid of a node and it's descendants in order to allow that tree\n * to be moved into another parent.\n */\nexport function resetOuids(node) {\n    node.ouid = undefined;\n    for (const descendant of descendants(node)) {\n        descendant.ouid = undefined;\n    }\n}\n\n//------------------------------------------------------------------------------\n// Prepare / Save / Restore state utilities\n//------------------------------------------------------------------------------\n\nconst prepareUpdateLockedEditables = new Set();\n/**\n * Any editor command is applied to a selection (collapsed or not). After the\n * command, the content type on the selection boundaries, in both direction,\n * should be preserved (some whitespace should disappear as went from collapsed\n * to non collapsed, or converted to &nbsp; as went from non collapsed to\n * collapsed, there also <br> to remove/duplicate, etc).\n *\n * This function returns a callback which allows to do that after the command\n * has been done.\n *\n * Note: the method has been made generic enough to work with non-collapsed\n * selection but can be used for an unique cursor position.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {...(HTMLElement|number)} args - argument 1 and 2 can be repeated for\n *     multiple preparations with only one restore callback returned. Note: in\n *     that case, the positions should be given in the document node order.\n * @param {Object} [options]\n * @param {boolean} [options.allowReenter = true] - if false, all calls to\n *     prepareUpdate before this one gets restored will be ignored.\n * @param {string} [options.label = <random 6 character string>]\n * @param {boolean} [options.debug = false] - if true, adds nicely formatted\n *     console logs to help with debugging.\n * @returns {function}\n */\nexport function prepareUpdate(...args) {\n    const closestRoot = args.length && ancestors(args[0]).find(ancestor => ancestor.oid === 'root');\n    const isPrepareUpdateLocked = closestRoot && prepareUpdateLockedEditables.has(closestRoot);\n    const hash = (Math.random() + 1).toString(36).substring(7);\n    const options = {\n        allowReenter: true,\n        label: hash,\n        debug: false,\n        ...(args.length && args[args.length - 1] instanceof Object ? args.pop() : {}),\n    };\n    if (options.debug) {\n        console.log(\n            '%cPreparing%c update: ' + options.label +\n            (options.label === hash ? '' : ` (${hash})`) +\n            '%c' + (isPrepareUpdateLocked ? ' LOCKED' : ''),\n            'color: cyan;',\n            'color: white;',\n            'color: red; font-weight: bold;',\n        );\n    }\n    if (isPrepareUpdateLocked) {\n        return () => {\n            if (options.debug) {\n                console.log(\n                    '%cRestoring%c update: ' + options.label +\n                    (options.label === hash ? '' : ` (${hash})`) +\n                    '%c LOCKED',\n                    'color: lightgreen;',\n                    'color: white;',\n                    'color: red; font-weight: bold;',\n                );\n            }\n        };\n    }\n    if (!options.allowReenter && closestRoot) {\n        prepareUpdateLockedEditables.add(closestRoot);\n    }\n    const positions = [...args];\n\n    // Check the state in each direction starting from each position.\n    const restoreData = [];\n    let el, offset;\n    while (positions.length) {\n        // Note: important to get the positions in reverse order to restore\n        // right side before left side.\n        offset = positions.pop();\n        el = positions.pop();\n        const left = getState(el, offset, DIRECTIONS.LEFT);\n        const right = getState(el, offset, DIRECTIONS.RIGHT, left.cType);\n        if (options.debug) {\n            const editable = el && closestElement(el, '.odoo-editor-editable');\n            const oldEditableHTML = editable && makeZeroWidthCharactersVisible(editable.innerHTML).replaceAll(' ', '_') || '';\n            left.oldEditableHTML = oldEditableHTML;\n            right.oldEditableHTML = oldEditableHTML;\n        }\n        restoreData.push(left, right);\n    }\n\n    // Create the callback that will be able to restore the state in each\n    // direction wherever the node in the opposite direction has landed.\n    return function restoreStates() {\n        if (options.debug) {\n            console.log(\n                '%cRestoring%c update: ' + options.label +\n                (options.label === hash ? '' : ` (${hash})`),\n                'color: lightgreen;',\n                'color: white;',\n            );\n        }\n        for (const data of restoreData) {\n            restoreState(data, options.debug);\n        }\n        if (!options.allowReenter && closestRoot) {\n            prepareUpdateLockedEditables.delete(closestRoot);\n        }\n    };\n}\n/**\n * Retrieves the \"state\" from a given position looking at the given direction.\n * The \"state\" is the type of content. The functions also returns the first\n * meaninful node looking in the opposite direction = the first node we trust\n * will not disappear if a command is played in the given direction.\n *\n * Note: only work for in-between nodes positions. If the position is inside a\n * text node, first split it @see splitTextNode.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {DIRECTIONS} direction @see DIRECTIONS.LEFT @see DIRECTIONS.RIGHT\n * @param {CTYPES} [leftCType]\n * @returns {Object}\n */\nexport function getState(el, offset, direction, leftCType) {\n    const leftDOMPath = leftLeafOnlyNotBlockPath;\n    const rightDOMPath = rightLeafOnlyNotBlockPath;\n\n    let domPath;\n    let inverseDOMPath;\n    const whitespaceAtStartRegex = new RegExp('^' + whitespace + '+');\n    const whitespaceAtEndRegex = new RegExp(whitespace + '+$');\n    const reasons = [];\n    if (direction === DIRECTIONS.LEFT) {\n        domPath = leftDOMPath(el, offset, reasons);\n        inverseDOMPath = rightDOMPath(el, offset);\n    } else {\n        domPath = rightDOMPath(el, offset, reasons);\n        inverseDOMPath = leftDOMPath(el, offset);\n    }\n\n    // TODO I think sometimes, the node we have to consider as the\n    // anchor point to restore the state is not the first one of the inverse\n    // path (like for example, empty text nodes that may disappear\n    // after the command so we would not want to get those ones).\n    const boundaryNode = inverseDOMPath.next().value;\n\n    // We only traverse through deep inline nodes. If we cannot find a\n    // meanfingful state between them, that means we hit a block.\n    let cType = undefined;\n\n    // Traverse the DOM in the given direction to check what type of content\n    // there is.\n    let lastSpace = null;\n    for (const node of domPath) {\n        if (node.nodeType === Node.TEXT_NODE) {\n            // ZWNBSP are technical characters which should be ignored.\n            const value = node.nodeValue.replaceAll('\\ufeff', '');\n            // If we hit a text node, the state depends on the path direction:\n            // any space encountered backwards is a visible space if we hit\n            // visible content afterwards. If going forward, spaces are only\n            // visible if we have content backwards.\n            if (direction === DIRECTIONS.LEFT) {\n                if (!isWhitespace(value)) {\n                    if (lastSpace) {\n                        cType = CTYPES.SPACE;\n                    } else {\n                        const rightLeaf = rightLeafOnlyNotBlockPath(node).next().value;\n                        const hasContentRight = rightLeaf && !whitespaceAtStartRegex.test(rightLeaf.textContent);\n                        cType = !hasContentRight && whitespaceAtEndRegex.test(node.textContent) ? CTYPES.SPACE : CTYPES.CONTENT;\n                    }\n                    break;\n                }\n                if (value.length) {\n                    lastSpace = node;\n                }\n            } else {\n                leftCType = leftCType || getState(el, offset, DIRECTIONS.LEFT).cType;\n                if (whitespaceAtStartRegex.test(value)) {\n                    const leftLeaf = leftLeafOnlyNotBlockPath(node).next().value;\n                    const hasContentLeft = leftLeaf && !whitespaceAtEndRegex.test(leftLeaf.textContent);\n                    const rct = !isWhitespace(value)\n                        ? CTYPES.CONTENT\n                        : getState(...rightPos(node), DIRECTIONS.RIGHT).cType;\n                    cType =\n                        leftCType & CTYPES.CONTENT && rct & (CTYPES.CONTENT | CTYPES.BR) && !hasContentLeft\n                            ? CTYPES.SPACE\n                            : rct;\n                    break;\n                }\n                if (!isWhitespace(value)) {\n                    cType = CTYPES.CONTENT;\n                    break;\n                }\n            }\n        } else if (node.nodeName === 'BR') {\n            cType = CTYPES.BR;\n            break;\n        } else if (isVisible(node)) {\n            // E.g. an image\n            cType = CTYPES.CONTENT;\n            break;\n        }\n    }\n\n    if (cType === undefined) {\n        cType = reasons.includes(PATH_END_REASONS.BLOCK_HIT)\n            ? CTYPES.BLOCK_OUTSIDE\n            : CTYPES.BLOCK_INSIDE;\n    }\n\n    return {\n        node: boundaryNode,\n        direction: direction,\n        cType: cType, // Short for contentType\n    };\n}\nconst priorityRestoreStateRules = [\n    // Each entry is a list of two objects, with each key being optional (the\n    // more key-value pairs, the bigger the priority).\n    // {direction: ..., cType1: ..., cType2: ...}\n    // ->\n    // {spaceVisibility: (false|true), brVisibility: (false|true)}\n    [\n        // Replace a space by &nbsp; when it was not collapsed before and now is\n        // collapsed (one-letter word removal for example).\n        { cType1: CTYPES.CONTENT, cType2: CTYPES.SPACE | CTGROUPS.BLOCK },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was content before and now it is\n        // a BR.\n        { direction: DIRECTIONS.LEFT, cType1: CTGROUPS.INLINE, cType2: CTGROUPS.BR },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was content before and now it is\n        // a BR (removal of last character before a BR for example).\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.CONTENT, cType2: CTGROUPS.BR },\n        { spaceVisibility: true },\n    ],\n    [\n        // Replace a space by &nbsp; when it was visible thanks to a BR which\n        // is now gone.\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.BR, cType2: CTYPES.SPACE | CTGROUPS.BLOCK },\n        { spaceVisibility: true },\n    ],\n    [\n        // Remove all collapsed spaces when a space is removed.\n        { cType1: CTYPES.SPACE },\n        { spaceVisibility: false },\n    ],\n    [\n        // Remove spaces once the preceeding BR is removed\n        { direction: DIRECTIONS.LEFT, cType1: CTGROUPS.BR },\n        { spaceVisibility: false },\n    ],\n    [\n        // Remove space before block once content is put after it (otherwise it\n        // would become visible).\n        { cType1: CTGROUPS.BLOCK, cType2: CTGROUPS.INLINE | CTGROUPS.BR },\n        { spaceVisibility: false },\n    ],\n    [\n        // Duplicate a BR once the content afterwards disappears\n        { direction: DIRECTIONS.RIGHT, cType1: CTGROUPS.INLINE, cType2: CTGROUPS.BLOCK },\n        { brVisibility: true },\n    ],\n    [\n        // Remove a BR at the end of a block once inline content is put after\n        // it (otherwise it would act as a line break).\n        {\n            direction: DIRECTIONS.RIGHT,\n            cType1: CTGROUPS.BLOCK,\n            cType2: CTGROUPS.INLINE | CTGROUPS.BR,\n        },\n        { brVisibility: false },\n    ],\n    [\n        // Remove a BR once the BR that preceeds it is now replaced by\n        // content (or if it was a BR at the start of a block which now is\n        // a trailing BR).\n        {\n            direction: DIRECTIONS.LEFT,\n            cType1: CTGROUPS.BR | CTGROUPS.BLOCK,\n            cType2: CTGROUPS.INLINE,\n        },\n        { brVisibility: false, extraBRRemovalCondition: brNode => isFakeLineBreak(brNode) },\n    ],\n];\nfunction restoreStateRuleHashCode(direction, cType1, cType2) {\n    return `${direction}-${cType1}-${cType2}`;\n}\nconst allRestoreStateRules = (function () {\n    const map = new Map();\n\n    const keys = ['direction', 'cType1', 'cType2'];\n    for (const direction of Object.values(DIRECTIONS)) {\n        for (const cType1 of Object.values(CTYPES)) {\n            for (const cType2 of Object.values(CTYPES)) {\n                const rule = { direction: direction, cType1: cType1, cType2: cType2 };\n\n                // Search for the rules which match whatever their priority\n                const matchedRules = [];\n                for (const entry of priorityRestoreStateRules) {\n                    let priority = 0;\n                    for (const key of keys) {\n                        const entryKeyValue = entry[0][key];\n                        if (entryKeyValue !== undefined) {\n                            if (\n                                typeof entryKeyValue === 'boolean'\n                                    ? rule[key] === entryKeyValue\n                                    : rule[key] & entryKeyValue\n                            ) {\n                                priority++;\n                            } else {\n                                priority = -1;\n                                break;\n                            }\n                        }\n                    }\n                    if (priority >= 0) {\n                        matchedRules.push([priority, entry[1]]);\n                    }\n                }\n\n                // Create the final rule by merging found rules by order of\n                // priority\n                const finalRule = {};\n                for (let p = 0; p <= keys.length; p++) {\n                    for (const entry of matchedRules) {\n                        if (entry[0] === p) {\n                            Object.assign(finalRule, entry[1]);\n                        }\n                    }\n                }\n\n                // Create an unique identifier for the set of values\n                // direction - state 1 - state2 to add the rule in the map\n                const hashCode = restoreStateRuleHashCode(direction, cType1, cType2);\n                map.set(hashCode, finalRule);\n            }\n        }\n    }\n\n    return map;\n})();\n/**\n * Restores the given state starting before the given while looking in the given\n * direction.\n *\n * @param {Object} prevStateData @see getState\n * @param {boolean} debug=false - if true, adds nicely formatted\n *     console logs to help with debugging.\n * @returns {Object|undefined} the rule that was applied to restore the state,\n *     if any, for testing purposes.\n */\nexport function restoreState(prevStateData, debug=false) {\n    const { node, direction, cType: cType1, oldEditableHTML } = prevStateData;\n    if (!node || !node.parentNode) {\n        // FIXME sometimes we want to restore the state starting from a node\n        // which has been removed by another restoreState call... Not sure if\n        // it is a problem or not, to investigate.\n        return;\n    }\n    const [el, offset] = direction === DIRECTIONS.LEFT ? leftPos(node) : rightPos(node);\n    const { cType: cType2 } = getState(el, offset, direction);\n\n    /**\n     * Knowing the old state data and the new state data, we know if we have to\n     * do something or not, and what to do.\n     */\n    const ruleHashCode = restoreStateRuleHashCode(direction, cType1, cType2);\n    const rule = allRestoreStateRules.get(ruleHashCode);\n    if (debug) {\n        const editable = closestElement(node, '.odoo-editor-editable');\n        console.log(\n            '%c' + makeZeroWidthCharactersVisible(node.textContent).replaceAll(' ', '_') + '\\n' +\n            '%c' + (direction === DIRECTIONS.LEFT ? 'left' : 'right') + '\\n' +\n            '%c' + ctypeToString(cType1) + '\\n' +\n            '%c' + ctypeToString(cType2) + '\\n' +\n            '%c' + 'BEFORE: ' + (oldEditableHTML || '(unavailable)') + '\\n' +\n            '%c' + 'AFTER:  ' + (editable ? makeZeroWidthCharactersVisible(editable.innerHTML).replaceAll(' ', '_') : '(unavailable)') + '\\n',\n            'color: white; display: block; width: 100%;',\n            'color: ' + (direction === DIRECTIONS.LEFT ? 'magenta' : 'lightgreen') + '; display: block; width: 100%;',\n            'color: pink; display: block; width: 100%;',\n            'color: lightblue; display: block; width: 100%;',\n            'color: white; display: block; width: 100%;',\n            'color: white; display: block; width: 100%;',\n            rule,\n        );\n    }\n    if (Object.values(rule).filter(x => x !== undefined).length) {\n        const inverseDirection = direction === DIRECTIONS.LEFT ? DIRECTIONS.RIGHT : DIRECTIONS.LEFT;\n        enforceWhitespace(el, offset, inverseDirection, rule);\n    }\n    return rule;\n}\n/**\n * Enforces the whitespace and BR visibility in the given direction starting\n * from the given position.\n *\n * @param {HTMLElement} el\n * @param {number} offset\n * @param {number} direction @see DIRECTIONS.LEFT @see DIRECTIONS.RIGHT\n * @param {Object} rule\n * @param {boolean} [rule.spaceVisibility]\n * @param {boolean} [rule.brVisibility]\n */\nexport function enforceWhitespace(el, offset, direction, rule) {\n    let domPath, whitespaceAtEdgeRegex;\n    if (direction === DIRECTIONS.LEFT) {\n        domPath = leftLeafOnlyNotBlockPath(el, offset);\n        whitespaceAtEdgeRegex = new RegExp(whitespace + '+$');\n    } else {\n        domPath = rightLeafOnlyNotBlockPath(el, offset);\n        whitespaceAtEdgeRegex = new RegExp('^' + whitespace + '+');\n    }\n\n    const invisibleSpaceTextNodes = [];\n    let foundVisibleSpaceTextNode = null;\n    for (const node of domPath) {\n        if (node.nodeName === 'BR') {\n            if (rule.brVisibility === undefined) {\n                break;\n            }\n            if (rule.brVisibility) {\n                node.before(document.createElement('br'));\n            } else {\n                if (!rule.extraBRRemovalCondition || rule.extraBRRemovalCondition(node)) {\n                    node.remove();\n                }\n            }\n            break;\n        } else if (node.nodeType === Node.TEXT_NODE && !isInPre(node)) {\n            if (whitespaceAtEdgeRegex.test(node.nodeValue)) {\n                // If we hit spaces going in the direction, either they are in a\n                // visible text node and we have to change the visibility of\n                // those spaces, or it is in an invisible text node. In that\n                // last case, we either remove the spaces if there are spaces in\n                // a visible text node going further in the direction or we\n                // change the visiblity or those spaces.\n                if (!isWhitespace(node)) {\n                    foundVisibleSpaceTextNode = node;\n                    break;\n                } else {\n                    invisibleSpaceTextNodes.push(node);\n                }\n            } else if (!isWhitespace(node)) {\n                break;\n            }\n        } else {\n            break;\n        }\n    }\n\n    if (rule.spaceVisibility === undefined) {\n        return;\n    }\n    if (!rule.spaceVisibility) {\n        for (const node of invisibleSpaceTextNodes) {\n            // Empty and not remove to not mess with offset-based positions in\n            // commands implementation, also remove non-block empty parents.\n            node.nodeValue = '';\n            const ancestorPath = closestPath(node.parentNode);\n            let toRemove = null;\n            for (const pNode of ancestorPath) {\n                if (toRemove) {\n                    toRemove.remove();\n                }\n                if (pNode.childNodes.length === 1 && !isBlock(pNode)) {\n                    pNode.after(node);\n                    toRemove = pNode;\n                } else {\n                    break;\n                }\n            }\n        }\n    }\n    const spaceNode = foundVisibleSpaceTextNode || invisibleSpaceTextNodes[0];\n    if (spaceNode) {\n        let spaceVisibility = rule.spaceVisibility;\n        // In case we are asked to replace the space by a &nbsp;, disobey and\n        // do the opposite if that space is currently not visible\n        // TODO I'd like this to not be needed, it feels wrong...\n        if (\n            spaceVisibility &&\n            !foundVisibleSpaceTextNode &&\n            getState(...rightPos(spaceNode), DIRECTIONS.RIGHT).cType & CTGROUPS.BLOCK &&\n            getState(...leftPos(spaceNode), DIRECTIONS.LEFT).cType !== CTYPES.CONTENT\n        ) {\n            spaceVisibility = false;\n        }\n        spaceNode.nodeValue = spaceNode.nodeValue.replace(whitespaceAtEdgeRegex, spaceVisibility ? '\\u00A0' : '');\n    }\n}\n\n/**\n * Takes a color (rgb, rgba or hex) and returns its hex representation. If the\n * color is given in rgba, the background color of the node whose color we're\n * converting is used in conjunction with the alpha to compute the resulting\n * color (using the formula: `alpha*color + (1 - alpha)*background` for each\n * channel).\n *\n * @param {string} rgb\n * @param {HTMLElement} [node]\n * @returns {string} hexadecimal color (#RRGGBB)\n */\nexport function rgbToHex(rgb = '', node = null) {\n    if (rgb.startsWith('#')) {\n        return rgb;\n    } else if (rgb.startsWith('rgba')) {\n        const values = rgb.match(/[\\d\\.]{1,5}/g) || [];\n        const alpha = parseFloat(values.pop());\n        // Retrieve the background color.\n        let bgRgbValues = [];\n        if (node) {\n            let bgColor = getComputedStyle(node).backgroundColor;\n            if (bgColor.startsWith('rgba')) {\n                // The background color is itself rgba so we need to compute\n                // the resulting color using the background color of its\n                // parent.\n                bgColor = rgbToHex(bgColor, node.parentElement);\n            }\n            if (bgColor && bgColor.startsWith('#')) {\n                bgRgbValues = (bgColor.match(/[\\da-f]{2}/gi) || []).map(val => parseInt(val, 16));\n            } else if (bgColor && bgColor.startsWith('rgb')) {\n                bgRgbValues = (bgColor.match(/[\\d\\.]{1,5}/g) || []).map(val => parseInt(val));\n            }\n        }\n        bgRgbValues = bgRgbValues.length ? bgRgbValues : [255, 255, 255]; // Default to white.\n\n        return (\n            '#' +\n            values.map((value, index) => {\n                const converted = Math.floor(alpha * parseInt(value) + (1 - alpha) * bgRgbValues[index]);\n                const hex = parseInt(converted).toString(16);\n                return hex.length === 1 ? '0' + hex : hex;\n            }).join('')\n        );\n    } else {\n        return (\n            '#' +\n            (rgb.match(/\\d{1,3}/g) || [])\n                .map(x => {\n                    x = parseInt(x).toString(16);\n                    return x.length === 1 ? '0' + x : x;\n                })\n                .join('')\n        );\n    }\n}\n\nexport function parseHTML(document, html) {\n    const fragment = document.createDocumentFragment();\n    const parser = new document.defaultView.DOMParser();\n    const parsedDocument = parser.parseFromString(html, 'text/html');\n    fragment.replaceChildren(...parsedDocument.body.childNodes);\n    return fragment;\n}\n\n/**\n * Take a string containing a size in pixels, return that size as a float.\n *\n * @param {string} sizeString\n * @returns {number}\n */\nexport function pxToFloat(sizeString) {\n    return parseFloat(sizeString.replace('px', ''));\n}\n\n/**\n * Returns position of a range in form of object (end\n * position of a range in case of non-collapsed range).\n *\n * @param {HTMLElement} el element for which range postion will be calculated\n * @param {Document} document\n * @param {Object} [options]\n * @param {Number} [options.marginRight] right margin to be considered\n * @param {Number} [options.marginBottom] bottom margin to be considered\n * @param {Number} [options.marginTop] top margin to be considered\n * @param {Number} [options.marginLeft] left margin to be considered\n * @param {Function} [options.getContextFromParentRect] to get context rect from parent\n * @returns {Object | undefined}\n */\nexport function getRangePosition(el, document, options = {}) {\n    const selection = document.getSelection();\n    if (!selection.rangeCount) return;\n    const range = selection.getRangeAt(0);\n    const isRtl = options.direction === 'rtl';\n\n    const marginRight = options.marginRight || 20;\n    const marginBottom = options.marginBottom || 20;\n    const marginTop = options.marginTop || 10;\n    const marginLeft = options.marginLeft || 10;\n\n    let offset;\n    if (range.endOffset - 1 > 0) {\n        const clonedRange = range.cloneRange();\n        clonedRange.setStart(range.endContainer, range.endOffset - 1);\n        clonedRange.setEnd(range.endContainer, range.endOffset);\n        const rect = clonedRange.getBoundingClientRect();\n        offset = { height: rect.height, left: rect.left + rect.width, top: rect.top };\n        clonedRange.detach();\n    }\n\n    if (!offset || offset.height === 0) {\n        const clonedRange = range.cloneRange();\n        const shadowCaret = document.createTextNode('|');\n        clonedRange.insertNode(shadowCaret);\n        clonedRange.selectNode(shadowCaret);\n        const rect = clonedRange.getBoundingClientRect();\n        offset = { height: rect.height, left: rect.left, top: rect.top };\n        shadowCaret.remove();\n        clonedRange.detach();\n    }\n\n    if (isRtl) {\n        // To handle the RTL case we shift the elelement to the left by its size\n        // and handle it the same as left.\n        offset.right = offset.left - el.offsetWidth;\n        const leftMove = Math.max(0, offset.right + el.offsetWidth + marginLeft - window.innerWidth);\n        if (leftMove && offset.right - leftMove > marginRight) {\n            offset.right -= leftMove;\n        } else if (offset.right - leftMove < marginRight) {\n            offset.right = marginRight;\n        }\n    }\n\n    const leftMove = Math.max(0, offset.left + el.offsetWidth + marginRight - window.innerWidth);\n    if (leftMove && offset.left - leftMove > marginLeft) {\n        offset.left -= leftMove;\n    } else if (offset.left - leftMove < marginLeft) {\n        offset.left = marginLeft;\n    }\n\n    if (options.getContextFromParentRect) {\n        const parentContextRect = options.getContextFromParentRect();\n        offset.left += parentContextRect.left;\n        offset.top += parentContextRect.top;\n        if (isRtl) {\n            offset.right += parentContextRect.left;\n        }\n    }\n\n    if (\n        offset.top - marginTop + offset.height + el.offsetHeight > window.innerHeight &&\n        offset.top - el.offsetHeight - marginBottom > 0\n    ) {\n        offset.top -= el.offsetHeight;\n    } else {\n        offset.top += offset.height;\n    }\n\n    if (offset) {\n        offset.top += window.scrollY;\n        offset.left += window.scrollX;\n        if (isRtl) {\n            offset.right += window.scrollX;\n        }\n    }\n    if (isRtl) {\n        // Get the actual right value.\n        offset.right = window.innerWidth - offset.right - el.offsetWidth;\n    }\n\n    return offset;\n}\n\nexport const isNotEditableNode = node =>\n    node.getAttribute &&\n    node.getAttribute('contenteditable') &&\n    node.getAttribute('contenteditable').toLowerCase() === 'false';\n\nexport const isRoot = node => node.oid === \"root\";\n\nexport const leftLeafFirstPath = createDOMPathGenerator(DIRECTIONS.LEFT);\nexport const leftLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.LEFT, {\n    leafOnly: true,\n    stopTraverseFunction: isBlock,\n    stopFunction: node => isBlock(node) || isRoot(node),\n});\nexport const leftLeafOnlyInScopeNotBlockEditablePath = createDOMPathGenerator(DIRECTIONS.LEFT, {\n    leafOnly: true,\n    inScope: true,\n    stopTraverseFunction: node => isNotEditableNode(node) || isBlock(node),\n    stopFunction: node => isNotEditableNode(node) || isBlock(node) || isRoot(node),\n});\n\nexport const rightLeafOnlyNotBlockPath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    stopTraverseFunction: isBlock,\n    stopFunction: node => isBlock(node) || isRoot(node),\n});\n\nexport const rightLeafOnlyPathNotBlockNotEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    stopFunction: node => isRoot(node),\n});\nexport const rightLeafOnlyInScopeNotBlockEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    inScope: true,\n    stopTraverseFunction: node => isNotEditableNode(node) || isBlock(node),\n    stopFunction: node => isNotEditableNode(node) || isBlock(node) || isRoot(node),\n});\nexport const rightLeafOnlyNotBlockNotEditablePath = createDOMPathGenerator(DIRECTIONS.RIGHT, {\n    leafOnly: true,\n    stopTraverseFunction: node => isNotEditableNode(node) || isBlock(node),\n    stopFunction: node => isBlock(node) && !isNotEditableNode(node) || isRoot(node),\n});\n//------------------------------------------------------------------------------\n// Miscelaneous\n//------------------------------------------------------------------------------\nexport function peek(arr) {\n    return arr[arr.length - 1];\n}\n/**\n * Check user OS\n * @returns {boolean}\n */\nexport function isMacOS() {\n    return window.navigator.userAgent.includes('Mac');\n}\n\n/**\n * Remove zero-width spaces from the provided node and its descendants.\n * Note: Does NOT remove zero-width NON-BREAK spaces (feff)!\n *\n * @param {Node} node\n */\nexport function cleanZWS(node) {\n    [node, ...descendants(node)]\n        .filter(node => node.nodeType === Node.TEXT_NODE && node.nodeValue.includes('\\u200B'))\n        .forEach(node => node.nodeValue = node.nodeValue.replace(/\\u200B/g, ''));\n}\n", "/** @odoo-module **/\n\nexport const fonts = {\n    /**\n     * Retrieves all the CSS rules which match the given parser (Regex).\n     *\n     * @param {Regex} filter\n     * @returns {Object[]} Array of CSS rules descriptions (objects). A rule is\n     *          defined by 3 values: 'selector', 'css' and 'names'. 'selector'\n     *          is a string which contains the whole selector, 'css' is a string\n     *          which contains the css properties and 'names' is an array of the\n     *          first captured groups for each selector part. E.g.: if the\n     *          filter is set to match .fa-* rules and capture the icon names,\n     *          the rule:\n     *              '.fa-alias1::before, .fa-alias2::before { hello: world; }'\n     *          will be retrieved as\n     *              {\n     *                  selector: '.fa-alias1::before, .fa-alias2::before',\n     *                  css: 'hello: world;',\n     *                  names: ['.fa-alias1', '.fa-alias2'],\n     *              }\n     */\n    cacheCssSelectors: {},\n    getCssSelectors: function (filter) {\n        if (this.cacheCssSelectors[filter]) {\n            return this.cacheCssSelectors[filter];\n        }\n        this.cacheCssSelectors[filter] = [];\n        var sheets = document.styleSheets;\n        for (var i = 0; i < sheets.length; i++) {\n            var rules;\n            try {\n                // try...catch because Firefox not able to enumerate\n                // document.styleSheets[].cssRules[] for cross-domain\n                // stylesheets.\n                rules = sheets[i].rules || sheets[i].cssRules;\n            } catch {\n                continue;\n            }\n            if (!rules) {\n                continue;\n            }\n\n            for (var r = 0 ; r < rules.length ; r++) {\n                var selectorText = rules[r].selectorText;\n                if (!selectorText) {\n                    continue;\n                }\n                var selectors = selectorText.split(/\\s*,\\s*/);\n                var data = null;\n                for (var s = 0; s < selectors.length; s++) {\n                    var match = selectors[s].trim().match(filter);\n                    if (!match) {\n                        continue;\n                    }\n                    if (!data) {\n                        data = {\n                            selector: match[0],\n                            css: rules[r].cssText.replace(/(^.*\\{\\s*)|(\\s*\\}\\s*$)/g, ''),\n                            names: [match[1]]\n                        };\n                    } else {\n                        data.selector += (', ' + match[0]);\n                        data.names.push(match[1]);\n                    }\n                }\n                if (data) {\n                    this.cacheCssSelectors[filter].push(data);\n                }\n            }\n        }\n        return this.cacheCssSelectors[filter];\n    },\n    /**\n     * List of font icons to load by editor. The icons are displayed in the media\n     * editor and identified like font and image (can be colored, spinned, resized\n     * with fa classes).\n     * To add font, push a new object {base, parser}\n     *\n     * - base: class who appear on all fonts\n     * - parser: regular expression used to select all font in css stylesheets\n     *\n     * @type Array\n     */\n    fontIcons: [{base: 'fa', parser: /\\.(fa-(?:\\w|-)+)::?before/i}],\n    computedFonts: false,\n    /**\n     * Searches the fonts described by the @see fontIcons variable.\n     */\n    computeFonts: function () {\n        if (!this.computedFonts) {\n            var self = this;\n            this.fontIcons.forEach((data) => {\n                data.cssData = self.getCssSelectors(data.parser);\n                data.alias = data.cssData.map((x) => x.names).flat();\n            });\n            this.computedFonts = true;\n        }\n    },\n};\n\nexport default fonts;\n", "/** @odoo-module **/\n\nimport { loadBundle } from \"@web/core/assets\";\nimport { ensureJQuery } from \"@web/core/ensure_jquery\";\nimport { attachComponent } from \"@web_editor/js/core/owl_utils\";\n\nexport async function loadWysiwygFromTextarea(parent, textarea, options) {\n    var loading = textarea.nextElementSibling;\n    if (loading && !loading.classList.contains('o_wysiwyg_loading')) {\n        loading = null;\n    }\n    const $textarea = $(textarea);\n    const currentOptions = Object.assign({}, options);\n    currentOptions.value = currentOptions.value || $textarea.val() || '';\n    if (!currentOptions.value.trim()) {\n        currentOptions.value = '<p><br></p>';\n    }\n\n    await ensureJQuery();\n    await loadBundle(\"web_editor.assets_wysiwyg\");\n    const { Wysiwyg } = await odoo.loader.modules.get('@web_editor/js/wysiwyg/wysiwyg');\n    let wysiwyg;\n    class LegacyWysiwyg extends Wysiwyg {\n        constructor(...args) {\n            super(...args);\n            wysiwyg = this;\n        }\n    }\n\n    const $wysiwygWrapper = $textarea.closest('.o_wysiwyg_textarea_wrapper');\n    const $form = $textarea.closest('form');\n\n    // hide and append the $textarea in $form so it's value will be send\n    // through the form.\n    $textarea.hide();\n    $form.append($textarea);\n    $wysiwygWrapper.html('');\n    const wysiwygWrapper = $wysiwygWrapper[0];\n    await attachComponent(parent, wysiwygWrapper, LegacyWysiwyg, {\n        options: currentOptions,\n        editingValue: currentOptions.value,\n    });\n\n    $form.find('.note-editable').data('wysiwyg', wysiwyg);\n\n    // o_we_selected_image has not always been removed when\n    // saving a post so we need the line below to remove it if it is present.\n    $form.find('.note-editable').find('img.o_we_selected_image').removeClass('o_we_selected_image');\n\n    let b64imagesPending = true;\n    $form.on('click', 'button[type=submit]', (ev) => {\n        if (b64imagesPending) {\n            ev.preventDefault();\n            wysiwyg.savePendingImages().finally(() => {\n                b64imagesPending = false;\n                ev.currentTarget.click();\n            });\n        } else {\n            $form.find('.note-editable').find('img.o_we_selected_image').removeClass('o_we_selected_image');\n            // float-start class messes up the post layout OPW 769721\n            $form.find('.note-editable').find('img.float-start').removeClass('float-start');\n            $textarea.val(wysiwyg.getValue());\n        }\n    });\n\n    return wysiwyg;\n};\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\n\npublicWidget.registry.SignUpForm = publicWidget.Widget.extend({\n    selector: '.oe_reset_password_form',\n    events: {\n        'submit': '_onSubmit',\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _onSubmit: function () {\n        var $btn = this.$('.oe_login_buttons > button[type=\"submit\"]');\n        $btn.attr('disabled', 'disabled');\n        $btn.prepend('<i class=\"fa fa-refresh fa-spin\"/> ');\n    },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\n\npublicWidget.registry.SignUpForm = publicWidget.Widget.extend({\n    selector: '.oe_signup_form',\n    events: {\n        'submit': '_onSubmit',\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _onSubmit: function () {\n        var $btn = this.$('.oe_login_buttons > button[type=\"submit\"]');\n        $btn.attr('disabled', 'disabled');\n        $btn.prepend('<i class=\"fa fa-refresh fa-spin\"/> ');\n    },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { rpc } from \"@web/core/network/rpc\";\n\npublicWidget.registry.portalDetails = publicWidget.Widget.extend({\n    selector: '.o_portal_details',\n    events: {\n        'change select[name=\"country_id\"]': '_onCountryChange',\n    },\n\n    /**\n     * @override\n     */\n    start: function () {\n        var def = this._super.apply(this, arguments);\n\n        this.$state = this.$('select[name=\"state_id\"]');\n        this.$stateOptions = this.$state.filter(':enabled').find('option:not(:first)');\n        this._adaptAddressForm();\n\n        return def;\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _adaptAddressForm: function () {\n        var $country = this.$('select[name=\"country_id\"]');\n        var countryID = ($country.val() || 0);\n        this.$stateOptions.detach();\n        var $displayedState = this.$stateOptions.filter('[data-country_id=' + countryID + ']');\n        var nb = $displayedState.appendTo(this.$state).removeClass('d-none').show().length;\n        this.$state.parent().toggle(nb >= 1);\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _onCountryChange: function () {\n        this._adaptAddressForm();\n    },\n});\n\nexport default publicWidget.registry.portalDetails;\n\nexport const PortalHomeCounters = publicWidget.Widget.extend({\n    selector: '.o_portal_my_home',\n\n    /**\n     * @override\n     */\n    start: function () {\n        var def = this._super.apply(this, arguments);\n        this._updateCounters();\n        return def;\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * Return a list of counters name linked to a line that we want to keep\n     * regardless of the number of documents present\n     * @private\n     * @returns {Array}\n     */\n    _getCountersAlwaysDisplayed() {\n        return [];\n    },\n\n    /**\n     * @private\n     */\n    async _updateCounters(elem) {\n        const needed = Object.values(this.el.querySelectorAll('[data-placeholder_count]'))\n                                .map(documentsCounterEl => documentsCounterEl.dataset['placeholder_count']);\n        const numberRpc = Math.min(Math.ceil(needed.length / 5), 3); // max 3 rpc, up to 5 counters by rpc ideally\n        const counterByRpc = Math.ceil(needed.length / numberRpc);\n        const countersAlwaysDisplayed = this._getCountersAlwaysDisplayed();\n\n        const proms = [...Array(Math.min(numberRpc, needed.length)).keys()].map(async i => {\n            const documentsCountersData = await rpc(\"/my/counters\", {\n                counters: needed.slice(i * counterByRpc, (i + 1) * counterByRpc)\n            });\n            Object.keys(documentsCountersData).forEach(counterName => {\n                const documentsCounterEl = this.el.querySelector(`[data-placeholder_count='${counterName}']`);\n                documentsCounterEl.textContent = documentsCountersData[counterName];\n                // The element is hidden by default, only show it if its counter is > 0 or if it's in the list of counters always shown\n                if (documentsCountersData[counterName] !== 0 || countersAlwaysDisplayed.includes(counterName)) {\n                    documentsCounterEl.closest('.o_portal_index_card').classList.remove('d-none');\n                }\n            });\n            return documentsCountersData;\n        });\n        return Promise.all(proms).then((results) => {\n            this.el.querySelector('.o_portal_doc_spinner').remove();\n        });\n    },\n});\n\npublicWidget.registry.PortalHomeCounters = PortalHomeCounters;\n\npublicWidget.registry.portalSearchPanel = publicWidget.Widget.extend({\n    selector: '.o_portal_search_panel',\n    events: {\n        'click .dropdown-item': '_onDropdownItemClick',\n        'submit': '_onSubmit',\n    },\n\n    /**\n     * @override\n     */\n    start: function () {\n        var def = this._super.apply(this, arguments);\n        this._adaptSearchLabel(this.$('.dropdown-item.active'));\n        return def;\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _adaptSearchLabel: function (elem) {\n        var $label = $(elem).clone();\n        $label.find('span.nolabel').remove();\n        this.$('input[name=\"search\"]').attr('placeholder', $label.text().trim());\n    },\n    /**\n     * @private\n     */\n    _search: function () {\n        var search = new URL(window.location).searchParams;\n        search.set(\"search_in\", this.$('.dropdown-item.active').attr('href')?.replace('#', '') || \"\");\n        search.set(\"search\", this.$('input[name=\"search\"]').val());\n        window.location.search = search.toString();\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _onDropdownItemClick: function (ev) {\n        ev.preventDefault();\n        var $item = $(ev.currentTarget);\n        $item.closest('.dropdown-menu').find('.dropdown-item').removeClass('active');\n        $item.addClass('active');\n\n        this._adaptSearchLabel(ev.currentTarget);\n    },\n    /**\n     * @private\n     */\n    _onSubmit: function (ev) {\n        ev.preventDefault();\n        this._search();\n    },\n});\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { escape } from \"@web/core/utils/strings\";\nimport { renderToElement } from \"@web/core/utils/render\";\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { post } from \"@web/core/network/http_service\";\nimport { Component } from \"@odoo/owl\";\nimport { rpc, RPCError } from \"@web/core/network/rpc\";\n\n/**\n * Widget PortalComposer\n *\n * Display the composer (according to access right)\n *\n */\nvar PortalComposer = publicWidget.Widget.extend({\n    template: 'portal.Composer',\n    events: {\n        'change .o_portal_chatter_file_input': '_onFileInputChange',\n        'click .o_portal_chatter_attachment_btn': '_onAttachmentButtonClick',\n        'click .o_portal_chatter_attachment_delete': 'async _onAttachmentDeleteClick',\n        'click .o_portal_chatter_composer_btn': 'async _onSubmitButtonClick',\n    },\n\n    /**\n     * @constructor\n     */\n    init: function (parent, options) {\n        this._super.apply(this, arguments);\n        this.options = Object.assign({\n            'allow_composer': true,\n            'display_composer': false,\n            'csrf_token': odoo.csrf_token,\n            'token': false,\n            'res_model': false,\n            'res_id': false,\n            'send_button_label': _t(\"Send\"),\n        }, options || {});\n        this.attachments = [];\n        this.notification = this.bindService(\"notification\");\n    },\n    /**\n     * @override\n     */\n    start: function () {\n        var self = this;\n        this.$attachmentButton = this.$('.o_portal_chatter_attachment_btn');\n        this.$fileInput = this.$('.o_portal_chatter_file_input');\n        this.$sendButton = this.$('.o_portal_chatter_composer_btn');\n        this.$attachments = this.$('.o_portal_chatter_composer_input .o_portal_chatter_attachments');\n        this.$inputTextarea = this.$('.o_portal_chatter_composer_input textarea[name=\"message\"]');\n\n        return this._super.apply(this, arguments).then(function () {\n            if (self.options.default_attachment_ids) {\n                self.attachments = self.options.default_attachment_ids || [];\n                self.attachments.forEach((attachment) => {\n                    attachment.state = 'done';\n                });\n                self._updateAttachments();\n            }\n            return Promise.resolve();\n        });\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _onAttachmentButtonClick: function () {\n        this.$fileInput.click();\n    },\n    /**\n     * @private\n     * @param {Event} ev\n     * @returns {Promise}\n     */\n    _onAttachmentDeleteClick: function (ev) {\n        var self = this;\n        var attachmentId = $(ev.currentTarget).closest('.o_portal_chatter_attachment').data('id');\n        var accessToken = this.attachments.find(attachment => attachment.id === attachmentId).access_token;\n        ev.preventDefault();\n        ev.stopPropagation();\n\n        this.$sendButton.prop('disabled', true);\n\n        return rpc('/portal/attachment/remove', {\n            'attachment_id': attachmentId,\n            'access_token': accessToken,\n        }).then(function () {\n            self.attachments = self.attachments.filter(attachment => attachment.id !== attachmentId);\n            self._updateAttachments();\n            self.$sendButton.prop('disabled', false);\n        });\n    },\n    _prepareAttachmentData: function (file) {\n        return {\n            is_pending: true,\n            thread_id: this.options.res_id,\n            thread_model: this.options.res_model,\n            token: this.options.token,\n            ufile: file,\n        };\n    },\n    /**\n     * @private\n     * @returns {Promise}\n     */\n    _onFileInputChange: function () {\n        var self = this;\n\n        this.$sendButton.prop('disabled', true);\n\n        return Promise.all([...this.$fileInput[0].files].map((file) => {\n            return new Promise(function (resolve, reject) {\n                var data = self._prepareAttachmentData(file);\n                if (odoo.csrf_token) {\n                    data.csrf_token = odoo.csrf_token;\n                }\n                post('/mail/attachment/upload', data).then(function (res) {\n                    let attachment = res.data[\"ir.attachment\"][0]\n                    attachment.state = 'pending';\n                    self.attachments.push(attachment);\n                    self._updateAttachments();\n                    resolve();\n                }).catch(function (error) {\n                    if (error instanceof RPCError) {\n                        self.notification.add(\n                            _t(\"Could not save file <strong>%s</strong>\", escape(file.name)),\n                            { type: 'warning', sticky: true }\n                        );\n                        resolve();\n                    }\n                });\n            });\n        })).then(function () {\n            // ensures any selection triggers a change, even if the same files are selected again\n            self.$fileInput[0].value = null;\n            self.$sendButton.prop('disabled', false);\n        });\n    },\n    /**\n     * prepares data to send message\n     *\n     * @private\n     */\n    _prepareMessageData: function () {\n        return Object.assign(this.options || {}, {\n            thread_model: this.options.res_model,\n            thread_id: this.options.res_id,\n            post_data: {\n                body: this.$('textarea[name=\"message\"]').val(),\n                attachment_ids: this.attachments.map((a) => a.id),\n                message_type: \"comment\",\n                subtype_xmlid: \"mail.mt_comment\",\n            },\n            attachment_tokens: this.attachments.map((a) => a.access_token),\n            token: this.options.token,\n            hash: this.options.hash,\n            pid: this.options.pid,\n        });\n    },\n    /**\n     * @private\n     * @param {Event} ev\n     */\n    _onSubmitButtonClick: function (ev) {\n        ev.preventDefault();\n        const error = this._onSubmitCheckContent();\n        if (error) {\n            this.$inputTextarea.addClass('border-danger');\n            this.$(\".o_portal_chatter_composer_error\").text(error).removeClass('d-none');\n            return Promise.reject();\n        } else {\n            return this._chatterPostMessage(ev.currentTarget.getAttribute('data-action'));\n        }\n    },\n\n    /**\n     * @private\n     */\n    _onSubmitCheckContent: function () {\n        if (!this.$inputTextarea.val().trim() && !this.attachments.length) {\n            return _t('Some fields are required. Please make sure to write a message or attach a document');\n        };\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * @private\n     */\n    _updateAttachments: function () {\n        this.$attachments.empty().append(renderToElement('portal.Chatter.Attachments', {\n            attachments: this.attachments,\n            showDelete: true,\n        }));\n    },\n    /**\n     * post message using rpc call and display new message and message count\n     *\n     * @private\n     * @param {String} route\n     * @returns {Promise}\n     */\n    _chatterPostMessage: async function (route) {\n        const result = await rpc(route, this._prepareMessageData());\n        Component.env.bus.trigger('reload_chatter_content', result);\n        return result;\n    },\n});\n\nexport default {\n    PortalComposer: PortalComposer,\n};\n", "/** @odoo-module **/\n\nimport { ConfirmationDialog } from '@web/core/confirmation_dialog/confirmation_dialog';\nimport { renderToMarkup } from \"@web/core/utils/render\";\nimport publicWidget from '@web/legacy/js/public/public_widget';\nimport { InputConfirmationDialog } from './components/input_confirmation_dialog/input_confirmation_dialog';\nimport { _t } from \"@web/core/l10n/translation\";\nimport { user } from \"@web/core/user\";\n\npublicWidget.registry.NewAPIKeyButton = publicWidget.Widget.extend({\n    selector: '.o_portal_new_api_key',\n    events: {\n        click: '_onClick'\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e){\n        e.preventDefault();\n        // This call is done just so it asks for the password confirmation before starting displaying the\n        // dialog forms, to mimic the behavior from the backend, in which it asks for the password before\n        // displaying the wizard.\n        // The result of the call is unused. But it's required to call a method with the decorator `@check_identity`\n        // in order to use `handleCheckIdentity`.\n        await handleCheckIdentity(\n            this.orm.call(\"res.users\", \"api_key_wizard\", [user.userId]),\n            this.orm,\n            this.dialog\n        );\n\n        this.call(\"dialog\", \"add\", InputConfirmationDialog, {\n            title: _t(\"New API Key\"),\n            body: renderToMarkup(\"portal.keydescription\"),\n            confirmLabel: _t(\"Confirm\"),\n            confirm: async ({ inputEl }) => {\n                const description = inputEl.value;\n                const wizard_id = await this.orm.create(\"res.users.apikeys.description\", [{ name: description }]);\n                const res = await handleCheckIdentity(\n                    this.orm.call(\"res.users.apikeys.description\", \"make_key\", [wizard_id]),\n                    this.orm,\n                    this.dialog\n                );\n\n                this.call(\"dialog\", \"add\", ConfirmationDialog, {\n                    title: _t(\"API Key Ready\"),\n                    body: renderToMarkup(\"portal.keyshow\", { key: res.context.default_key }),\n                    confirmLabel: _t(\"Close\"),\n                }, {\n                    onClose: () => {\n                        window.location = window.location;\n                    },\n                })\n            }\n        });\n    }\n});\n\npublicWidget.registry.RemoveAPIKeyButton = publicWidget.Widget.extend({\n    selector: '.o_portal_remove_api_key',\n    events: {\n        click: '_onClick'\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e){\n        e.preventDefault();\n        await handleCheckIdentity(\n            this.orm.call(\"res.users.apikeys\", \"remove\", [parseInt(this.el.id)]),\n            this.orm,\n            this.dialog\n        );\n        window.location = window.location;\n    }\n});\n\npublicWidget.registry.portalSecurity = publicWidget.Widget.extend({\n    selector: '.o_portal_security_body',\n\n    /**\n     * @override\n     */\n    init: function () {\n        // Show the \"deactivate your account\" modal if needed\n        $('.modal.show#portal_deactivate_account_modal').removeClass('d-block').modal('show');\n\n        // Remove the error messages when we close the modal,\n        // so when we re-open it again we get a fresh new form\n        $('.modal#portal_deactivate_account_modal').on('hide.bs.modal', (event) => {\n            const $target = $(event.currentTarget);\n            $target.find('.alert').remove();\n            $target.find('.invalid-feedback').remove();\n            $target.find('.is-invalid').removeClass('is-invalid');\n        });\n\n        return this._super(...arguments);\n    },\n\n});\n\n/**\n * Defining what happens when you click the \"Log out from all devices\"\n * on the \"/my/security\" page.\n */\npublicWidget.registry.RevokeSessionsButton = publicWidget.Widget.extend({\n    selector: '#portal_revoke_all_sessions_popup',\n    events: {\n        click: '_onClick',\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\")\n    },\n\n    async _onClick() {\n        await handleCheckIdentity(\n            this.orm.call(\"res.users\", \"action_revoke_all_devices\", [user.userId]),\n            this.orm,\n            this.dialog\n        );\n        window.location = window.location;\n        return true;\n    },\n});\n\n/**\n * Wraps an RPC call in a check for the result being an identity check action\n * descriptor. If no such result is found, just returns the wrapped promise's\n * result as-is; otherwise shows an identity check dialog and resumes the call\n * on success.\n *\n * Warning: does not in and of itself trigger an identity check, a promise which\n * never triggers and identity check internally will do nothing of use.\n *\n * @param {Promise} wrapped promise to check for an identity check request\n * @param {Function} ormService bound do the widget\n * @param {Function} dialogService dialog service\n * @returns {Promise} result of the original call\n */\nexport async function handleCheckIdentity(wrapped, ormService, dialogService) {\n    return wrapped.then((r) => {\n        if (!(r.type && r.type === \"ir.actions.act_window\" && r.res_model === \"res.users.identitycheck\")) {\n            return r;\n        }\n        const checkId = r.res_id;\n        return new Promise((resolve) => {\n            dialogService.add(InputConfirmationDialog, {\n                title: _t(\"Security Control\"),\n                body: renderToMarkup(\"portal.identitycheck\"),\n                confirmLabel: _t(\"Confirm Password\"),\n                confirm: async ({ inputEl }) => {\n                    if (!inputEl.reportValidity()) {\n                        inputEl.classList.add(\"is-invalid\");\n                        return false;\n                    }\n                    let result;\n                    await ormService.write(\"res.users.identitycheck\", [checkId], { password: inputEl.value });\n                    try {\n                        result = await ormService.call(\"res.users.identitycheck\", \"run_check\", [checkId]);\n                    } catch {\n                        inputEl.classList.add(\"is-invalid\");\n                        inputEl.setCustomValidity(_t(\"Check failed\"));\n                        inputEl.reportValidity();\n                        return false;\n                    }\n                    resolve(result);\n                    return true;\n                },\n                cancel: () => {},\n                onInput: ({ inputEl }) => {\n                    inputEl.classList.remove(\"is-invalid\");\n                    inputEl.setCustomValidity(\"\");\n                },\n            });\n        });\n    });\n}\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { deserializeDateTime } from \"@web/core/l10n/dates\";\n\nconst { DateTime } = luxon;\n\nvar PortalSidebar = publicWidget.Widget.extend({\n    /**\n     * @override\n     */\n    start: function () {\n        this._setDelayLabel();\n        return this._super.apply(this, arguments);\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //---------------------------------------------------------------------------\n\n    /**\n     * Set the due/delay information according to the given date\n     * like : <span class=\"o_portal_sidebar_timeago\" t-att-datetime=\"invoice.date_due\"/>\n     *\n     * @private\n     */\n    _setDelayLabel: function () {\n        var $sidebarTimeago = this.$el.find('.o_portal_sidebar_timeago').toArray();\n        $sidebarTimeago.forEach((el) => {\n            var dateTime = deserializeDateTime($(el).attr('datetime')).startOf('day'),\n                today = DateTime.now().startOf('day'),\n                diff = dateTime.diff(today).as(\"days\"),\n                displayStr;\n\n                if (diff === 0) {\n                    displayStr = _t('Due today');\n                } else if (diff > 0) {\n                    // Workaround: force uniqueness of these two translations. We use %1d because the string\n                    // with %d is already used in mail and mail's translations are not sent to the frontend.\n                    displayStr = _t('Due in %s days', Math.abs(diff).toFixed());\n                } else {\n                    displayStr = _t('%s days overdue', Math.abs(diff).toFixed());\n                }\n                $(el).text(displayStr);\n        });\n    },\n    /**\n     * @private\n     * @param {string} href\n     */\n    _printIframeContent: function (href) {\n        if (!this.printContent) {\n            this.printContent = $('<iframe id=\"print_iframe_content\" src=\"' + href + '\" style=\"display:none\"></iframe>');\n            this.$el.append(this.printContent);\n            this.printContent.on('load', function () {\n                $(this).get(0).contentWindow.print();\n            });\n        } else {\n            this.printContent.get(0).contentWindow.print();\n        }\n    },\n});\nexport default PortalSidebar;\n", "/** @odoo-module */\n\nimport { useEffect } from \"@odoo/owl\";\nimport { ConfirmationDialog } from \"@web/core/confirmation_dialog/confirmation_dialog\";\n\nexport class InputConfirmationDialog extends ConfirmationDialog {\n    static props = {\n        ...ConfirmationDialog.props,\n        onInput: { type: Function, optional: true },\n    };\n    static template = \"portal.InputConfirmationDialog\";\n\n    setup() {\n        super.setup();\n\n        const onInput = () => {\n            if (this.props.onInput) {\n                this.props.onInput({ inputEl: this.inputEl });\n            }\n        };\n        const onKeydown = (ev) => {\n            if (ev.key && ev.key.toLowerCase() === \"enter\") {\n                ev.preventDefault();\n                this._confirm();\n            }\n        };\n        useEffect(\n            (inputEl) => {\n                this.inputEl = inputEl;\n                if (this.inputEl) {\n                    this.inputEl.focus();\n                    this.inputEl.addEventListener(\"keydown\", onKeydown);\n                    this.inputEl.addEventListener(\"input\", onInput);\n                    return () => {\n                        this.inputEl.removeEventListener(\"keydown\", onKeydown);\n                        this.inputEl.removeEventListener(\"input\", onInput);\n                    };\n                }\n            },\n            () => [this.modalRef.el?.querySelector(\"input\")]\n        );\n    }\n\n    _confirm() {\n        this.execButton(() => this.props.confirm({ inputEl: this.inputEl }));\n    }\n}\n", "/** @odoo-module **/\n\nimport { Component, onMounted, useRef, useState } from \"@odoo/owl\";\nimport { _t } from \"@web/core/l10n/translation\";\nimport { addLoadingEffect } from '@web/core/utils/ui';\nimport { rpc } from \"@web/core/network/rpc\";\nimport { registry } from \"@web/core/registry\";\nimport { redirect } from \"@web/core/utils/urls\";\nimport { NameAndSignature } from \"@web/core/signature/name_and_signature\";\n\n/**\n * This Component is a signature request form. It uses\n * @see NameAndSignature for the input fields, adds a submit\n * button, and handles the RPC to save the result.\n */\nclass SignatureForm extends Component {\n    static template = \"portal.SignatureForm\"\n    static components = { NameAndSignature }\n    static props = [\"*\"];\n\n    setup() {\n        this.rootRef = useRef(\"root\");\n\n        this.csrfToken = odoo.csrf_token;\n        this.state = useState({\n            error: false,\n            success: false,\n        });\n        this.signature = useState({ name: this.props.defaultName });\n        this.nameAndSignatureProps = {\n            signature: this.signature,\n            fontColor: this.props.fontColor || \"black\",\n        };\n        if (this.props.signatureRatio) {\n            this.nameAndSignatureProps.displaySignatureRatio = this.props.signatureRatio;\n        }\n        if (this.props.signatureType) {\n            this.nameAndSignatureProps.signatureType = this.props.signatureType;\n        }\n        if (this.props.mode) {\n            this.nameAndSignatureProps.mode = this.props.mode;\n        }\n\n        // Correctly set up the signature area if it is inside a modal\n        onMounted(() => {\n            this.rootRef.el.closest('.modal').addEventListener('shown.bs.modal', () => {\n                this.signature.resetSignature();\n            });\n        });\n    }\n\n    get sendLabel() {\n        return this.props.sendLabel || _t(\"Accept & Sign\");\n    }\n\n     /**\n     * Handles click on the submit button.\n     *\n     * This will get the current name and signature and validate them.\n     * If they are valid, they are sent to the server, and the reponse is\n     * handled. If they are invalid, it will display the errors to the user.\n     *\n     * @returns {Promise}\n     */\n    async onClickSubmit() {\n        const button = document.querySelector('.o_portal_sign_submit')\n        const icon = button.removeChild(button.firstChild)\n        const restoreBtnLoading = addLoadingEffect(button);\n\n        const name = this.signature.name;\n        const signature = this.signature.getSignatureImage().split(\",\")[1];\n        const data = await rpc(this.props.callUrl, { name, signature });\n        if (data.force_refresh) {\n            restoreBtnLoading();\n            button.prepend(icon)\n            if (data.redirect_url) {\n                redirect(data.redirect_url);\n            } else {\n                window.location.reload();\n            }\n            // do not resolve if we reload the page\n            return new Promise(() => {});\n        }\n        this.state.error = data.error || false;\n        this.state.success = !data.error && {\n            message: data.message,\n            redirectUrl: data.redirect_url,\n            redirectMessage: data.redirect_message,\n        };\n    }\n}\n\nregistry.category(\"public_components\").add(\"portal.signature_form\", SignatureForm);\n", "import { Deferred } from \"@web/core/utils/concurrency\";\nimport { loadBundle } from \"@web/core/assets\";\nimport { registry } from \"@web/core/registry\";\nimport { memoize } from \"@web/core/utils/functions\";\n\nodoo.portalChatterReady = new Deferred();\n\nconst loader = {\n    loadChatter: memoize(() => loadBundle(\"portal.assets_chatter\")),\n};\nexport const portalChatterBootService = {\n    start() {\n        const chatterEl = document.querySelector(\".o_portal_chatter\");\n        if (chatterEl) {\n            loader.loadChatter();\n        }\n    },\n};\nregistry.category(\"services\").add(\"portal.chatter.boot\", portalChatterBootService);\n", "/** @odoo-module **/\n\nimport { scrollTo } from \"@web/core/utils/scrolling\";\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport PortalSidebar from \"@portal/js/portal_sidebar\";\n\npublicWidget.registry.AccountPortalSidebar = PortalSidebar.extend({\n    selector: '.o_portal_invoice_sidebar',\n    events: {\n        'click .o_portal_invoice_print': '_onPrintInvoice',\n    },\n\n    /**\n     * @override\n     */\n    start: function () {\n        var def = this._super.apply(this, arguments);\n\n        var $invoiceHtml = this.$el.find('iframe#invoice_html');\n        var updateIframeSize = this._updateIframeSize.bind(this, $invoiceHtml);\n\n        $(window).on('resize', updateIframeSize);\n\n        var iframeDoc = $invoiceHtml[0].contentDocument || $invoiceHtml[0].contentWindow.document;\n        if (iframeDoc.readyState === 'complete') {\n            updateIframeSize();\n        } else {\n            $invoiceHtml.on('load', updateIframeSize);\n        }\n\n        return def;\n    },\n\n    //--------------------------------------------------------------------------\n    // Handlers\n    //--------------------------------------------------------------------------\n\n    /**\n     * Called when the iframe is loaded or the window is resized on customer portal.\n     * The goal is to expand the iframe height to display the full report without scrollbar.\n     *\n     * @private\n     * @param {object} $el: the iframe\n     */\n    _updateIframeSize: function ($el) {\n        var $wrapwrap = $el.contents().find('div#wrapwrap');\n        // Set it to 0 first to handle the case where scrollHeight is too big for its content.\n        $el.height(0);\n        $el.height($wrapwrap[0].scrollHeight);\n\n        // scroll to the right place after iframe resize\n        const isAnchor = /^#[\\w-]+$/.test(window.location.hash)\n        if (!isAnchor) {\n            return;\n        }\n        var $target = $(window.location.hash);\n        if (!$target.length) {\n            return;\n        }\n        scrollTo($target[0], { behavior: \"instant\" });\n    },\n    /**\n     * @private\n     * @param {MouseEvent} ev\n     */\n    _onPrintInvoice: function (ev) {\n        ev.preventDefault();\n        var href = $(ev.currentTarget).attr('href');\n        this._printIframeContent(href);\n    },\n});\n", "/** @odoo-module */\n\nimport { PortalHomeCounters } from '@portal/js/portal';\n\nPortalHomeCounters.include({\n    /**\n     * @override\n     */\n    _getCountersAlwaysDisplayed() {\n        return this._super(...arguments).concat(['invoice_count']);\n    },\n});\n", "/** @odoo-module **/\nimport { rpc } from \"@web/core/network/rpc\";\nimport { registry } from \"@web/core/registry\";\n\nimport { accountTaxHelpers } from \"@account/helpers/account_tax\";\n\nimport { xml, useState, Component } from \"@odoo/owl\";\n\nexport class TestsSharedJsPython extends Component {\n    static template = xml`\n        <button t-attf-class=\"#{state.done ? 'text-success' : ''}\" t-on-click=\"processTests\">Test</button>\n    `;\n    static props = {\n        tests: { type: Array, optional: true },\n    };\n\n    setup() {\n        super.setup();\n        this.state = useState({ done: false });\n    }\n\n    processTest(params) {\n        if (params.test === \"taxes_computation\") {\n            const kwargs = {\n                product: params.product,\n                precision_rounding: params.precision_rounding,\n                rounding_method: params.rounding_method,\n            };\n            const results = {\n                results: accountTaxHelpers.get_tax_details(\n                    params.taxes,\n                    params.price_unit,\n                    params.quantity,\n                    kwargs,\n                )\n            };\n            if (params.rounding_method === \"round_globally\") {\n                results.total_excluded_results = accountTaxHelpers.get_tax_details(\n                    params.taxes,\n                    results.results.total_excluded / params.quantity,\n                    params.quantity,\n                    {...kwargs, special_mode: \"total_excluded\"}\n                );\n                results.total_included_results = accountTaxHelpers.get_tax_details(\n                    params.taxes,\n                    results.results.total_included / params.quantity,\n                    params.quantity,\n                    {...kwargs, special_mode: \"total_included\"}\n                );\n            }\n            return results;\n        }\n        if (params.test === \"adapt_price_unit_to_another_taxes\") {\n            return {\n                price_unit: accountTaxHelpers.adapt_price_unit_to_another_taxes(\n                    params.price_unit,\n                    params.product,\n                    params.original_taxes,\n                    params.new_taxes\n                )\n            }\n        }\n        if (params.test === \"tax_totals_summary\") {\n            const document = this.populateDocument(params.document);\n            const taxTotals = accountTaxHelpers.get_tax_totals_summary(\n                document.lines,\n                document.currency,\n                document.company,\n                {cash_rounding: document.cash_rounding}\n            );\n            return {tax_totals: taxTotals};\n        }\n        if (params.test === \"tax_total\") {\n            const document = this.populateDocument(params.document);\n            const taxTotals = accountTaxHelpers.get_tax_totals_summary(\n                document.lines,\n                document.currency,\n                document.company,\n                {cash_rounding: document.cash_rounding}\n            );\n            return {tax_amount_currency: taxTotals.tax_amount_currency};\n        }\n    }\n\n    async processTests() {\n        const tests = this.props.tests || [];\n        const results = tests.map(this.processTest.bind(this));\n        await rpc(\"/account/post_tests_shared_js_python\", { results: results });\n        this.state.done = true;\n    }\n\n    populateDocument(document) {\n        const base_lines = document.lines.map(line => accountTaxHelpers.prepare_base_line_for_taxes_computation(null, line));\n        accountTaxHelpers.add_tax_details_in_base_lines(base_lines, document.company);\n        accountTaxHelpers.round_base_lines_tax_details(base_lines, document.company);\n        return {\n            ...document,\n            lines: base_lines,\n        }\n    }\n}\n\nregistry.category(\"public_components\").add(\"account.tests_shared_js_python\", TestsSharedJsPython);\n", "import { floatIsZero, roundPrecision } from \"@web/core/utils/numbers\";\nimport { _t } from \"@web/core/l10n/translation\";\n\nexport const accountTaxHelpers = {\n    // -------------------------------------------------------------------------\n    // HELPERS IN BOTH PYTHON/JAVASCRIPT (account_tax.js / account_tax.py)\n\n    // PREPARE TAXES COMPUTATION\n    // -------------------------------------------------------------------------\n\n    /**\n     * [!] Mirror of the same method in account_tax.py.\n     * PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.\n     */\n    eval_taxes_computation_prepare_product_values(default_product_values, product) {\n        const product_values = {};\n        for (const [field_name, field_info] of Object.entries(default_product_values)) {\n            product_values[field_name] = product\n                ? product[field_name] || field_info.default_value\n                : field_info.default_value;\n        }\n        return product_values;\n    },\n\n    /**\n     * [!] Mirror of the same method in account_tax.py.\n     * PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.\n     */\n    batch_for_taxes_computation(taxes, { special_mode = false } = {}) {\n        function sort_key(taxes) {\n            return taxes.sort((t1, t2) => t1.sequence - t2.sequence || t1.id - t2.id);\n        }\n\n        const results = {\n            batch_per_tax: {},\n            group_per_tax: {},\n            sorted_taxes: [],\n        };\n\n        // Flatten the taxes.\n        for (const tax of sort_key(taxes)) {\n            if (tax.amount_type === \"group\") {\n                const children = sort_key(tax.children_tax_ids);\n                for (const child of children) {\n                    results.group_per_tax[child.id] = tax;\n                    results.sorted_taxes.push(child);\n                }\n            } else {\n                results.sorted_taxes.push(tax);\n            }\n        }\n\n        // Group them per batch.\n        let batch = [];\n        let is_base_affected = false;\n        for (const tax of results.sorted_taxes.toReversed()) {\n            if (batch.length > 0) {\n                const same_batch =\n                    tax.amount_type === batch[0].amount_type &&\n                    (special_mode || tax.price_include === batch[0].price_include) &&\n                    tax.include_base_amount === batch[0].include_base_amount &&\n                    ((tax.include_base_amount && !is_base_affected) || !tax.include_base_amount);\n                if (!same_batch) {\n                    for (const batch_tax of batch) {\n                        results.batch_per_tax[batch_tax.id] = batch;\n                    }\n                    batch = [];\n                }\n            }\n\n            is_base_affected = tax.is_base_affected;\n            batch.push(tax);\n        }\n\n        if (batch.length !== 0) {\n            for (const batch_tax of batch) {\n                results.batch_per_tax[batch_tax.id] = batch;\n            }\n        }\n        return results;\n    },\n\n    propagate_extra_taxes_base(taxes, tax, taxes_data, { special_mode = false } = {}) {\n        function* get_tax_before() {\n            for (const tax_before of taxes) {\n                if (taxes_data[tax.id].batch.includes(tax_before)) {\n                    break;\n                }\n                yield tax_before;\n            }\n        }\n\n        function* get_tax_after() {\n            for (const tax_after of taxes.toReversed()) {\n                if (taxes_data[tax.id].batch.includes(tax_after)) {\n                    break;\n                }\n                yield tax_after;\n            }\n        }\n\n        function add_extra_base(other_tax, sign) {\n            const tax_amount = taxes_data[tax.id].tax_amount;\n            if (!(\"tax_amount\" in taxes_data[other_tax.id])) {\n                taxes_data[other_tax.id].extra_base_for_tax += sign * tax_amount;\n            }\n            taxes_data[other_tax.id].extra_base_for_base += sign * tax_amount;\n        }\n\n        if (tax.price_include) {\n            // Case: special mode is False or 'total_included'\n            if (!special_mode || special_mode === \"total_included\") {\n                if (!tax.include_base_amount) {\n                    for (const other_tax of get_tax_after()) {\n                        if (other_tax.price_include) {\n                            add_extra_base(other_tax, -1)\n                        }\n                    }\n                }\n                for (const other_tax of get_tax_before()) {\n                    add_extra_base(other_tax, -1);\n                }\n\n            // Case: special_mode = 'total_excluded'\n            } else {\n                for (const other_tax of get_tax_after()) {\n                    if (!other_tax.price_include || tax.include_base_amount) {\n                        add_extra_base(other_tax, 1);\n                    }\n                }\n            }\n\n        } else if (!tax.price_include) {\n            // Case: special_mode is False or 'total_excluded'\n            if (!special_mode || special_mode === \"total_excluded\") {\n                if (tax.include_base_amount) {\n                    for (const other_tax of get_tax_after()) {\n                        add_extra_base(other_tax, 1);\n                    }\n                }\n\n            // Case: special_mode = 'total_included'\n            } else {\n                if (!tax.include_base_amount) {\n                    for (const other_tax of get_tax_after()) {\n                        add_extra_base(other_tax, -1);\n                    }\n                }\n                for (const other_tax of get_tax_before()) {\n                    add_extra_base(other_tax, -1);\n                }\n            }\n        }\n    },\n\n    /**\n     * [!] Mirror of the same method in account_tax.py.\n     * PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.\n     */\n    eval_tax_amount_fixed_amount(tax, batch, raw_base, evaluation_context) {\n        if (tax.amount_type === \"fixed\") {\n            return evaluation_context.quantity * tax.amount;\n        }\n        return null;\n    },\n\n    /**\n     * [!] Mirror of the same method in account_tax.py.\n     * PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.\n     */\n    eval_tax_amount_price_included(tax, batch, raw_base, evaluation_context) {\n        if (tax.amount_type === \"percent\") {\n            const total_percentage =\n                batch.reduce(\n                    (sum, batch_tax) => sum + batch_tax.amount,\n                    0\n                ) / 100.0;\n            const to_price_excluded_factor =\n                total_percentage !== -1 ? 1 / (1 + total_percentage) : 0.0;\n            return (raw_base * to_price_excluded_factor * tax.amount) / 100.0;\n        }\n\n        if (tax.amount_type === \"division\") {\n            return (raw_base * tax.amount) / 100.0;\n        }\n        return null;\n    },\n\n    /**\n     * [!] Mirror of the same method in account_tax.py.\n     * PLZ KEEP BOTH METHODS CONSISTENT WITH EACH OTHERS.\n     */\n    eval_tax_amount_price_excluded(tax, batch, raw_base, evaluation_context) {\n        if (tax.amount_type === \"percent\") {\n            return (raw_base * tax.amount) / 100.0;\n        }\n\n        if (tax.amount_type === \"division\") {\n            const total_percentage =\n                batch.reduce(\n                    (sum, batch_tax) => sum + batch_tax.amount,\n                    0\n                ) / 100.0;\n            const incl_base_multiplicator = total_percentage === 1.0 ? 1.0 : 1 - total_percentage;\n            return (raw_base * tax.amount) / 100.0 / incl_base_multiplicator;\n        }\n        return null;\n    },\n\n    eval_raw_base(quantity, price_unit, evaluation_context) {\n        return quantity * price_unit;\n    },\n\n    get_tax_details(\n        taxes,\n        price_unit,\n        quantity,\n        {\n            precision_rounding = null,\n            rounding_method = \"round_per_line\",\n            // When product is null, we need the product default values to make the \"formula\" taxes\n            // working. In that case, we need to deal with the product default values before calling this\n            // method because we have no way to deal with it automatically in this method since it depends of\n            // the type of involved fields and we don't have access to this information js-side.\n            product = null,\n            special_mode = false,\n        } = {}\n    ) {\n        const self = this;\n\n        function add_tax_amount_to_results(tax, tax_amount) {\n            taxes_data[tax.id].tax_amount = tax_amount;\n            if (rounding_method === \"round_per_line\") {\n                taxes_data[tax.id].tax_amount = roundPrecision(\n                    taxes_data[tax.id].tax_amount,\n                    precision_rounding\n                );\n            }\n            if (tax.has_negative_factor){\n                reverse_charge_taxes_data[tax.id].tax_amount = -taxes_data[tax.id].tax_amount;\n            }\n\n            self.propagate_extra_taxes_base(sorted_taxes, tax, taxes_data, {\n                special_mode: special_mode,\n            });\n        }\n\n        function eval_tax_amount(tax_amount_function, tax) {\n            const is_already_computed = \"tax_amount\" in taxes_data[tax.id];\n            if (is_already_computed) {\n                return;\n            }\n\n            const tax_amount = tax_amount_function(\n                tax,\n                taxes_data[tax.id].batch,\n                raw_base + taxes_data[tax.id].extra_base_for_tax,\n                evaluation_context\n            );\n            if (tax_amount !== null) {\n                add_tax_amount_to_results(tax, tax_amount);\n            }\n        }\n\n        // Flatten the taxes and order them.\n\n        function prepare_tax_extra_data(tax, kwargs = {}) {\n            let price_include;\n            if (special_mode === \"total_included\") {\n                price_include = true;\n            } else if (special_mode === \"total_excluded\") {\n                price_include = false;\n            } else {\n                price_include = tax.price_include;\n            }\n            return {\n                ...kwargs,\n                tax: tax,\n                price_include: price_include,\n                extra_base_for_tax: 0.0,\n                extra_base_for_base: 0.0,\n            };\n        }\n\n        const batching_results = this.batch_for_taxes_computation(taxes, {\n            special_mode: special_mode,\n        });\n        const sorted_taxes = batching_results.sorted_taxes;\n        const taxes_data = {};\n        const reverse_charge_taxes_data = {};\n        for (const tax of sorted_taxes) {\n            taxes_data[tax.id] = prepare_tax_extra_data(tax, {\n                group: batching_results.group_per_tax[tax.id],\n                batch: batching_results.batch_per_tax[tax.id],\n            });\n            if (tax.has_negative_factor) {\n                reverse_charge_taxes_data[tax.id] = {\n                    ...taxes_data[tax.id],\n                    is_reverse_charge: true,\n                }\n            }\n        }\n\n        const raw_base_evaluation_context = {\n            taxes: sorted_taxes,\n            precision_rounding: precision_rounding,\n        };\n        let raw_base = this.eval_raw_base(quantity, price_unit, raw_base_evaluation_context);\n        if (rounding_method === \"round_per_line\") {\n            raw_base = roundPrecision(raw_base, precision_rounding);\n        }\n\n        let evaluation_context = {\n            product: product || {},\n            price_unit: price_unit,\n            quantity: quantity,\n            raw_base: raw_base,\n            special_mode: special_mode,\n            precision_rounding: precision_rounding,\n        };\n\n        // Define the order in which the taxes must be evaluated.\n        // Fixed taxes are computed directly because they could affect the base of a price included batch right after.\n        for (const tax of sorted_taxes.toReversed()) {\n            eval_tax_amount(this.eval_tax_amount_fixed_amount.bind(this), tax);\n        }\n\n        // Then, let's travel the batches in the reverse order and process the price-included taxes.\n        for (const tax of sorted_taxes.toReversed()) {\n            if (taxes_data[tax.id].price_include) {\n                eval_tax_amount(this.eval_tax_amount_price_included.bind(this), tax);\n            }\n        }\n\n        // Then, let's travel the batches in the normal order and process the price-excluded taxes.\n        for (const tax of sorted_taxes) {\n            if (!taxes_data[tax.id].price_include) {\n                eval_tax_amount(this.eval_tax_amount_price_excluded.bind(this), tax);\n            }\n        }\n\n        // Mark the base to be computed in the descending order. The order doesn't matter for no special mode or 'total_excluded' but\n        // it must be in the reverse order when special_mode is 'total_included'.\n        for (const tax of sorted_taxes.toReversed()) {\n            if (!(\"tax_amount\" in taxes_data[tax.id])) {\n                continue;\n            }\n\n            const total_tax_amount = taxes_data[tax.id].batch.reduce(\n                (sum, other_tax) => sum + taxes_data[other_tax.id].tax_amount,\n                0\n            );\n            let base = raw_base + taxes_data[tax.id].extra_base_for_base;\n            if (\n                taxes_data[tax.id].price_include &&\n                (!special_mode || special_mode === \"total_included\")\n            ) {\n                base -= total_tax_amount;\n            }\n            taxes_data[tax.id].base = base;\n            if(tax.has_negative_factor){\n                reverse_charge_taxes_data[tax.id].base = base;\n            }\n        }\n\n        const taxes_data_list = [];\n        for (const tax of sorted_taxes) {\n            const tax_data = taxes_data[tax.id];\n            if (\"tax_amount\" in tax_data){\n                taxes_data_list.push(tax_data);\n                if (tax.has_negative_factor) {\n                    taxes_data_list.push(reverse_charge_taxes_data[tax.id]);\n                }\n            }\n        }\n\n        let total_excluded, total_included;\n        if (taxes_data_list.length > 0) {\n            total_excluded = taxes_data_list[0].base;\n            const tax_amount = taxes_data_list.reduce(\n                (sum, tax_data) => sum + tax_data.tax_amount,\n                0\n            );\n            total_included = total_excluded + tax_amount;\n        } else {\n            total_excluded = total_included = raw_base;\n        }\n\n        return {\n            total_excluded: total_excluded,\n            total_included: total_included,\n            taxes_data: taxes_data_list.map(tax_data => Object.assign({}, {\n                tax: tax_data.tax,\n                group: batching_results.group_per_tax[tax_data.tax.id],\n                batch: batching_results.batch_per_tax[tax_data.tax.id],\n                tax_amount: tax_data.tax_amount,\n                base_amount: tax_data.base,\n                is_reverse_charge: tax_data.is_reverse_charge || false\n            })),\n        };\n    },\n\n    // -------------------------------------------------------------------------\n    // MAPPING PRICE_UNIT\n    // -------------------------------------------------------------------------\n\n    adapt_price_unit_to_another_taxes(price_unit, product, original_taxes, new_taxes) {\n        const original_tax_ids = new Set(original_taxes.map((x) => x.id));\n        const new_tax_ids = new Set(new_taxes.map((x) => x.id));\n        if (\n            (original_tax_ids.size === new_tax_ids.size &&\n                [...original_tax_ids].every((value) => new_tax_ids.has(value))) ||\n            original_taxes.some((x) => !x.price_include)\n        ) {\n            return price_unit;\n        }\n\n        // Find the price unit without tax.\n        let taxes_computation = this.get_tax_details(original_taxes, price_unit, 1.0, {\n            rounding_method: \"round_globally\",\n            product: product,\n        });\n        price_unit = taxes_computation.total_excluded;\n\n        // Find the new price unit after applying the price included taxes.\n        taxes_computation = this.get_tax_details(new_taxes, price_unit, 1.0, {\n            rounding_method: \"round_globally\",\n            product: product,\n            special_mode: \"total_excluded\",\n        });\n        let delta = 0.0;\n        for (const tax_data of taxes_computation.taxes_data) {\n            if (tax_data.tax.price_include) {\n                delta += tax_data.tax_amount;\n            }\n        }\n        return price_unit + delta;\n    },\n\n    // -------------------------------------------------------------------------\n    // GENERIC REPRESENTATION OF BUSINESS OBJECTS & METHODS\n    // -------------------------------------------------------------------------\n\n    get_base_line_field_value_from_record(record, field, extra_values, fallback) {\n        if (field in extra_values) {\n            return extra_values[field] || fallback;\n        }\n        if (field in record) {\n            return record[field] || fallback;\n        }\n        return fallback;\n    },\n\n    prepare_base_line_for_taxes_computation(record, kwargs = {}){\n        const load = (field, fallback) => this.get_base_line_field_value_from_record(record, field, kwargs, fallback);\n\n        const currency = (\n            load('currency_id', null)\n            || load('company_currency_id', null)\n            || load('company_id', {}).currency_id\n            || {}\n        )\n\n        return {\n            ...kwargs,\n            record: record,\n            id: load('id', 0),\n            product_id: load('product_id', {}),\n            tax_ids: load('tax_ids', {}),\n            price_unit: load('price_unit', 0.0),\n            quantity: load('quantity', 0.0),\n            discount: load('discount', 0.0),\n            currency_id: currency,\n            sign: load('sign', 1.0),\n            special_mode: kwargs.special_mode || false,\n            special_type: kwargs.special_type || false,\n        }\n    },\n\n    add_tax_details_in_base_line(base_line, company) {\n        const price_unit_after_discount = base_line.price_unit * (1 - (base_line.discount / 100.0));\n        const currency_pd = base_line.currency_id.rounding;\n        const company_currency_pd = company.currency_id.rounding;\n        const taxes_computation = this.get_tax_details(\n            base_line.tax_ids,\n            price_unit_after_discount,\n            base_line.quantity,\n            {\n                precision_rounding: currency_pd,\n                rounding_method: company.tax_calculation_rounding_method,\n                product: base_line.product_id,\n                special_mode: base_line.special_mode\n            }\n        );\n\n        const rate = base_line.rate;\n        const tax_details = base_line.tax_details = {\n            raw_total_excluded_currency: taxes_computation.total_excluded,\n            raw_total_excluded: rate ? taxes_computation.total_excluded / rate : 0.0,\n            raw_total_included_currency: taxes_computation.total_included,\n            raw_total_included: rate ? taxes_computation.total_included / rate : 0.0,\n            taxes_data: []\n        };\n\n        if (company.tax_calculation_rounding_method === 'round_per_line') {\n            tax_details.raw_total_excluded = roundPrecision(tax_details.raw_total_excluded, currency_pd);\n            tax_details.raw_total_included = roundPrecision(tax_details.raw_total_included, currency_pd);\n        }\n\n        for (const tax_data of taxes_computation.taxes_data) {\n            let tax_amount = rate ? tax_data.tax_amount / rate : 0.0;\n            let base_amount = rate ? tax_data.base_amount / rate : 0.0;\n\n            if (company.tax_calculation_rounding_method === 'round_per_line') {\n                tax_amount = roundPrecision(tax_amount, company_currency_pd);\n                base_amount = roundPrecision(base_amount, company_currency_pd);\n            }\n\n            tax_details.taxes_data.push({\n                ...tax_data,\n                raw_tax_amount_currency: tax_data.tax_amount,\n                raw_tax_amount: tax_amount,\n                raw_base_amount_currency: tax_data.base_amount,\n                raw_base_amount: base_amount\n            });\n        }\n    },\n\n    add_tax_details_in_base_lines(base_lines, company) {\n        for(const base_line of base_lines){\n            this.add_tax_details_in_base_line(base_line, company);\n        }\n    },\n\n    round_base_lines_tax_details(base_lines, company) {\n        const company_pd = company.currency_id.rounding;\n        const total_per_tax = {};\n\n        for (const base_line of base_lines) {\n            const currency = base_line.currency_id;\n            const currency_pd = currency.rounding;\n            const tax_details = base_line.tax_details;\n            tax_details.delta_base_amount_currency = 0.0;\n            tax_details.delta_base_amount = 0.0;\n            tax_details.total_excluded_currency = roundPrecision(\n                tax_details.raw_total_excluded_currency,\n                currency_pd\n            );\n            tax_details.total_excluded = roundPrecision(tax_details.raw_total_excluded, company_pd);\n            tax_details.total_included_currency = roundPrecision(\n                tax_details.raw_total_included_currency,\n                currency_pd\n            );\n            tax_details.total_included = roundPrecision(tax_details.raw_total_included, company_pd);\n\n            for (const tax_data of tax_details.taxes_data) {\n                const tax = tax_data.tax;\n\n                tax_data.tax_amount_currency = roundPrecision(\n                    tax_data.raw_tax_amount_currency,\n                    currency_pd\n                );\n                tax_data.tax_amount = roundPrecision(tax_data.raw_tax_amount, company_pd);\n                tax_data.base_amount_currency = roundPrecision(\n                    tax_data.raw_base_amount_currency,\n                    currency_pd\n                );\n                tax_data.base_amount = roundPrecision(tax_data.raw_base_amount, company_pd);\n\n                const rounding_key = [tax.id, currency.id, base_line.is_refund, tax_data.is_reverse_charge];\n                if (!(rounding_key in total_per_tax)) {\n                    total_per_tax[rounding_key] = {\n                        tax: tax,\n                        is_reverse_charge: tax_data.is_reverse_charge,\n                        currency_pd: currency.rounding,\n                        company_currency_pd: company.currency_id.rounding,\n                        base_amount_currency: 0.0,\n                        base_amount: 0.0,\n                        raw_base_amount_currency: 0.0,\n                        raw_base_amount: 0.0,\n                        tax_amount_currency: 0.0,\n                        tax_amount: 0.0,\n                        raw_tax_amount_currency: 0.0,\n                        raw_tax_amount: 0.0,\n                        base_lines: [],\n                    };\n                }\n\n                const amounts = total_per_tax[rounding_key];\n                amounts.tax_amount_currency += tax_data.tax_amount_currency;\n                amounts.raw_tax_amount_currency += tax_data.raw_tax_amount_currency;\n                amounts.tax_amount += tax_data.tax_amount;\n                amounts.raw_tax_amount += tax_data.raw_tax_amount;\n                amounts.base_amount_currency += tax_data.base_amount_currency;\n                amounts.raw_base_amount_currency += tax_data.raw_base_amount_currency;\n                amounts.base_amount += tax_data.base_amount;\n                amounts.raw_base_amount += tax_data.raw_base_amount;\n                if (!base_line.special_type) {\n                    amounts.base_lines.push(base_line);\n                }\n            }\n        }\n\n        // Round 'total_per_tax'.\n        for (const amounts of Object.values(total_per_tax)) {\n            amounts.raw_tax_amount_currency = roundPrecision(\n                amounts.raw_tax_amount_currency,\n                amounts.currency_pd\n            );\n            amounts.raw_tax_amount = roundPrecision(\n                amounts.raw_tax_amount,\n                amounts.company_currency_pd\n            );\n            amounts.raw_base_amount_currency = roundPrecision(\n                amounts.raw_base_amount_currency,\n                amounts.currency_pd\n            );\n            amounts.raw_base_amount = roundPrecision(\n                amounts.raw_base_amount,\n                amounts.company_currency_pd\n            );\n        }\n\n        // Dispatch the delta across the base lines.\n        for (const amounts of Object.values(total_per_tax)) {\n            if (!amounts.base_lines.length) {\n                continue;\n            }\n\n            const base_line = amounts.base_lines.sort(\n                (a, b) =>\n                    a.tax_details.total_included_currency - b.tax_details.total_included_currency\n            )[0];\n\n            const tax_details = base_line.tax_details;\n            const [index, tax_data] = tax_details.taxes_data\n                .map((x, i) => [i, x])\n                .find(\n                    ([i, x]) =>\n                        x.tax.id === amounts.tax.id &&\n                        x.is_reverse_charge === amounts.is_reverse_charge\n                );\n\n            const delta_base_amount_currency =\n                amounts.raw_base_amount_currency - amounts.base_amount_currency;\n            const delta_base_amount = amounts.raw_base_amount - amounts.base_amount;\n\n            if (index === 0) {\n                tax_details.delta_base_amount_currency += delta_base_amount_currency;\n                tax_details.delta_base_amount += delta_base_amount;\n            }\n\n            tax_data.base_amount_currency += delta_base_amount_currency;\n            tax_data.base_amount += delta_base_amount;\n            tax_data.tax_amount_currency +=\n                amounts.raw_tax_amount_currency - amounts.tax_amount_currency;\n            tax_data.tax_amount += amounts.raw_tax_amount - amounts.tax_amount;\n        }\n    },\n\n    // -------------------------------------------------------------------------\n    // TAX TOTALS SUMMARY\n    // -------------------------------------------------------------------------\n\n    get_tax_totals_summary(base_lines, currency, company, {cash_rounding = null} = {}) {\n        const company_pd = company.currency_id.rounding;\n        const tax_totals_summary = {\n            currency_id: currency.id,\n            currency_pd: currency.rounding,\n            company_currency_id: company.currency_id.id,\n            company_currency_pd: company.currency_id.rounding,\n            has_tax_groups: false,\n            subtotals: [],\n            base_amount_currency: 0.0,\n            base_amount: 0.0,\n            tax_amount_currency: 0.0,\n            tax_amount: 0.0,\n        };\n\n        // Global tax values.\n        const global_grouping_function = (base_line, tax_data) => true;\n\n        let base_lines_aggregated_values = this.aggregate_base_lines_tax_details(base_lines, global_grouping_function);\n        let values_per_grouping_key = this.aggregate_base_lines_aggregated_values(base_lines_aggregated_values);\n\n        for (const values of Object.values(values_per_grouping_key)) {\n            if (values.grouping_key) {\n                tax_totals_summary.has_tax_groups = true;\n            }\n            for (const key of ['base_amount_currency', 'base_amount', 'tax_amount_currency', 'tax_amount']) {\n                tax_totals_summary[key] += values[key];\n            }\n        }\n\n        // Subtotals.\n        const untaxed_amount_subtotal_label = _t(\"Untaxed Amount\");\n        const subtotals = {};\n\n        const subtotal_grouping_function = (base_line, tax_data) =>\n            tax_data.tax.tax_group_id.preceding_subtotal || untaxed_amount_subtotal_label;\n\n        base_lines_aggregated_values = this.aggregate_base_lines_tax_details(base_lines, subtotal_grouping_function);\n        values_per_grouping_key = this.aggregate_base_lines_aggregated_values(base_lines_aggregated_values);\n\n        for (const values of Object.values(values_per_grouping_key)) {\n            const subtotal_label = values.grouping_key || untaxed_amount_subtotal_label;\n            if (!(subtotal_label in subtotals)) {\n                subtotals[subtotal_label] = {\n                    tax_groups: [],\n                    tax_amount_currency: 0.0,\n                    tax_amount: 0.0,\n                    base_amount_currency: 0.0,\n                    base_amount: 0.0,\n                };\n            }\n            const subtotal = subtotals[subtotal_label];\n            for (const key of ['base_amount_currency', 'base_amount', 'tax_amount_currency', 'tax_amount']) {\n                subtotal[key] += values[key];\n            }\n        }\n\n        // Tax groups.\n        const tax_group_grouping_function = (base_line, tax_data) => {\n            return {\n                grouping_key: tax_data.tax.tax_group_id.id,\n                raw_grouping_key: tax_data.tax.tax_group_id,\n            };\n        }\n\n        base_lines_aggregated_values = this.aggregate_base_lines_tax_details(base_lines, tax_group_grouping_function);\n        values_per_grouping_key = this.aggregate_base_lines_aggregated_values(base_lines_aggregated_values);\n\n        const sorted_total_per_tax_group = Object.values(values_per_grouping_key)\n            .filter(values => values.grouping_key)\n            .sort((a, b) => (a.grouping_key.sequence - b.grouping_key.sequence) || (a.grouping_key.id - b.grouping_key.id));\n\n        const encountered_base_amounts = new Set();\n        const subtotals_order = {};\n\n        for (const [order, values] of sorted_total_per_tax_group.entries()) {\n            const tax_group = values.grouping_key;\n\n            // Get all involved taxes in the tax group.\n            const involved_tax_ids = new Set();\n            const involved_amount_types = new Set();\n            const involved_price_include = new Set();\n            values.base_line_x_taxes_data.forEach(([base_line, taxes_data]) => {\n                taxes_data.forEach(tax_data => {\n                    const tax = tax_data.tax;\n                    involved_tax_ids.add(tax.id);\n                    involved_amount_types.add(tax.amount_type);\n                    involved_price_include.add(tax.price_include);\n                });\n            });\n\n            // Compute the display base amounts.\n            let display_base_amount = values.base_amount;\n            let display_base_amount_currency = values.base_amount_currency;\n            if (involved_amount_types.size === 1 && involved_amount_types.has(\"fixed\")) {\n                display_base_amount = null;\n                display_base_amount_currency = null;\n            } else if (\n                involved_amount_types.size === 1\n                && involved_amount_types.has(\"division\")\n                && involved_price_include.size === 1\n                && involved_price_include.has(true)\n            ) {\n                values.base_line_x_taxes_data.forEach(([base_line, _taxes_data]) => {\n                    base_line.tax_details.taxes_data.forEach(tax_data => {\n                        if (tax_data.tax.amount_type === 'division') {\n                            display_base_amount_currency += tax_data.tax_amount_currency;\n                            display_base_amount += tax_data.tax_amount;\n                        }\n                    });\n                });\n            }\n\n            if (display_base_amount_currency !== null) {\n                encountered_base_amounts.add(parseFloat(display_base_amount_currency.toFixed(currency.decimal_places)));\n            }\n\n            // Order of the subtotals.\n            const preceding_subtotal = tax_group.preceding_subtotal || untaxed_amount_subtotal_label;\n            if (!(preceding_subtotal in subtotals_order)) {\n                subtotals_order[preceding_subtotal] = order;\n            }\n\n            subtotals[preceding_subtotal].tax_groups.push({\n                id: tax_group.id,\n                involved_tax_ids: Array.from(involved_tax_ids),\n                tax_amount_currency: values.tax_amount_currency,\n                tax_amount: values.tax_amount,\n                base_amount_currency: values.base_amount_currency,\n                base_amount: values.base_amount,\n                display_base_amount_currency,\n                display_base_amount,\n                group_name: tax_group.name,\n                group_label: tax_group.pos_receipt_label,\n            });\n        }\n\n        // Cash rounding\n        const cash_rounding_lines = base_lines.filter(base_line => base_line.special_type === 'cash_rounding');\n        if (cash_rounding_lines.length) {\n            tax_totals_summary.cash_rounding_base_amount_currency = 0.0;\n            tax_totals_summary.cash_rounding_base_amount = 0.0;\n            cash_rounding_lines.forEach(base_line => {\n                const tax_details = base_line.tax_details;\n                tax_totals_summary.cash_rounding_base_amount_currency += tax_details.total_excluded_currency;\n                tax_totals_summary.cash_rounding_base_amount += tax_details.total_excluded;\n            });\n        } else if (cash_rounding !== null) {\n            const strategy = cash_rounding.strategy;\n            const cash_rounding_pd = cash_rounding.rounding;\n            const cash_rounding_method = cash_rounding.rounding_method;\n            const total_amount_currency = tax_totals_summary.base_amount_currency + tax_totals_summary.tax_amount_currency;\n            const total_amount = tax_totals_summary.base_amount + tax_totals_summary.tax_amount;\n            const expected_total_amount_currency = roundPrecision(total_amount_currency, cash_rounding_pd, cash_rounding_method);\n            const cash_rounding_base_amount_currency = expected_total_amount_currency - total_amount_currency;\n            if (!floatIsZero(cash_rounding_base_amount_currency, currency.decimal_places)) {\n                const rate = total_amount ? Math.abs(total_amount_currency / total_amount) : 0.0;\n                const cash_rounding_base_amount = rate ? roundPrecision(cash_rounding_base_amount_currency / rate, company_pd) : 0.0;\n                if (strategy === 'add_invoice_line') {\n                    tax_totals_summary.cash_rounding_base_amount_currency = cash_rounding_base_amount_currency;\n                    tax_totals_summary.cash_rounding_base_amount = cash_rounding_base_amount;\n                    tax_totals_summary.base_amount_currency += cash_rounding_base_amount_currency;\n                    tax_totals_summary.base_amount += cash_rounding_base_amount;\n                    subtotals[untaxed_amount_subtotal_label].base_amount_currency += cash_rounding_base_amount_currency;\n                    subtotals[untaxed_amount_subtotal_label].base_amount += cash_rounding_base_amount;\n                } else if (strategy === 'biggest_tax') {\n                    const [max_subtotal, max_tax_group] = Array.from(Object.values(subtotals))\n                        .flatMap(subtotal => subtotal.tax_groups.map(tax_group => [subtotal, tax_group]))\n                        .reduce((a, b) => (b[1].tax_amount_currency > a[1].tax_amount_currency ? b : a));\n\n                    max_tax_group.tax_amount_currency += cash_rounding_base_amount_currency;\n                    max_tax_group.tax_amount += cash_rounding_base_amount;\n                    max_subtotal.tax_amount_currency += cash_rounding_base_amount_currency;\n                    max_subtotal.tax_amount += cash_rounding_base_amount;\n                    tax_totals_summary.tax_amount_currency += cash_rounding_base_amount_currency;\n                    tax_totals_summary.tax_amount += cash_rounding_base_amount;\n                }\n            }\n        }\n\n        // Misc.\n        const ordered_subtotals = Array.from(Object.entries(subtotals))\n            .sort((a, b) => (subtotals_order[a[0]] || 0) - (subtotals_order[b[0]] || 0));\n        ordered_subtotals.forEach(([subtotal_label, subtotal]) => {\n            subtotal.name = subtotal_label;\n            tax_totals_summary.subtotals.push(subtotal);\n        });\n\n        tax_totals_summary.same_tax_base = encountered_base_amounts.size === 1;\n\n        // Total amount.\n        tax_totals_summary.total_amount_currency = tax_totals_summary.base_amount_currency + tax_totals_summary.tax_amount_currency;\n        tax_totals_summary.total_amount = tax_totals_summary.base_amount + tax_totals_summary.tax_amount;\n\n        return tax_totals_summary;\n    },\n\n    // -------------------------------------------------------------------------\n    // EDI HELPERS\n    // -------------------------------------------------------------------------\n\n    aggregate_base_line_tax_details(base_line, grouping_function) {\n        const values_per_grouping_key = {};\n        const tax_details = base_line.tax_details;\n        const taxes_data = tax_details.taxes_data;\n\n        for (const tax_data of taxes_data) {\n            const generated_grouping_key = grouping_function(base_line, tax_data);\n            let raw_grouping_key = generated_grouping_key;\n            let grouping_key = generated_grouping_key;\n\n            // There is no FrozenDict in javascript.\n            // When the key is a record, it can't be jsonified so this is a trick to provide both the\n            // raw_grouping_key (to be jsonified) from the grouping_key (to be added to the values).\n            if (typeof raw_grouping_key === 'object' && (\"raw_grouping_key\" in raw_grouping_key)) {\n                raw_grouping_key = generated_grouping_key.raw_grouping_key;\n                grouping_key = generated_grouping_key.grouping_key;\n            }\n\n            // Handle dictionary-like keys (converted to string in JS)\n            if (typeof grouping_key === 'object') {\n                grouping_key = JSON.stringify(raw_grouping_key);\n            }\n\n            // Base amount\n            if(!(grouping_key in values_per_grouping_key)){\n                values_per_grouping_key[grouping_key] = {\n                    base_amount_currency: tax_data.base_amount_currency,\n                    base_amount: tax_data.base_amount,\n                    raw_base_amount_currency: tax_data.raw_base_amount_currency,\n                    raw_base_amount: tax_data.raw_base_amount,\n                    tax_amount_currency: 0.0,\n                    tax_amount: 0.0,\n                    raw_tax_amount_currency: 0.0,\n                    raw_tax_amount: 0.0,\n                    taxes_data: [],\n                    grouping_key: raw_grouping_key\n                };\n            }\n            const values = values_per_grouping_key[grouping_key];\n            values.taxes_data.push(tax_data);\n\n            // Tax amount\n            values.tax_amount_currency += tax_data.tax_amount_currency;\n            values.tax_amount += tax_data.tax_amount;\n            values.raw_tax_amount_currency += tax_data.raw_tax_amount_currency;\n            values.raw_tax_amount += tax_data.raw_tax_amount;\n        }\n\n        if (!taxes_data.length) {\n            values_per_grouping_key[null] = {\n                base_amount_currency: tax_details.total_excluded_currency,\n                base_amount: tax_details.total_excluded,\n                raw_base_amount_currency: tax_details.raw_total_excluded_currency,\n                raw_base_amount: tax_details.raw_total_excluded,\n                tax_amount_currency: 0.0,\n                tax_amount: 0.0,\n                raw_tax_amount_currency: 0.0,\n                raw_tax_amount: 0.0,\n                taxes_data: [],\n                grouping_key: null\n            };\n        }\n\n        return values_per_grouping_key;\n    },\n\n    aggregate_base_lines_tax_details(base_lines, grouping_function) {\n        return base_lines.map(base_line => [base_line, this.aggregate_base_line_tax_details(base_line, grouping_function)]);\n    },\n\n    aggregate_base_lines_aggregated_values(base_lines_aggregated_values) {\n        const default_float_fields = new Set([\n            'base_amount_currency',\n            'base_amount',\n            'raw_base_amount_currency',\n            'raw_base_amount',\n            'tax_amount_currency',\n            'tax_amount',\n            'raw_tax_amount_currency',\n            'raw_tax_amount'\n        ]);\n        const values_per_grouping_key = {};\n        for (const [base_line, aggregated_values] of base_lines_aggregated_values) {\n            for (const [raw_grouping_key, values] of Object.entries(aggregated_values)) {\n                const grouping_key = values.grouping_key;\n\n                if(!(raw_grouping_key in values_per_grouping_key)){\n                    const initial_values = values_per_grouping_key[raw_grouping_key] = {\n                        base_line_x_taxes_data: [],\n                        grouping_key: grouping_key,\n                    };\n                    default_float_fields.forEach(field => {\n                        initial_values[field] = 0.0;\n                    });\n                }\n                const agg_values = values_per_grouping_key[raw_grouping_key];\n                default_float_fields.forEach(field => {\n                    agg_values[field] += values[field];\n                });\n                agg_values.base_line_x_taxes_data.push([base_line, values.taxes_data]);\n            }\n        }\n\n        return values_per_grouping_key;\n    },\n\n};\n", "import { useAutoresize } from \"@web/core/utils/autoresize\";\n\n/**\n * This overriden version of the resizeTextArea method is specificly done for the product_label_section_and_note widget\n * His necessity is found in the fact that the cell of said widget doesn't contain only the input or textarea to resize\n * but also another node containing the name of the product if said data is available. This means that the autoresize\n * method which sets the height of the parent cell should sometimes add an additional row to the parent cell so that\n * no text overflows\n *\n * @param {Ref} ref\n */\nexport function useProductAndLabelAutoresize(ref, options = {}) {\n    useAutoresize(ref, { onResize: productAndLabelResizeTextArea, ...options });\n}\n\nexport function productAndLabelResizeTextArea(textarea, options = {}) {\n    const style = window.getComputedStyle(textarea);\n    if (options.targetParentName) {\n        let target = textarea.parentElement;\n        let shouldContinue = true;\n        while (target && shouldContinue) {\n            const totalParentHeight = Array.from(target.children).reduce((total, child) => {\n                const childHeight = child.style.height || style.lineHeight;\n                return total + parseFloat(childHeight);\n            }, 0);\n            target.style.height = `${totalParentHeight}px`;\n            if (target.getAttribute(\"name\") === options.targetParentName) {\n                shouldContinue = false;\n            }\n            target = target.parentElement;\n        }\n    }\n}\n", "// Generated by CoffeeScript 1.7.1\n(function() {\n  var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlashAndSpace, hasTextSelected, luhnCheck, reFormatCVC, reFormatCardNumber, reFormatExpiry, reFormatNumeric, replaceFullWidthChars, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, safeVal, setCardType,\n    __slice = [].slice,\n    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };\n\n  $ = window.jQuery || window.Zepto || window.$;\n\n  $.payment = {};\n\n  $.payment.fn = {};\n\n  $.fn.payment = function() {\n    var args, method;\n    method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];\n    return $.payment.fn[method].apply(this, args);\n  };\n\n  defaultFormat = /(\\d{1,4})/g;\n\n  $.payment.cards = cards = [\n    {\n      type: 'maestro',\n      patterns: [5018, 502, 503, 506, 56, 58, 639, 6220, 67],\n      format: defaultFormat,\n      length: [12, 13, 14, 15, 16, 17, 18, 19],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'forbrugsforeningen',\n      patterns: [600],\n      format: defaultFormat,\n      length: [16],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'dankort',\n      patterns: [5019],\n      format: defaultFormat,\n      length: [16],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'visa',\n      patterns: [4],\n      format: defaultFormat,\n      length: [13, 16],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'mastercard',\n      patterns: [51, 52, 53, 54, 55, 22, 23, 24, 25, 26, 27],\n      format: defaultFormat,\n      length: [16],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'amex',\n      patterns: [34, 37],\n      format: /(\\d{1,4})(\\d{1,6})?(\\d{1,5})?/,\n      length: [15],\n      cvcLength: [3, 4],\n      luhn: true\n    }, {\n      type: 'dinersclub',\n      patterns: [30, 36, 38, 39],\n      format: /(\\d{1,4})(\\d{1,6})?(\\d{1,4})?/,\n      length: [14],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'discover',\n      patterns: [60, 64, 65, 622],\n      format: defaultFormat,\n      length: [16],\n      cvcLength: [3],\n      luhn: true\n    }, {\n      type: 'unionpay',\n      patterns: [62, 88],\n      format: defaultFormat,\n      length: [16, 17, 18, 19],\n      cvcLength: [3],\n      luhn: false\n    }, {\n      type: 'jcb',\n      patterns: [35],\n      format: defaultFormat,\n      length: [16],\n      cvcLength: [3],\n      luhn: true\n    }\n  ];\n\n  cardFromNumber = function(num) {\n    var card, p, pattern, _i, _j, _len, _len1, _ref;\n    num = (num + '').replace(/\\D/g, '');\n    for (_i = 0, _len = cards.length; _i < _len; _i++) {\n      card = cards[_i];\n      _ref = card.patterns;\n      for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {\n        pattern = _ref[_j];\n        p = pattern + '';\n        if (num.substr(0, p.length) === p) {\n          return card;\n        }\n      }\n    }\n  };\n\n  cardFromType = function(type) {\n    var card, _i, _len;\n    for (_i = 0, _len = cards.length; _i < _len; _i++) {\n      card = cards[_i];\n      if (card.type === type) {\n        return card;\n      }\n    }\n  };\n\n  luhnCheck = function(num) {\n    var digit, digits, odd, sum, _i, _len;\n    odd = true;\n    sum = 0;\n    digits = (num + '').split('').reverse();\n    for (_i = 0, _len = digits.length; _i < _len; _i++) {\n      digit = digits[_i];\n      digit = parseInt(digit, 10);\n      if ((odd = !odd)) {\n        digit *= 2;\n      }\n      if (digit > 9) {\n        digit -= 9;\n      }\n      sum += digit;\n    }\n    return sum % 10 === 0;\n  };\n\n  hasTextSelected = function($target) {\n    var _ref;\n    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== $target.prop('selectionEnd')) {\n      return true;\n    }\n    if ((typeof document !== \"undefined\" && document !== null ? (_ref = document.selection) != null ? _ref.createRange : void 0 : void 0) != null) {\n      if (document.selection.createRange().text) {\n        return true;\n      }\n    }\n    return false;\n  };\n\n  safeVal = function(value, $target) {\n    var currPair, cursor, digit, error, last, prevPair;\n    try {\n      cursor = $target.prop('selectionStart');\n    } catch (_error) {\n      error = _error;\n      cursor = null;\n    }\n    last = $target.val();\n    $target.val(value);\n    if (cursor !== null && $target.is(\":focus\")) {\n      if (cursor === last.length) {\n        cursor = value.length;\n      }\n      if (last !== value) {\n        prevPair = last.slice(cursor - 1, +cursor + 1 || 9e9);\n        currPair = value.slice(cursor - 1, +cursor + 1 || 9e9);\n        digit = value[cursor];\n        if (/\\d/.test(digit) && prevPair === (\"\" + digit + \" \") && currPair === (\" \" + digit)) {\n          cursor = cursor + 1;\n        }\n      }\n      $target.prop('selectionStart', cursor);\n      return $target.prop('selectionEnd', cursor);\n    }\n  };\n\n  replaceFullWidthChars = function(str) {\n    var chars, chr, fullWidth, halfWidth, idx, value, _i, _len;\n    if (str == null) {\n      str = '';\n    }\n    fullWidth = '\\uff10\\uff11\\uff12\\uff13\\uff14\\uff15\\uff16\\uff17\\uff18\\uff19';\n    halfWidth = '0123456789';\n    value = '';\n    chars = str.split('');\n    for (_i = 0, _len = chars.length; _i < _len; _i++) {\n      chr = chars[_i];\n      idx = fullWidth.indexOf(chr);\n      if (idx > -1) {\n        chr = halfWidth[idx];\n      }\n      value += chr;\n    }\n    return value;\n  };\n\n  reFormatNumeric = function(e) {\n    var $target;\n    $target = $(e.currentTarget);\n    return setTimeout(function() {\n      var value;\n      value = $target.val();\n      value = replaceFullWidthChars(value);\n      value = value.replace(/\\D/g, '');\n      return safeVal(value, $target);\n    });\n  };\n\n  reFormatCardNumber = function(e) {\n    var $target;\n    $target = $(e.currentTarget);\n    return setTimeout(function() {\n      var value;\n      value = $target.val();\n      value = replaceFullWidthChars(value);\n      value = $.payment.formatCardNumber(value);\n      return safeVal(value, $target);\n    });\n  };\n\n  formatCardNumber = function(e) {\n    var $target, card, digit, length, re, upperLength, value;\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    $target = $(e.currentTarget);\n    value = $target.val();\n    card = cardFromNumber(value + digit);\n    length = (value.replace(/\\D/g, '') + digit).length;\n    upperLength = 16;\n    if (card) {\n      upperLength = card.length[card.length.length - 1];\n    }\n    if (length >= upperLength) {\n      return;\n    }\n    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {\n      return;\n    }\n    if (card && card.type === 'amex') {\n      re = /^(\\d{4}|\\d{4}\\s\\d{6})$/;\n    } else {\n      re = /(?:^|\\s)(\\d{4})$/;\n    }\n    if (re.test(value)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(value + ' ' + digit);\n      });\n    } else if (re.test(value + digit)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(value + digit + ' ');\n      });\n    }\n  };\n\n  formatBackCardNumber = function(e) {\n    var $target, value;\n    $target = $(e.currentTarget);\n    value = $target.val();\n    if (e.which !== 8) {\n      return;\n    }\n    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {\n      return;\n    }\n    if (/\\d\\s$/.test(value)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(value.replace(/\\d\\s$/, ''));\n      });\n    } else if (/\\s\\d?$/.test(value)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(value.replace(/\\d$/, ''));\n      });\n    }\n  };\n\n  reFormatExpiry = function(e) {\n    var $target;\n    $target = $(e.currentTarget);\n    return setTimeout(function() {\n      var value;\n      value = $target.val();\n      value = replaceFullWidthChars(value);\n      value = $.payment.formatExpiry(value);\n      return safeVal(value, $target);\n    });\n  };\n\n  formatExpiry = function(e) {\n    var $target, digit, val;\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    $target = $(e.currentTarget);\n    val = $target.val() + digit;\n    if (/^\\d$/.test(val) && (val !== '0' && val !== '1')) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(\"0\" + val + \" / \");\n      });\n    } else if (/^\\d\\d$/.test(val)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        var m1, m2;\n        m1 = parseInt(val[0], 10);\n        m2 = parseInt(val[1], 10);\n        if (m2 > 2 && m1 !== 0) {\n          return $target.val(\"0\" + m1 + \" / \" + m2);\n        } else {\n          return $target.val(\"\" + val + \" / \");\n        }\n      });\n    }\n  };\n\n  formatForwardExpiry = function(e) {\n    var $target, digit, val;\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    $target = $(e.currentTarget);\n    val = $target.val();\n    if (/^\\d\\d$/.test(val)) {\n      return $target.val(\"\" + val + \" / \");\n    }\n  };\n\n  formatForwardSlashAndSpace = function(e) {\n    var $target, val, which;\n    which = String.fromCharCode(e.which);\n    if (!(which === '/' || which === ' ')) {\n      return;\n    }\n    $target = $(e.currentTarget);\n    val = $target.val();\n    if (/^\\d$/.test(val) && val !== '0') {\n      return $target.val(\"0\" + val + \" / \");\n    }\n  };\n\n  formatBackExpiry = function(e) {\n    var $target, value;\n    $target = $(e.currentTarget);\n    value = $target.val();\n    if (e.which !== 8) {\n      return;\n    }\n    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {\n      return;\n    }\n    if (/\\d\\s\\/\\s$/.test(value)) {\n      e.preventDefault();\n      return setTimeout(function() {\n        return $target.val(value.replace(/\\d\\s\\/\\s$/, ''));\n      });\n    }\n  };\n\n  reFormatCVC = function(e) {\n    var $target;\n    $target = $(e.currentTarget);\n    return setTimeout(function() {\n      var value;\n      value = $target.val();\n      value = replaceFullWidthChars(value);\n      value = value.replace(/\\D/g, '').slice(0, 4);\n      return safeVal(value, $target);\n    });\n  };\n\n  restrictNumeric = function(e) {\n    var input;\n    if (e.metaKey || e.ctrlKey) {\n      return true;\n    }\n    if (e.which === 32) {\n      return false;\n    }\n    if (e.which === 0) {\n      return true;\n    }\n    if (e.which < 33) {\n      return true;\n    }\n    input = String.fromCharCode(e.which);\n    return !!/[\\d\\s]/.test(input);\n  };\n\n  restrictCardNumber = function(e) {\n    var $target, card, digit, value;\n    $target = $(e.currentTarget);\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    if (hasTextSelected($target)) {\n      return;\n    }\n    value = ($target.val() + digit).replace(/\\D/g, '');\n    card = cardFromNumber(value);\n    if (card) {\n      return value.length <= card.length[card.length.length - 1];\n    } else {\n      return value.length <= 16;\n    }\n  };\n\n  restrictExpiry = function(e) {\n    var $target, digit, value;\n    $target = $(e.currentTarget);\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    if (hasTextSelected($target)) {\n      return;\n    }\n    value = $target.val() + digit;\n    value = value.replace(/\\D/g, '');\n    if (value.length > 6) {\n      return false;\n    }\n  };\n\n  restrictCVC = function(e) {\n    var $target, digit, val;\n    $target = $(e.currentTarget);\n    digit = String.fromCharCode(e.which);\n    if (!/^\\d+$/.test(digit)) {\n      return;\n    }\n    if (hasTextSelected($target)) {\n      return;\n    }\n    val = $target.val() + digit;\n    return val.length <= 4;\n  };\n\n  setCardType = function(e) {\n    var $target, allTypes, card, cardType, val;\n    $target = $(e.currentTarget);\n    val = $target.val();\n    cardType = $.payment.cardType(val) || 'unknown';\n    if (!$target.hasClass(cardType)) {\n      allTypes = (function() {\n        var _i, _len, _results;\n        _results = [];\n        for (_i = 0, _len = cards.length; _i < _len; _i++) {\n          card = cards[_i];\n          _results.push(card.type);\n        }\n        return _results;\n      })();\n      $target.removeClass('unknown');\n      $target.removeClass(allTypes.join(' '));\n      $target.addClass(cardType);\n      $target.toggleClass('identified', cardType !== 'unknown');\n      return $target.trigger('payment.cardType', cardType);\n    }\n  };\n\n  $.payment.fn.formatCardCVC = function() {\n    this.on('keypress', restrictNumeric);\n    this.on('keypress', restrictCVC);\n    this.on('paste', reFormatCVC);\n    this.on('change', reFormatCVC);\n    this.on('input', reFormatCVC);\n    return this;\n  };\n\n  $.payment.fn.formatCardExpiry = function() {\n    this.on('keypress', restrictNumeric);\n    this.on('keypress', restrictExpiry);\n    this.on('keypress', formatExpiry);\n    this.on('keypress', formatForwardSlashAndSpace);\n    this.on('keypress', formatForwardExpiry);\n    this.on('keydown', formatBackExpiry);\n    this.on('change', reFormatExpiry);\n    this.on('input', reFormatExpiry);\n    return this;\n  };\n\n  $.payment.fn.formatCardNumber = function() {\n    this.on('keypress', restrictNumeric);\n    this.on('keypress', restrictCardNumber);\n    this.on('keypress', formatCardNumber);\n    this.on('keydown', formatBackCardNumber);\n    this.on('keyup', setCardType);\n    this.on('paste', reFormatCardNumber);\n    this.on('change', reFormatCardNumber);\n    this.on('input', reFormatCardNumber);\n    this.on('input', setCardType);\n    return this;\n  };\n\n  $.payment.fn.restrictNumeric = function() {\n    this.on('keypress', restrictNumeric);\n    this.on('paste', reFormatNumeric);\n    this.on('change', reFormatNumeric);\n    this.on('input', reFormatNumeric);\n    return this;\n  };\n\n  $.payment.fn.cardExpiryVal = function() {\n    return $.payment.cardExpiryVal($(this).val());\n  };\n\n  $.payment.cardExpiryVal = function(value) {\n    var month, prefix, year, _ref;\n    _ref = value.split(/[\\s\\/]+/, 2), month = _ref[0], year = _ref[1];\n    if ((year != null ? year.length : void 0) === 2 && /^\\d+$/.test(year)) {\n      prefix = (new Date).getFullYear();\n      prefix = prefix.toString().slice(0, 2);\n      year = prefix + year;\n    }\n    month = parseInt(month, 10);\n    year = parseInt(year, 10);\n    return {\n      month: month,\n      year: year\n    };\n  };\n\n  $.payment.validateCardNumber = function(num) {\n    var card, _ref;\n    num = (num + '').replace(/\\s+|-/g, '');\n    if (!/^\\d+$/.test(num)) {\n      return false;\n    }\n    card = cardFromNumber(num);\n    if (!card) {\n      return false;\n    }\n    return (_ref = num.length, __indexOf.call(card.length, _ref) >= 0) && (card.luhn === false || luhnCheck(num));\n  };\n\n  $.payment.validateCardExpiry = function(month, year) {\n    var currentTime, expiry, _ref;\n    if (typeof month === 'object' && 'month' in month) {\n      _ref = month, month = _ref.month, year = _ref.year;\n    }\n    if (!(month && year)) {\n      return false;\n    }\n    month = $.trim(month);\n    year = $.trim(year);\n    if (!/^\\d+$/.test(month)) {\n      return false;\n    }\n    if (!/^\\d+$/.test(year)) {\n      return false;\n    }\n    if (!((1 <= month && month <= 12))) {\n      return false;\n    }\n    if (year.length === 2) {\n      if (year < 70) {\n        year = \"20\" + year;\n      } else {\n        year = \"19\" + year;\n      }\n    }\n    if (year.length !== 4) {\n      return false;\n    }\n    expiry = new Date(year, month);\n    currentTime = new Date;\n    expiry.setMonth(expiry.getMonth() - 1);\n    expiry.setMonth(expiry.getMonth() + 1, 1);\n    return expiry > currentTime;\n  };\n\n  $.payment.validateCardCVC = function(cvc, type) {\n    var card, _ref;\n    cvc = $.trim(cvc);\n    if (!/^\\d+$/.test(cvc)) {\n      return false;\n    }\n    card = cardFromType(type);\n    if (card != null) {\n      return _ref = cvc.length, __indexOf.call(card.cvcLength, _ref) >= 0;\n    } else {\n      return cvc.length >= 3 && cvc.length <= 4;\n    }\n  };\n\n  $.payment.cardType = function(num) {\n    var _ref;\n    if (!num) {\n      return null;\n    }\n    return ((_ref = cardFromNumber(num)) != null ? _ref.type : void 0) || null;\n  };\n\n  $.payment.formatCardNumber = function(num) {\n    var card, groups, upperLength, _ref;\n    num = num.replace(/\\D/g, '');\n    card = cardFromNumber(num);\n    if (!card) {\n      return num;\n    }\n    upperLength = card.length[card.length.length - 1];\n    num = num.slice(0, upperLength);\n    if (card.format.global) {\n      return (_ref = num.match(card.format)) != null ? _ref.join(' ') : void 0;\n    } else {\n      groups = card.format.exec(num);\n      if (groups == null) {\n        return;\n      }\n      groups.shift();\n      groups = $.grep(groups, function(n) {\n        return n;\n      });\n      return groups.join(' ');\n    }\n  };\n\n  $.payment.formatExpiry = function(expiry) {\n    var mon, parts, sep, year;\n    parts = expiry.match(/^\\D*(\\d{1,2})(\\D+)?(\\d{1,4})?/);\n    if (!parts) {\n      return '';\n    }\n    mon = parts[1] || '';\n    sep = parts[2] || '';\n    year = parts[3] || '';\n    if (year.length > 0) {\n      sep = ' / ';\n    } else if (sep === ' /') {\n      mon = mon.substring(0, 1);\n      sep = '';\n    } else if (mon.length === 2 || sep.length > 0) {\n      sep = ' / ';\n    } else if (mon.length === 1 && (mon !== '0' && mon !== '1')) {\n      mon = \"0\" + mon;\n      sep = ' / ';\n    }\n    return mon + sep + year;\n  };\n\n}).call(this);\n", "/** @odoo-module */\n\nimport publicWidget from '@web/legacy/js/public/public_widget';\nimport { Component } from '@odoo/owl';\n\npublicWidget.registry.PaymentExpressCheckoutForm = publicWidget.Widget.extend({\n    selector: 'form[name=\"o_payment_express_checkout_form\"]',\n\n    /**\n     * @override\n     */\n    start: async function () {\n        await this._super(...arguments);\n        this.paymentContext = {};\n        Object.assign(this.paymentContext, this.el.dataset);\n        this.paymentContext.shippingInfoRequired = !!this.paymentContext['shippingInfoRequired'];\n        const expressCheckoutForms = this._getExpressCheckoutForms();\n        for (const expressCheckoutForm of expressCheckoutForms) {\n            await this._prepareExpressCheckoutForm(expressCheckoutForm.dataset);\n        }\n        // Monitor updates of the amount on eCommerce's cart pages.\n        Component.env.bus.addEventListener('cart_amount_changed', (ev) => this._updateAmount(...ev.detail));\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //--------------------------------------------------------------------------\n\n    /**\n     * Return all express checkout forms found on the page.\n     *\n     * @private\n     * @return {NodeList} - All express checkout forms found on the page.\n     */\n    _getExpressCheckoutForms() {\n        return document.querySelectorAll(\n            'form[name=\"o_payment_express_checkout_form\"] div[name=\"o_express_checkout_container\"]'\n        );\n    },\n\n    /**\n     * Prepare the provider-specific express checkout form based on the provided data.\n     *\n     * For a provider to manage an express checkout form, it must override this method.\n     *\n     * @private\n     * @param {Object} providerData - The provider-specific data.\n     * @return {void}\n     */\n    async _prepareExpressCheckoutForm(providerData) {},\n\n    /**\n     * Prepare the params for the RPC to the transaction route.\n     *\n     * @private\n     * @param {number} providerId - The id of the provider handling the transaction.\n     * @returns {object} - The transaction route params.\n     */\n    _prepareTransactionRouteParams(providerId) {\n        return {\n            'provider_id': parseInt(providerId),\n            'payment_method_id': parseInt(this.paymentContext['paymentMethodUnknownId']),\n            'token_id': null,\n            'flow': 'direct',\n            'tokenization_requested': false,\n            'landing_route': this.paymentContext['landingRoute'],\n            'access_token': this.paymentContext['accessToken'],\n            'csrf_token': odoo.csrf_token,\n        };\n    },\n\n    /**\n     * Update the amount of the express checkout form.\n     *\n     * For a provider to manage an express form, it must override this method.\n     *\n     * @private\n     * @param {number} newAmount - The new amount.\n     * @param {number} newMinorAmount - The new minor amount.\n     * @return {void}\n     */\n    _updateAmount(newAmount, newMinorAmount) {\n        this.paymentContext.amount = parseFloat(newAmount);\n        this.paymentContext.minorAmount = parseInt(newMinorAmount);\n        this._getExpressCheckoutForms().forEach(form => {\n            if (newAmount == 0) {\n                form.classList.add('d-none')}\n            else {\n                form.classList.remove('d-none')\n            }\n        })\n    },\n\n});\n\nexport const paymentExpressCheckoutForm = publicWidget.registry.PaymentExpressCheckoutForm;\n", "/** @odoo-module **/\n\nimport publicWidget from '@web/legacy/js/public/public_widget';\nimport { Component } from \"@odoo/owl\";\n\npublicWidget.registry.PaymentButton = publicWidget.Widget.extend({\n    selector: 'button[name=\"o_payment_submit_button\"]',\n\n    async start() {\n        await this._super(...arguments);\n        this.paymentButton = this.el;\n        this.iconClass = this.paymentButton.dataset.iconClass;\n        this._enable();\n        Component.env.bus.addEventListener('enablePaymentButton', this._enable.bind(this));\n        Component.env.bus.addEventListener('disablePaymentButton',this._disable.bind(this));\n        Component.env.bus.addEventListener('hidePaymentButton', this._hide.bind(this));\n        Component.env.bus.addEventListener('showPaymentButton', this._show.bind(this));\n    },\n\n    /**\n     * Check if the payment button can be enabled and do it if so.\n     *\n     * @private\n     * @return {void}\n     */\n    _enable() {\n        if (this._canSubmit()) {\n            this.paymentButton.disabled = false;\n        }\n    },\n\n    /**\n     * Check whether the payment form can be submitted, i.e. whether exactly one payment option is\n     * selected.\n     *\n     * For a module to add a condition on the submission of the form, it must override this method\n     * and return whether both this method's condition and the override method's condition are met.\n     *\n     * @private\n     * @return {boolean} Whether the form can be submitted.\n     */\n    _canSubmit() {\n        const paymentForm = document.querySelector('#o_payment_form');\n        if (!paymentForm) {  // Payment form is not present.\n            return true; // Ignore the check.\n        }\n        return document.querySelectorAll('input[name=\"o_payment_radio\"]:checked').length === 1;\n    },\n\n    /**\n     * Disable the payment button.\n     *\n     * @private\n     * @return {void}\n     */\n    _disable() {\n        this.paymentButton.disabled = true;\n    },\n\n    /**\n     * Hide the payment button.\n     *\n     * @private\n     * @return {void}\n     */\n    _hide() {\n        this.paymentButton.classList.add('d-none');\n    },\n\n    /**\n     * Show the payment button.\n     *\n     * @private\n     * @return {void}\n     */\n    _show() {\n        this.paymentButton.classList.remove('d-none');\n    },\n\n});\nexport default publicWidget.registry.PaymentButton;\n", "/** @odoo-module **/\n\nimport { Component } from '@odoo/owl';\nimport publicWidget from '@web/legacy/js/public/public_widget';\nimport { browser } from '@web/core/browser/browser';\nimport { ConfirmationDialog } from '@web/core/confirmation_dialog/confirmation_dialog';\nimport { _t } from '@web/core/l10n/translation';\nimport { renderToMarkup } from '@web/core/utils/render';\nimport { rpc, RPCError } from '@web/core/network/rpc';\n\npublicWidget.registry.PaymentForm = publicWidget.Widget.extend({\n    selector: '#o_payment_form',\n    events: Object.assign({}, publicWidget.Widget.prototype.events, {\n        'click [name=\"o_payment_radio\"]': '_selectPaymentOption',\n        'click [name=\"o_payment_delete_token\"]': '_fetchTokenData',\n        'click [name=\"o_payment_expand_button\"]': '_hideExpandButton',\n        'click [name=\"o_payment_submit_button\"]': '_submitForm',\n    }),\n\n    // #=== WIDGET LIFECYCLE ===#\n\n    /**\n     * @override\n     */\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n    },\n\n    /**\n     * @override\n     */\n    async start() {\n        // Synchronously initialize paymentContext before any await.\n        this.paymentContext = {};\n        Object.assign(this.paymentContext, this.el.dataset);\n\n        await this._super(...arguments);\n\n        // Expand the payment form of the selected payment option if there is only one.\n        const checkedRadio = document.querySelector('input[name=\"o_payment_radio\"]:checked');\n        if (checkedRadio) {\n            await this._expandInlineForm(checkedRadio);\n            this._enableButton(false);\n        } else {\n            this._setPaymentFlow(); // Initialize the payment flow to let providers overwrite it.\n        }\n\n        this.$('[data-bs-toggle=\"tooltip\"]').tooltip();\n    },\n\n    // #=== EVENT HANDLERS ===#\n\n    /**\n     * Open the inline form of the selected payment option, if any.\n     *\n     * @private\n     * @param {Event} ev\n     * @return {void}\n     */\n    async _selectPaymentOption(ev) {\n        // Show the inputs in case they have been hidden.\n        this._showInputs();\n\n        // Disable the submit button while preparing the inline form.\n        this._disableButton();\n\n        // Unfold and prepare the inline form of the selected payment option.\n        const checkedRadio = ev.target;\n        await this._expandInlineForm(checkedRadio);\n\n        // Re-enable the submit button after the inline form has been prepared.\n        this._enableButton(false);\n    },\n\n    /**\n     * Fetch data relative to the documents linked to the token and delegate them to the token\n     * deletion confirmation dialog.\n     *\n     * @private\n     * @param {Event} ev\n     * @return {void}\n     */\n    _fetchTokenData(ev) {\n        ev.preventDefault();\n\n        const linkedRadio = document.getElementById(ev.currentTarget.dataset['linkedRadio']);\n        const tokenId = this._getPaymentOptionId(linkedRadio);\n        this.orm.call(\n            'payment.token',\n            'get_linked_records_info',\n            [tokenId],\n        ).then(linkedRecordsInfo => {\n            this._challengeTokenDeletion(tokenId, linkedRecordsInfo);\n        }).catch(error => {\n            if (error instanceof RPCError) {\n                this._displayErrorDialog(\n                    _t(\"Cannot delete payment method\"), error.data.message\n                );\n            } else {\n                return Promise.reject(error);\n            }\n        });\n    },\n\n    /**\n     * Hide the button to expand the payment methods section once it has been clicked.\n     *\n     * @private\n     * @param {Event} ev\n     * @return {void}\n     */\n    _hideExpandButton(ev) {\n        ev.target.classList.add('d-none');\n    },\n\n    /**\n     * Update the payment context with the selected payment option and initiate its payment flow.\n     *\n     * @private\n     * @param {Event} ev\n     * @return {void}\n     */\n    async _submitForm(ev) {\n        ev.stopPropagation();\n        ev.preventDefault();\n\n        const checkedRadio = this.el.querySelector('input[name=\"o_payment_radio\"]:checked');\n\n        // Block the entire UI to prevent fiddling with other widgets.\n        this._disableButton(true);\n\n        // Initiate the payment flow of the selected payment option.\n        const flow = this.paymentContext.flow = this._getPaymentFlow(checkedRadio);\n        const paymentOptionId = this.paymentContext.paymentOptionId = this._getPaymentOptionId(\n            checkedRadio\n        );\n        if (flow === 'token' && this.paymentContext['assignTokenRoute']) { // Assign token flow.\n            await this._assignToken(paymentOptionId);\n        } else { // Both tokens and payment methods must process a payment operation.\n            const providerCode = this.paymentContext.providerCode = this._getProviderCode(\n                checkedRadio\n            );\n            const pmCode = this.paymentContext.paymentMethodCode = this._getPaymentMethodCode(\n                checkedRadio\n            );\n            this.paymentContext.providerId = this._getProviderId(checkedRadio);\n            if (this._getPaymentOptionType(checkedRadio) === 'token') {\n                this.paymentContext.tokenId = paymentOptionId;\n            } else { // 'payment_method'\n                this.paymentContext.paymentMethodId = paymentOptionId;\n            }\n            const inlineForm = this._getInlineForm(checkedRadio);\n            this.paymentContext.tokenizationRequested = inlineForm?.querySelector(\n                '[name=\"o_payment_tokenize_checkbox\"]'\n            )?.checked ?? this.paymentContext['mode'] === 'validation';\n            await this._initiatePaymentFlow(providerCode, paymentOptionId, pmCode, flow);\n        }\n    },\n\n    // #=== DOM MANIPULATION ===#\n\n    /**\n     * Check if the submit button can be enabled and do it if so.\n     *\n     * @private\n     * @param {boolean} unblockUI - Whether the UI should also be unblocked.\n     * @return {void}\n     */\n    _enableButton(unblockUI = true) {\n        Component.env.bus.trigger('enablePaymentButton');\n        if (unblockUI) {\n            this.call('ui', 'unblock');\n        }\n    },\n\n    /**\n     * Disable the submit button.\n     *\n     * @private\n     * @param {boolean} blockUI - Whether the UI should also be blocked.\n     * @return {void}\n     */\n    _disableButton(blockUI = false) {\n        Component.env.bus.trigger('disablePaymentButton');\n        if (blockUI) {\n            this.call('ui', 'block');\n        }\n    },\n\n    /**\n     * Show the tokenization checkbox, its label, and the submit button.\n     *\n     * @private\n     * @return {void}\n     */\n    _showInputs() {\n        // Show the tokenization checkbox and its label.\n        const tokenizeContainer = this.el.querySelector('[name=\"o_payment_tokenize_container\"]');\n        tokenizeContainer?.classList.remove('d-none');\n\n        // Show the submit button.\n        Component.env.bus.trigger('showPaymentButton');\n    },\n\n    /**\n     * Hide the tokenization checkbox, its label, and the submit button.\n     *\n     * The inputs should typically be hidden when the customer has to perform additional actions in\n     * the inline form. All inputs are automatically shown again when the customer selects another\n     * payment option.\n     *\n     * @private\n     * @return {void}\n     */\n    _hideInputs() {\n        // Hide the tokenization checkbox and its label.\n        const tokenizeContainer = this.el.querySelector('[name=\"o_payment_tokenize_container\"]');\n        tokenizeContainer?.classList.add('d-none');\n\n        // Hide the submit button.\n        Component.env.bus.trigger('hidePaymentButton');\n    },\n\n    /**\n     * Open the inline form of the selected payment option and collapse the others.\n     *\n     * @private\n     * @param {HTMLInputElement} radio - The radio button linked to the payment option.\n     * @return {void}\n     */\n    async _expandInlineForm(radio) {\n        this._collapseInlineForms(); // Collapse previously opened inline forms.\n        this._setPaymentFlow(); // Reset the payment flow to let providers overwrite it.\n\n        // Prepare the inline form of the selected payment option.\n        const providerId = this._getProviderId(radio);\n        const providerCode = this._getProviderCode(radio);\n        const paymentOptionId = this._getPaymentOptionId(radio);\n        const paymentMethodCode = this._getPaymentMethodCode(radio);\n        const flow = this._getPaymentFlow(radio);\n        await this._prepareInlineForm(\n            providerId, providerCode, paymentOptionId, paymentMethodCode, flow\n        );\n\n        // Display the prepared inline form if it is not empty.\n        const inlineForm = this._getInlineForm(radio);\n        if (inlineForm && inlineForm.children.length > 0) {\n            inlineForm.classList.remove('d-none');\n        }\n    },\n\n    /**\n     * Prepare the provider-specific inline form of the selected payment option.\n     *\n     * For a provider to manage an inline form, it must override this method and render the content\n     * of the form.\n     *\n     * @private\n     * @param {number} providerId - The id of the selected payment option's provider.\n     * @param {string} providerCode - The code of the selected payment option's provider.\n     * @param {number} paymentOptionId - The id of the selected payment option.\n     * @param {string} paymentMethodCode - The code of the selected payment method, if any.\n     * @param {string} flow - The online payment flow of the selected payment option.\n     * @return {void}\n     */\n    async _prepareInlineForm(providerId, providerCode, paymentOptionId, paymentMethodCode, flow) {},\n\n    /**\n     * Collapse all inline forms of the current widget.\n     *\n     * @private\n     * @return {void}\n     */\n    _collapseInlineForms() {\n        this.el.querySelectorAll('[name=\"o_payment_inline_form\"]').forEach(inlineForm => {\n            inlineForm.classList.add('d-none');\n        });\n    },\n\n    /**\n     * Display an error dialog.\n     *\n     * @private\n     * @param {string} title - The title of the dialog.\n     * @param {string} errorMessage - The error message.\n     * @return {void}\n     */\n    _displayErrorDialog(title, errorMessage = '') {\n        this.call('dialog', 'add', ConfirmationDialog, { title: title, body: errorMessage || \"\" });\n    },\n\n    /**\n     * Display the token deletion confirmation dialog.\n     *\n     * @private\n     * @param {number} tokenId - The id of the token whose deletion was requested.\n     * @param {object} linkedRecordsInfo - The data relative to the documents linked to the token.\n     * @return {void}\n     */\n    _challengeTokenDeletion(tokenId, linkedRecordsInfo) {\n        const body = renderToMarkup('payment.deleteTokenDialog', { linkedRecordsInfo });\n        this.call('dialog', 'add', ConfirmationDialog, {\n            title: _t(\"Warning!\"),\n            body,\n            confirmLabel: _t(\"Confirm Deletion\"),\n            confirm: () => this._archiveToken(tokenId),\n            cancel: () => {},\n        });\n    },\n\n    // #=== PAYMENT FLOW ===#\n\n    /**\n     * Set the payment flow for the selected payment option.\n     *\n     * For a provider to manage direct payments, it must call this method and set the payment flow\n     * when its payment option is selected.\n     *\n     * @private\n     * @param {string} flow - The flow for the selected payment option. Either 'redirect', 'direct',\n     *                        or 'token'\n     * @return {void}\n     */\n    _setPaymentFlow(flow = 'redirect') {\n        if (['redirect', 'direct', 'token'].includes(flow)) {\n            this.paymentContext.flow = flow;\n        } else {\n            console.warn(`The value ${flow} is not a supported flow. Falling back to redirect.`);\n            this.paymentContext.flow = 'redirect';\n        }\n    },\n\n    /**\n     * Assign the selected token to a document through the `assignTokenRoute`.\n     *\n     * @private\n     * @param {number} tokenId - The id of the token to assign.\n     * @return {void}\n     */\n    async _assignToken(tokenId) {\n        rpc(this.paymentContext['assignTokenRoute'], {\n            'token_id': tokenId,\n            'access_token': this.paymentContext['accessToken'],\n        }).then(() => {\n            window.location = this.paymentContext['landingRoute'];\n        }).catch(error => {\n            if (error instanceof RPCError) {\n                this._displayErrorDialog(_t(\"Cannot save payment method\"), error.data.message);\n                this._enableButton(); // The button has been disabled before initiating the flow.\n            } else {\n                return Promise.reject(error);\n            }\n        });\n    },\n\n    /**\n     * Make an RPC to initiate the payment flow by creating a new transaction.\n     *\n     * For a provider to do pre-processing work (e.g., perform checks on the form inputs), or to\n     * process the payment flow in its own terms (e.g., re-schedule the RPC to the transaction\n     * route), it must override this method.\n     *\n     * To alter the flow-specific processing, it is advised to override `_processRedirectFlow`,\n     * `_processDirectFlow`, or `_processTokenFlow` instead.\n     *\n     * @private\n     * @param {string} providerCode - The code of the selected payment option's provider.\n     * @param {number} paymentOptionId - The id of the selected payment option.\n     * @param {string} paymentMethodCode - The code of the selected payment method, if any.\n     * @param {string} flow - The payment flow of the selected payment option.\n     * @return {void}\n     */\n    async _initiatePaymentFlow(providerCode, paymentOptionId, paymentMethodCode, flow) {\n        // Create a transaction and retrieve its processing values.\n        await rpc(\n            this.paymentContext['transactionRoute'],\n            this._prepareTransactionRouteParams(),\n        ).then(processingValues => {\n            if (flow === 'redirect') {\n                this._processRedirectFlow(\n                    providerCode, paymentOptionId, paymentMethodCode, processingValues\n                );\n            } else if (flow === 'direct') {\n                this._processDirectFlow(\n                    providerCode, paymentOptionId, paymentMethodCode, processingValues\n                );\n            } else if (flow === 'token') {\n                this._processTokenFlow(\n                    providerCode, paymentOptionId, paymentMethodCode, processingValues\n                );\n            }\n        }).catch(error => {\n            if (error instanceof RPCError) {\n                this._displayErrorDialog(_t(\"Payment processing failed\"), error.data.message);\n                this._enableButton(); // The button has been disabled before initiating the flow.\n            }\n            return Promise.reject(error);\n        });\n    },\n\n    /**\n     * Prepare the params for the RPC to the transaction route.\n     *\n     * @private\n     * @return {object} The transaction route params.\n     */\n    _prepareTransactionRouteParams() {\n        let transactionRouteParams = {\n            'provider_id': this.paymentContext.providerId,\n            'payment_method_id': this.paymentContext.paymentMethodId ?? null,\n            'token_id': this.paymentContext.tokenId ?? null,\n            'amount': this.paymentContext['amount'] !== undefined\n                ? parseFloat(this.paymentContext['amount']) : null,\n            'flow': this.paymentContext['flow'],\n            'tokenization_requested': this.paymentContext['tokenizationRequested'],\n            'landing_route': this.paymentContext['landingRoute'],\n            'is_validation': this.paymentContext['mode'] === 'validation',\n            'access_token': this.paymentContext['accessToken'],\n            'csrf_token': odoo.csrf_token,\n        };\n        // Generic payment flows (i.e., that are not attached to a document) require extra params.\n        if (this.paymentContext['transactionRoute'] === '/payment/transaction') {\n            Object.assign(transactionRouteParams, {\n                'currency_id': this.paymentContext['currencyId']\n                    ? parseInt(this.paymentContext['currencyId']) : null,\n                'partner_id': parseInt(this.paymentContext['partnerId']),\n                'reference_prefix': this.paymentContext['referencePrefix']?.toString(),\n            });\n        }\n        return transactionRouteParams;\n    },\n\n    /**\n     * Redirect the customer by submitting the redirect form included in the processing values.\n     *\n     * @private\n     * @param {string} providerCode - The code of the selected payment option's provider.\n     * @param {number} paymentOptionId - The id of the selected payment option.\n     * @param {string} paymentMethodCode - The code of the selected payment method, if any.\n     * @param {object} processingValues - The processing values of the transaction.\n     * @return {void}\n     */\n    _processRedirectFlow(providerCode, paymentOptionId, paymentMethodCode, processingValues) {\n        // Create and configure the form element with the content rendered by the server.\n        const div = document.createElement('div');\n        div.innerHTML = processingValues['redirect_form_html'];\n        const redirectForm = div.querySelector('form');\n        redirectForm.setAttribute('id', 'o_payment_redirect_form');\n        redirectForm.setAttribute('target', '_top');  // Ensures redirections when in an iframe.\n\n        // Submit the form.\n        document.body.appendChild(redirectForm);\n        redirectForm.submit();\n    },\n\n   /**\n     * Process the provider-specific implementation of the direct payment flow.\n     *\n     * @private\n     * @param {string} providerCode - The code of the selected payment option's provider.\n     * @param {number} paymentOptionId - The id of the selected payment option.\n     * @param {string} paymentMethodCode - The code of the selected payment method, if any.\n     * @param {object} processingValues - The processing values of the transaction.\n     * @return {void}\n     */\n    _processDirectFlow(providerCode, paymentOptionId, paymentMethodCode, processingValues) {},\n\n    /**\n     * Redirect the customer to the status route.\n     *\n     * @private\n     * @param {string} providerCode - The code of the selected payment option's provider.\n     * @param {number} paymentOptionId - The id of the selected payment option.\n     * @param {string} paymentMethodCode - The code of the selected payment method, if any.\n     * @param {object} processingValues - The processing values of the transaction.\n     * @return {void}\n     */\n    _processTokenFlow(providerCode, paymentOptionId, paymentMethodCode, processingValues) {\n        // The flow is already completed as payments by tokens are immediately processed.\n        window.location = '/payment/status';\n    },\n\n    /**\n     * Archive the provided token.\n     *\n     * @private\n     * @param {number} tokenId - The id of the token whose deletion was requested.\n     * @return {void}\n     */\n    _archiveToken(tokenId) {\n        rpc('/payment/archive_token', {\n            'token_id': tokenId,\n        }).then(() => {\n            browser.location.reload();\n        }).catch(error => {\n            if (error instanceof RPCError) {\n                this._displayErrorDialog(\n                    _t(\"Cannot delete payment method\"), error.data.message\n                );\n            } else {\n                return Promise.reject(error);\n            }\n        });\n    },\n\n    // #=== GETTERS ===#\n\n    /**\n     * Determine and return the inline form of the selected payment option.\n     *\n     * @private\n     * @param {HTMLInputElement} radio - The radio button linked to the payment option.\n     * @return {Element | null} The inline form of the selected payment option, if any.\n     */\n    _getInlineForm(radio) {\n        const inlineFormContainer = radio.closest('[name=\"o_payment_option\"]');\n        return inlineFormContainer?.querySelector('[name=\"o_payment_inline_form\"]');\n    },\n\n    /**\n     * Determine and return the payment flow of the selected payment option.\n     *\n     * As some providers implement both direct payments and the payment with redirection flow, we\n     * cannot infer it from the radio button only. The radio button indicates only whether the\n     * payment option is a token. If not, the payment context is looked up to determine whether the\n     * flow is 'direct' or 'redirect'.\n     *\n     * @private\n     * @param {HTMLInputElement} radio - The radio button linked to the payment option.\n     * @return {string} The flow of the selected payment option: 'redirect', 'direct' or 'token'.\n     */\n    _getPaymentFlow(radio) {\n        // The flow is read from the payment context too in case it was forced in a custom implem.\n        if (this._getPaymentOptionType(radio) === 'token' || this.paymentContext.flow === 'token') {\n            return 'token';\n        } else if (this.paymentContext.flow === 'redirect') {\n            return 'redirect';\n        } else {\n            return 'direct';\n        }\n    },\n\n    /**\n     * Determine and return the code of the selected payment method.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment method.\n     * @return {string} The code of the selected payment method.\n     */\n    _getPaymentMethodCode(radio) {\n        return radio.dataset['paymentMethodCode'];\n    },\n\n    /**\n     * Determine and return the id of the selected payment option.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment option.\n     * @return {number} The id of the selected payment option.\n     */\n    _getPaymentOptionId(radio) {\n        return Number(radio.dataset['paymentOptionId']);\n    },\n\n    /**\n     * Determine and return the type of the selected payment option.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment option.\n     * @return {string} The type of the selected payment option: 'token' or 'payment_method'.\n     */\n    _getPaymentOptionType(radio) {\n        return radio.dataset['paymentOptionType'];\n    },\n\n    /**\n     * Determine and return the id of the provider of the selected payment option.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment option.\n     * @return {number} The id of the provider of the selected payment option.\n     */\n    _getProviderId(radio) {\n        return Number(radio.dataset['providerId']);\n    },\n\n    /**\n     * Determine and return the code of the provider of the selected payment option.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment option.\n     * @return {string} The code of the provider of the selected payment option.\n     */\n    _getProviderCode(radio) {\n        return radio.dataset['providerCode'];\n    },\n\n    /**\n     * Determine and return the state of the provider of the selected payment option.\n     *\n     * @private\n     * @param {HTMLElement} radio - The radio button linked to the payment option.\n     * @return {string} The state of the provider of the selected payment option.\n     */\n    _getProviderState(radio) {\n        return radio.dataset['providerState'];\n    },\n\n});\n\nexport default publicWidget.registry.PaymentForm;\n", "/** @odoo-module **/\n\nimport publicWidget from '@web/legacy/js/public/public_widget';\nimport { ConnectionLostError, rpc, RPCError } from '@web/core/network/rpc';\n\npublicWidget.registry.PaymentPostProcessing = publicWidget.Widget.extend({\n    selector: 'div[name=\"o_payment_status\"]',\n\n    timeout: 0,\n    pollCount: 0,\n\n    async start() {\n        this._poll();\n        return this._super.apply(this, arguments);\n    },\n\n    _poll() {\n        this._updateTimeout();\n        setTimeout(() => {\n            // Fetch the post-processing values from the server.\n            const self = this;\n            rpc('/payment/status/poll', {\n                'csrf_token': odoo.csrf_token,\n            }).then(postProcessingValues => {\n                let {provider_code, state, landing_route} = postProcessingValues;\n\n                // Redirect the user to the landing route if the transaction reached a final state.\n                if (self._getFinalStates(provider_code).has(state)) {\n                    window.location = landing_route;\n                } else {\n                    self._poll();\n                }\n            }).catch(error => {\n                const isRetryError = error instanceof RPCError && error.data.message === 'retry';\n                const isConnectionLostError = error instanceof ConnectionLostError;\n                if (isRetryError || isConnectionLostError) {\n                    self._poll();\n                }\n                if (!isRetryError) {\n                    throw error;\n                }\n            });\n        }, this.timeout);\n    },\n\n    _getFinalStates(providerCode) {\n        return new Set(['authorized', 'done', 'cancel', 'error']);\n    },\n\n    _updateTimeout() {\n        if (this.pollCount >= 1 && this.pollCount < 10) {\n            this.timeout = 3000;\n        }\n        if (this.pollCount >= 10 && this.pollCount < 20) {\n            this.timeout = 10000;\n        }\n        else if (this.pollCount >= 20) {\n            this.timeout = 30000;\n        }\n        this.pollCount++;\n    },\n});\n\nexport default publicWidget.registry.PaymentPostProcessing;\n", "/** @odoo-module **/\n\nimport PaymentForm from \"@payment/js/payment_form\";\n\nPaymentForm.include({\n    /**\n     * Set whether we are paying an installment before submitting.\n     *\n     * @override method from payment.payment_form\n     * @private\n     * @param {Event} ev\n     * @returns {void}\n     */\n    async _submitForm(ev) {\n        ev.stopPropagation();\n        ev.preventDefault();\n\n        const paymentDialog = this.el.closest(\"#pay_with\");\n        const chosenPaymentDetails = paymentDialog\n            ? paymentDialog.querySelector(\".o_btn_payment_tab.active\")\n            : null;\n        if (chosenPaymentDetails){\n            if (chosenPaymentDetails.id === \"o_payment_installments_tab\") {\n                this.paymentContext.amount = parseFloat(this.paymentContext.invoiceNextAmountToPay);\n            } else {\n                this.paymentContext.amount = parseFloat(this.paymentContext.invoiceAmountDue);\n            }\n        }\n        await this._super(...arguments);\n    },\n\n        /**\n     * Prepare the params for the RPC to the transaction route.\n     *\n     * @override method from payment.payment_form\n     * @private\n     * @return {object} The transaction route params.\n     */\n        _prepareTransactionRouteParams() {\n            const transactionRouteParams =  this._super(...arguments);\n            transactionRouteParams.payment_reference = this.paymentContext.paymentReference;\n            return transactionRouteParams;\n        },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\n\npublicWidget.registry.PortalInvoicePagePayment = publicWidget.Widget.extend({\n    selector: \"#portal_pay\",\n\n    /**\n     * Show the payment dialog when the context parameter is set.\n     *\n     * @returns {void}\n     */\n    start() {\n        if (this.el.dataset.payment) {\n            const paymentDialog = new Modal(\"#pay_with\");\n            paymentDialog.show();\n        }\n        return this._super(...arguments);\n    },\n});\n", "/** @odoo-module **/\n\nimport {_t} from \"@web/core/l10n/translation\";\nimport {deserializeDateTime} from \"@web/core/l10n/dates\";\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\n\nconst {DateTime} = luxon;\n\npublicWidget.registry.PortalMyInvoicesPaymentList = publicWidget.Widget.extend({\n    selector: \".o_portal_my_doc_table\",\n\n    start() {\n        this._setDueDateLabel();\n        return this._super(...arguments);\n    },\n\n    _setDueDateLabel() {\n        const dueDateLabels = this.el.querySelectorAll(\".o_portal_invoice_due_date\");\n        const today = DateTime.now().startOf(\"day\");\n        dueDateLabels.forEach((label) => {\n            const dateTime = deserializeDateTime(label.getAttribute(\"datetime\")).startOf('day');\n            const diff = dateTime.diff(today).as(\"days\");\n\n            let dueDateLabel = \"\";\n\n            if (diff === 0) {\n                dueDateLabel = _t(\"due today\");\n            } else if (diff > 0) {\n                dueDateLabel = _t(\"due in %s day(s)\", Math.abs(diff).toFixed());\n            } else {\n                dueDateLabel = _t(\"%s day(s) overdue\", Math.abs(diff).toFixed());\n            }\n            // We use `.createTextNode()` to escape possible HTML in translations (XSS)\n            label.replaceChildren(document.createTextNode(dueDateLabel));\n        });\n    },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport PortalSidebar from \"@portal/js/portal_sidebar\";\nimport { uniqueId } from \"@web/core/utils/functions\";\n\npublicWidget.registry.SalePortalSidebar = PortalSidebar.extend({\n    selector: '.o_portal_sale_sidebar',\n\n    /**\n     * @constructor\n     */\n    init: function (parent, options) {\n        this._super.apply(this, arguments);\n        this.authorizedTextTag = ['em', 'b', 'i', 'u'];\n        this.spyWatched = $('body[data-target=\".navspy\"]');\n    },\n    /**\n     * @override\n     */\n    start: function () {\n        var def = this._super.apply(this, arguments);\n        var $spyWatcheElement = this.$el.find('[data-id=\"portal_sidebar\"]');\n        this._setElementId($spyWatcheElement);\n        // Nav Menu ScrollSpy\n        this._generateMenu();\n        // After signature, automatically open the popup for payment\n        const searchParams = new URLSearchParams(window.location.search.substring(1));\n        const payNowButton = this.$('#o_sale_portal_paynow')\n        if (searchParams.get(\"allow_payment\") === \"yes\" && payNowButton) {\n            payNowButton[0].click();\n        }\n        return def;\n    },\n\n    //--------------------------------------------------------------------------\n    // Private\n    //---------------------------------------------------------------------------\n\n    /**\n     * create an unique id and added as a attribute of spyWatched element\n     *\n     * @private\n     * @param {string} prefix\n     * @param {Object} $el\n     *\n     */\n    _setElementId: function (prefix, $el) {\n        var id = uniqueId(prefix);\n        this.spyWatched.find($el).attr('id', id);\n        return id;\n    },\n    /**\n     * generate the new spy menu\n     *\n     * @private\n     *\n     */\n    _generateMenu: function () {\n        var self = this,\n            lastLI = false,\n            lastUL = null,\n            $bsSidenav = this.$el.find('.bs-sidenav');\n\n        $(\"#quote_content [id^=quote_header_], #quote_content [id^=quote_]\", this.spyWatched).attr(\"id\", \"\");\n        this.spyWatched.find(\"#quote_content h2, #quote_content h3\").toArray().forEach((el) => {\n            var id, text;\n            switch (el.tagName.toLowerCase()) {\n                case \"h2\":\n                    id = self._setElementId('quote_header_', el);\n                    text = self._extractText($(el));\n                    if (!text) {\n                        break;\n                    }\n                    lastLI = $(\"<li class='nav-item'>\").append($('<a class=\"nav-link p-0\" href=\"#' + id + '\"/>').text(text)).appendTo($bsSidenav);\n                    lastUL = false;\n                    break;\n                case \"h3\":\n                    id = self._setElementId('quote_', el);\n                    text = self._extractText($(el));\n                    if (!text) {\n                        break;\n                    }\n                    if (lastLI) {\n                        if (!lastUL) {\n                            lastUL = $(\"<ul class='nav flex-column'>\").appendTo(lastLI);\n                        }\n                        $(\"<li class='nav-item'>\").append($('<a class=\"nav-link p-0\" href=\"#' + id + '\"/>').text(text)).appendTo(lastUL);\n                    }\n                    break;\n            }\n            el.setAttribute('data-anchor', true);\n        });\n        this.trigger_up('widgets_start_request', {$target: $bsSidenav});\n    },\n    /**\n     * extract text of menu title for sidebar\n     *\n     * @private\n     * @param {Object} $node\n     *\n     */\n    _extractText: function ($node) {\n        var self = this;\n        var rawText = [];\n        $node.contents().toArray().forEach((el) => {\n            var current = $(el);\n            if ($.trim(current.text())) {\n                var tagName = current.prop(\"tagName\");\n                if (\n                    typeof tagName === \"undefined\" ||\n                    (typeof tagName !== \"undefined\" &&\n                        self.authorizedTextTag.includes(tagName.toLowerCase()))\n                ) {\n                    rawText.push($.trim(current.text()));\n                }\n            }\n        });\n        return rawText.join(' ');\n    },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\n\npublicWidget.registry.PortalPrepayment = publicWidget.Widget.extend({\n    selector: '.o_portal_sale_sidebar',\n    events: Object.assign({}, publicWidget.Widget.prototype.events, {\n        'click button[name=\"o_sale_portal_amount_prepayment_button\"]': '_onClickAmountPrepaymentButton',\n        'click button[name=\"o_sale_portal_amount_total_button\"]': '_onClickAmountTotalButton',\n    }),\n\n    start: async function () {\n        this.AmountTotalButton = document.querySelector(\n            'button[name=\"o_sale_portal_amount_total_button\"]'\n        );\n        this.AmountPrepaymentButton = document.querySelector(\n            'button[name=\"o_sale_portal_amount_prepayment_button\"]'\n        );\n\n        if (!this.AmountTotalButton) {\n            // Button not available in dom => confirmed SO or partial payment not enabled on this SO\n            // this widget has nothing to manage\n            return;\n        }\n\n        const params = new URLSearchParams(window.location.search);\n        const isPartialPayment = params.has('downpayment') ? params.get('downpayment') === 'true': true;\n        const showPaymentModal = params.get('showPaymentModal') === 'true';\n\n        // Prepare the modal to show if the down payment amount is selected or not.\n        if (isPartialPayment) {\n            this._onClickAmountPrepaymentButton(false);\n        } else {\n            this._onClickAmountTotalButton(false);\n        }\n\n        // When updating the amount re-open the modal.\n        if (showPaymentModal) {\n            const payNowButton = this.$('#o_sale_portal_paynow')[0];\n            payNowButton && payNowButton.click();\n        }\n    },\n\n    _onClickAmountPrepaymentButton: function (doReload=true) {\n        this.AmountTotalButton?.classList.remove('active');\n        this.AmountPrepaymentButton?.classList.add('active');\n\n        if (doReload) {\n            this._reloadAmount(true);\n        } else {\n            this.$('span[id=\"o_sale_portal_use_amount_total\"]').hide();\n            this.$('span[id=\"o_sale_portal_use_amount_prepayment\"]').show();\n        }\n    },\n\n    _onClickAmountTotalButton: function(doReload=true) {\n        this.AmountPrepaymentButton?.classList.remove('active');\n        this.AmountTotalButton?.classList.add('active');\n\n        if (doReload) {\n            this._reloadAmount(false);\n        } else {\n            this.$('span[id=\"o_sale_portal_use_amount_total\"]').show();\n            this.$('span[id=\"o_sale_portal_use_amount_prepayment\"]').hide();\n        }\n    },\n\n    _reloadAmount: function (partialPayment) {\n        const searchParams = new URLSearchParams(window.location.search);\n\n        if (partialPayment) {\n            searchParams.set('downpayment', true);\n        } else {\n            searchParams.set('downpayment', false);\n        }\n        searchParams.set('showPaymentModal', true);\n\n        window.location.search = searchParams.toString();\n    },\n});\nexport default publicWidget.registry.PortalPrepayment;\n", "/** @odoo-module */\n\nimport { PortalHomeCounters } from '@portal/js/portal';\n\nPortalHomeCounters.include({\n    /**\n     * @override\n     */\n    _getCountersAlwaysDisplayed() {\n        return this._super(...arguments).concat(['order_count']);\n    },\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { rpc } from \"@web/core/network/rpc\";\n\npublicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({\n    selector: '.o_portal_sale_sidebar',\n    events: {\n        'click a.js_update_line_json': '_onClickOptionQuantityButton',\n        'click a.js_add_optional_products': '_onClickAddOptionalProduct',\n        'change .js_quantity': '_onChangeOptionQuantity',\n    },\n\n    /**\n     * @override\n     */\n    async start() {\n        await this._super(...arguments);\n        this.orderDetail = this.$el.find('table#sales_order_table').data();\n    },\n\n    /**\n     * Calls the route to get updated values of the line and order\n     * when the quantity of a product has changed\n     *\n     * @private\n     * @param {integer} order_id\n     * @param {Object} params\n     * @return {Deferred}\n     */\n     _callUpdateLineRoute(order_id, params) {\n        return rpc(\"/my/orders/\" + order_id + \"/update_line_dict\", params);\n    },\n\n    /**\n     * Refresh the UI of the order details\n     *\n     * @private\n     * @param {Object} data: contains order html details\n     */\n    _refreshOrderUI(data){\n        window.location.reload();\n    },\n\n    /**\n     * Process the change in line quantity\n     *\n     * @private\n     * @param {Event} ev\n     */\n    async _onChangeOptionQuantity(ev) {\n        ev.preventDefault();\n        let self = this,\n            $target = $(ev.currentTarget),\n            quantity = parseInt($target.val());\n\n        const result = await this._callUpdateLineRoute(self.orderDetail.orderId, {\n            'line_id': $target.data('lineId'),\n            'input_quantity': quantity >= 0 ? quantity : false,\n            'access_token': self.orderDetail.token\n        });\n        this._refreshOrderUI(result);\n    },\n\n    /**\n     * Reacts to the click on the -/+ buttons\n     *\n     * @private\n     * @param {Event} ev\n     */\n    async _onClickOptionQuantityButton(ev) {\n        ev.preventDefault();\n        let self = this,\n            $target = $(ev.currentTarget);\n\n        const result = await this._callUpdateLineRoute(self.orderDetail.orderId, {\n            'line_id': $target.data('lineId'),\n            'remove': $target.data('remove'),\n            'unlink': $target.data('unlink'),\n            'access_token': self.orderDetail.token\n        });\n        this._refreshOrderUI(result);\n    },\n\n    /**\n     * Triggered when optional product added to order from portal.\n     *\n     * @private\n     * @param {Event} ev\n     */\n     _onClickAddOptionalProduct(ev) {\n        ev.preventDefault();\n        let self = this,\n            $target = $(ev.currentTarget);\n\n        // to avoid double click on link with href.\n        $target.css('pointer-events', 'none');\n\n        rpc(\n            \"/my/orders/\" + self.orderDetail.orderId + \"/add_option/\" + $target.data('optionId'),\n            {access_token: self.orderDetail.token}\n        ).then((data) => {\n            this._refreshOrderUI(data);\n        });\n    },\n\n});\n", "/** @odoo-module **/\n\n    import publicWidget from \"@web/legacy/js/public/public_widget\";\n    import { loadJS } from \"@web/core/assets\";\n    /* global OdooFin */\n\n    publicWidget.registry.OnlineSyncPortal = publicWidget.Widget.extend({\n        selector: '.oe_online_sync',\n        events: Object.assign({}, {\n            'click #renew_consent_button': '_onRenewConsent',\n        }),\n\n        OdooFinConnector: function (parent, action) {\n            // Ensure that the proxyMode is valid\n            const modeRegexp = /^[a-z0-9-_]+$/i;\n            if (!modeRegexp.test(action.params.proxyMode)) {\n                return;\n            }\n            const url = 'https://' + action.params.proxyMode + '.odoofin.com/proxy/v1/odoofin_link';\n\n            loadJS(url)\n                .then(() => {\n                    // Create and open the iframe\n                    const params = {\n                        data: action.params,\n                        proxyMode: action.params.proxyMode,\n                        onEvent: function (event, data) {\n                            switch (event) {\n                                case 'success':\n                                    const processUrl = window.location.pathname + '/complete' + window.location.search;\n                                    $('.js_reconnect').toggleClass('d-none');\n                                    $.post(processUrl, {csrf_token: odoo.csrf_token});\n                                default:\n                                    return;\n                            }\n                        },\n                    };\n                    OdooFin.create(params);\n                    OdooFin.open();\n                });\n            return;\n        },\n\n        /**\n         * @private\n         * @param {Event} ev\n         */\n        _onRenewConsent: async function (ev) {\n            ev.preventDefault();\n            const action = JSON.parse($(ev.currentTarget).attr('iframe-params'));\n            return this.OdooFinConnector(this, action);\n        },\n    });\n\n    export default {\n        OnlineSyncPortal: publicWidget.registry.OnlineSyncPortal,\n    };\n", "/** @odoo-module **/\n\nimport { _t } from \"@web/core/l10n/translation\";\nimport { markup } from \"@odoo/owl\";\nimport { InputConfirmationDialog } from \"@portal/js/components/input_confirmation_dialog/input_confirmation_dialog\";\nimport { handleCheckIdentity } from \"@portal/js/portal_security\";\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { browser } from \"@web/core/browser/browser\";\nimport { user } from \"@web/core/user\";\n\n/**\n * Replaces specific <field> elements by normal HTML, strip out the rest entirely\n */\nfunction fromField(f, record) {\n    switch (f.getAttribute('name')) {\n    case 'qrcode':\n        const qrcode = document.createElement('img');\n        qrcode.setAttribute('class', 'img img-fluid');\n        qrcode.setAttribute('src', 'data:image/png;base64,' + record['qrcode']);\n        return qrcode;\n    case 'url':\n        const url = document.createElement('a');\n        url.setAttribute('href', record['url']);\n        url.textContent = f.getAttribute('text') || record['url'];\n        return url;\n    case 'code':\n        const code = document.createElement('input');\n        code.setAttribute('name', 'code');\n        code.setAttribute('class', 'form-control col-10 col-md-6');\n        code.setAttribute('placeholder', '6-digit code');\n        code.required = true;\n        code.maxLength = 6;\n        code.minLength = 6;\n        return code;\n    case 'secret':\n        // As CopyClipboard wizard is backend only, mimic his behaviour to use it in frontend.\n        // Field\n        const secretSpan = document.createElement('span');\n        secretSpan.setAttribute('name', 'secret');\n        secretSpan.setAttribute('class', 'o_field_copy_url');\n        secretSpan.textContent = record['secret'];\n\n        // Copy Button\n        const copySpanIcon = document.createElement('span');\n        copySpanIcon.setAttribute('class', 'fa fa-clipboard');\n        const copySpanText = document.createElement('span');\n        copySpanText.textContent = _t(' Copy');\n\n        const copyButton = document.createElement('button');\n        copyButton.setAttribute('class', 'btn btn-sm btn-primary o_clipboard_button o_btn_char_copy py-0 px-2');\n        copyButton.onclick = async function(event) {\n            event.preventDefault();\n            $(copyButton).tooltip({title: _t(\"Copied!\"), trigger: \"manual\", placement: \"bottom\"});\n            await browser.navigator.clipboard.writeText($(secretSpan)[0].innerText);\n            $(copyButton).tooltip('show');\n            setTimeout(() => $(copyButton).tooltip(\"hide\"), 800);\n        };\n\n        copyButton.appendChild(copySpanIcon);\n        copyButton.appendChild(copySpanText);\n\n        // CopyClipboard Div\n        const secretDiv = document.createElement('div');\n        secretDiv.setAttribute('class', 'o_field_copy d-flex justify-content-center align-items-center');\n        secretDiv.appendChild(secretSpan);\n        secretDiv.appendChild(copyButton);\n\n        return secretDiv;\n    default: // just display the field's data\n        return document.createTextNode(record[f.getAttribute('name')] || '');\n    }\n}\n\n/**\n * Apparently chrome literally absolutely can't handle parsing XML and using\n * those nodes in an HTML document (even when parsing as application/xhtml+xml),\n * this results in broken rendering and a number of things not working (e.g.\n * classes) without any specific warning in the console or anything, things are\n * just broken with no indication of why.\n *\n * So... rebuild the entire f'ing body using document.createElement to ensure\n * we have HTML elements.\n *\n * This is a recursive implementation so it's not super efficient but the views\n * to fixup *should* be relatively simple.\n */\nfunction fixupViewBody(oldNode, record) {\n    let qrcode = null, code = null, node = null;\n\n    switch (oldNode.nodeType) {\n        case 1: // element\n            if (oldNode.tagName === 'field') {\n                node = fromField(oldNode, record);\n                switch (oldNode.getAttribute('name')) {\n                case 'qrcode':\n                    qrcode = node;\n                    break;\n                case 'code':\n                    code = node;\n                    break\n                }\n                break; // no need to recurse here\n            }\n            node = document.createElement(oldNode.tagName);\n            for(let i=0; i<oldNode.attributes.length; ++i) {\n                const attr = oldNode.attributes[i];\n                node.setAttribute(attr.name, attr.value);\n            }\n            for(let j=0; j<oldNode.childNodes.length; ++j) {\n                const [ch, qr, co] = fixupViewBody(oldNode.childNodes[j], record);\n                if (ch) { node.appendChild(ch); }\n                if (qr) { qrcode = qr; }\n                if (co) { code = co; }\n            }\n            break;\n        case 3: case 4: // text, cdata\n            node = document.createTextNode(oldNode.data);\n            break;\n        default:\n            // don't care about PI & al\n    }\n\n    return [node, qrcode, code]\n}\n\npublicWidget.registry.TOTPButton = publicWidget.Widget.extend({\n    selector: '#auth_totp_portal_enable',\n    events: {\n        click: '_onClick',\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e) {\n        e.preventDefault();\n\n        const w = await handleCheckIdentity(\n            this.orm.call(\"res.users\", \"action_totp_enable_wizard\", [user.userId]),\n            this.orm,\n            this.dialog\n        );\n\n        if (!w) {\n            // TOTP probably already enabled, just reload page\n            window.location = window.location;\n            return;\n        }\n\n        const {res_model: model, res_id: wizard_id} = w;\n\n        const record = await this.orm.read(model, [wizard_id], []).then(ar => ar[0]);\n\n        const doc = new DOMParser().parseFromString(\n            document.getElementById('totp_wizard_view').textContent,\n            'application/xhtml+xml'\n        );\n\n        const xmlBody = doc.querySelector('sheet *');\n        const [body, ,] = fixupViewBody(xmlBody, record);\n\n        this.call(\"dialog\", \"add\", InputConfirmationDialog, {\n            body: markup(body.outerHTML),\n            onInput: ({ inputEl }) => {\n                inputEl.setCustomValidity(\"\");\n            },\n            confirmLabel: _t(\"Activate\"),\n            confirm: async ({ inputEl }) => {\n                if (!inputEl.reportValidity()) {\n                    inputEl.classList.add(\"is-invalid\");\n                    return false;\n                }\n\n                try {\n                    await this.orm.write(model, [record.id], { code: inputEl.value });\n                    await handleCheckIdentity(\n                        this.orm.call(model, \"enable\", [record.id]),\n                        this.orm,\n                        this.dialog\n                    );\n                } catch (e) {\n                    const errorMessage = (\n                        !e.message ? e.toString()\n                      : !e.message.data ? e.message.message\n                      : e.message.data.message || _t(\"Operation failed for unknown reason.\")\n                    );\n                    inputEl.classList.add(\"is-invalid\");\n                    // show custom validity error message\n                    inputEl.setCustomValidity(errorMessage);\n                    inputEl.reportValidity();\n                    return false;\n                }\n                // reloads page, avoid window.location.reload() because it re-posts forms\n                window.location = window.location;\n            },\n            cancel: () => {},\n        });\n    },\n});\npublicWidget.registry.DisableTOTPButton = publicWidget.Widget.extend({\n    selector: '#auth_totp_portal_disable',\n    events: {\n        click: '_onClick'\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e) {\n        e.preventDefault();\n        await handleCheckIdentity(\n            this.orm.call(\"res.users\", \"action_totp_disable\", [user.userId]),\n            this.orm,\n            this.dialog\n        )\n        window.location = window.location;\n    }\n});\npublicWidget.registry.RevokeTrustedDeviceButton = publicWidget.Widget.extend({\n    selector: '#totp_wizard_view + * .fa.fa-trash.text-danger',\n    events: {\n        click: '_onClick'\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e){\n        e.preventDefault();\n        await handleCheckIdentity(\n            this.orm.call(\"auth_totp.device\", \"remove\", [parseInt(this.el.id)]),\n            this.orm,\n            this.dialog\n        );\n        window.location = window.location;\n    }\n});\npublicWidget.registry.RevokeAllTrustedDevicesButton = publicWidget.Widget.extend({\n    selector: '#auth_totp_portal_revoke_all_devices',\n    events: {\n        click: '_onClick'\n    },\n\n    init() {\n        this._super(...arguments);\n        this.orm = this.bindService(\"orm\");\n        this.dialog = this.bindService(\"dialog\");\n    },\n\n    async _onClick(e){\n        e.preventDefault();\n        await handleCheckIdentity(\n            this.orm.call(\"res.users\", \"revoke_all_devices\", [user.userId]),\n            this.orm,\n            this.dialog\n        );\n        window.location = window.location;\n    }\n});\n", "/** @odoo-module **/\n\nimport publicWidget from \"@web/legacy/js/public/public_widget\";\nimport { rpc } from \"@web/core/network/rpc\";\n\npublicWidget.registry.UnsplashBeacon = publicWidget.Widget.extend({\n    // /!\\ To adapt the day the beacon makes sense for backend customizations\n    selector: '#wrapwrap',\n\n    /**\n     * @override\n     */\n    start: function () {\n        var unsplashImages = Array.from(this.$('img[src*=\"/unsplash/\"]')).map((img) => {\n            // get image id from URL (`http://www.domain.com:1234/unsplash/xYdf5feoI/lion.jpg` -> `xYdf5feoI`)\n            return img.src.split('/unsplash/')[1].split('/')[0];\n        });\n        if (unsplashImages.length) {\n            rpc('/web_unsplash/get_app_id').then(function (appID) {\n                if (!appID) {\n                    return;\n                }\n                $.get('https://views.unsplash.com/v', {\n                    'photo_id': unsplashImages.join(','),\n                    'app_id': appID,\n                });\n            });\n        }\n        return this._super.apply(this, arguments);\n    },\n});\n"], "file": "/web/assets/e39357c/web.assets_frontend_lazy.js", "sourceRoot": "../../../"}