)]}' {"version": 3, "sources": ["/website/static/src/js/editor/editor.js", "/website/static/src/js/editor/snippets.editor.js", "/website/static/src/js/editor/snippets.options.js", "/website/static/src/snippets/s_facebook_page/options.js", "/website/static/src/snippets/s_image_gallery/options.js", "/website/static/src/snippets/s_countdown/options.js", "/website/static/src/snippets/s_masonry_block/options.js", "/website/static/src/snippets/s_popup/options.js", "/website/static/src/snippets/s_product_catalog/options.js", "/website/static/src/snippets/s_chart/options.js", "/website/static/src/snippets/s_rating/options.js", "/website/static/src/snippets/s_tabs/options.js", "/website/static/src/snippets/s_progress_bar/options.js", "/website/static/src/snippets/s_blockquote/options.js", "/website/static/src/snippets/s_showcase/options.js", "/website/static/src/snippets/s_table_of_content/options.js", "/website/static/src/snippets/s_timeline/options.js", "/website/static/src/snippets/s_media_list/options.js", "/website/static/src/snippets/s_google_map/options.js", "/website/static/src/snippets/s_map/options.js", "/website/static/src/snippets/s_dynamic_snippet/options.js", "/website/static/src/snippets/s_dynamic_snippet_carousel/options.js", "/website/static/src/snippets/s_embed_code/options.js", "/website/static/src/snippets/s_website_form/options.js", "/website/static/src/snippets/s_searchbar/options.js", "/website/static/src/js/editor/wysiwyg.js", "/website/static/src/js/editor/widget_link.js", "/website/static/src/js/widgets/media.js", "/website/static/src/js/widgets/link_popover_widget.js", "/website_payment/static/src/snippets/s_donation/options.js", "/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js", "/website_blog/static/src/js/options.js", "/website_blog/static/src/js/wysiwyg.js", "/website_blog/static/src/snippets/s_blog_posts/options.js", "/theme_prime/static/src/js/editor/snippets.editor.js", "/website_mass_mailing/static/src/js/wysiwyg.js", "/website_mass_mailing/static/src/js/website_mass_mailing.editor.js"], "mappings": "AAAA;;;;;;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;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC7hHA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvLA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACloBA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACrJA;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;ACdA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACpJA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;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;AC7CA;;;;;;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;ACrIA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;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;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;;;;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;ACzUA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;;;;ACnGA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC9nDA;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC5aA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;ACdA;;;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;ACxFA;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;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;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;AClGA;;;;;;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", "sourcesContent": ["odoo.define('website.editor', function (require) {\n'use strict';\n\nvar weWidgets = require('web_editor.widget');\nvar wUtils = require('website.utils');\n\nweWidgets.LinkDialog.include({\n /**\n * Allows the URL input to propose existing website pages.\n *\n * @override\n */\n start: async function () {\n const result = await this._super.apply(this, arguments);\n wUtils.autocompleteWithPages(this, this.$('input[name=\"url\"]'));\n return result;\n },\n});\n});\n", "odoo.define('website.snippet.editor', function (require) {\n'use strict';\n\nconst {qweb, _t, _lt} = require('web.core');\nconst Dialog = require('web.Dialog');\nconst publicWidget = require('web.public.widget');\nconst weSnippetEditor = require('web_editor.snippet.editor');\nconst wSnippetOptions = require('website.editor.snippets.options');\nconst OdooEditorLib = require('@web_editor/../lib/odoo-editor/src/utils/utils');\nconst getDeepRange = OdooEditorLib.getDeepRange;\nconst getTraversedNodes = OdooEditorLib.getTraversedNodes;\n\nconst FontFamilyPickerUserValueWidget = wSnippetOptions.FontFamilyPickerUserValueWidget;\n\nweSnippetEditor.SnippetsMenu.include({\n xmlDependencies: (weSnippetEditor.SnippetsMenu.prototype.xmlDependencies || [])\n .concat(['/website/static/src/xml/website.editor.xml']),\n events: _.extend({}, weSnippetEditor.SnippetsMenu.prototype.events, {\n 'click .o_we_customize_theme_btn': '_onThemeTabClick',\n 'click .o_we_animate_text': '_onAnimateTextClick',\n 'click .o_we_highlight_animated_text': '_onHighlightAnimatedTextClick',\n }),\n custom_events: Object.assign({}, weSnippetEditor.SnippetsMenu.prototype.custom_events, {\n 'gmap_api_request': '_onGMapAPIRequest',\n 'gmap_api_key_request': '_onGMapAPIKeyRequest',\n }),\n tabs: _.extend({}, weSnippetEditor.SnippetsMenu.prototype.tabs, {\n THEME: 'theme',\n }),\n optionsTabStructure: [\n ['theme-colors', _lt(\"Theme Colors\")],\n ['theme-options', _lt(\"Theme Options\")],\n ['website-settings', _lt(\"Website Settings\")],\n ],\n\n /**\n * @override\n */\n async start() {\n await this._super(...arguments);\n this.$currentAnimatedText = $();\n\n this.__onSelectionChange = ev => {\n this._toggleAnimatedTextButton();\n };\n this.ownerDocument.addEventListener('selectionchange', this.__onSelectionChange);\n },\n /**\n * @override\n */\n destroy() {\n this._super(...arguments);\n this.ownerDocument.removeEventListener('selectionchange', this.__onSelectionChange);\n document.body.classList.remove('o_animated_text_highlighted');\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeSnippetTemplates: function (html) {\n const $html = $(html);\n const fontVariables = _.map($html.find('we-fontfamilypicker[data-variable]'), el => {\n return el.dataset.variable;\n });\n FontFamilyPickerUserValueWidget.prototype.fontVariables = fontVariables;\n\n const ret = this._super(...arguments);\n\n // TODO adapt in master. This patches the embed code snippet\n // in stable versions.\n const $sbody = this.$snippets.find('[data-snippet=\"s_embed_code\"]');\n if ($sbody.length) {\n $sbody[0].classList.remove('o_half_screen_height');\n $sbody[0].classList.add('pt64', 'pb64');\n }\n\n return ret;\n },\n /**\n * Depending of the demand, reconfigure they gmap key or configure it\n * if not already defined.\n *\n * @private\n * @param {boolean} [reconfigure=false] // TODO name is confusing \"alwaysReconfigure\" is better\n * @param {boolean} [onlyIfUndefined=false] // TODO name is confusing \"configureIfNecessary\" is better\n */\n async _configureGMapAPI({reconfigure, onlyIfUndefined}) {\n if (!reconfigure && !onlyIfUndefined) {\n return false;\n }\n\n const apiKey = await new Promise(resolve => {\n this.getParent().trigger_up('gmap_api_key_request', {\n onSuccess: key => resolve(key),\n });\n });\n const apiKeyValidation = apiKey ? await this._validateGMapAPIKey(apiKey) : {\n isValid: false,\n message: undefined,\n };\n if (!reconfigure && onlyIfUndefined && apiKey && apiKeyValidation.isValid) {\n return false;\n }\n\n let websiteId;\n this.trigger_up('context_get', {\n callback: ctx => websiteId = ctx['website_id'],\n });\n\n function applyError(message) {\n const $apiKeyInput = this.find('#api_key_input');\n const $apiKeyHelp = this.find('#api_key_help');\n $apiKeyInput.addClass('is-invalid');\n $apiKeyHelp.empty().text(message);\n }\n\n const $content = $(qweb.render('website.s_google_map_modal', {\n apiKey: apiKey,\n }));\n if (!apiKeyValidation.isValid && apiKeyValidation.message) {\n applyError.call($content, apiKeyValidation.message);\n }\n\n return new Promise(resolve => {\n let invalidated = false;\n const dialog = new Dialog(this, {\n size: 'medium',\n title: _t(\"Google Map API Key\"),\n buttons: [\n {text: _t(\"Save\"), classes: 'btn-primary', click: async (ev) => {\n const valueAPIKey = dialog.$('#api_key_input').val();\n if (!valueAPIKey) {\n applyError.call(dialog.$el, _t(\"Enter an API Key\"));\n return;\n }\n const $button = $(ev.currentTarget);\n $button.prop('disabled', true);\n const res = await this._validateGMapAPIKey(valueAPIKey);\n if (res.isValid) {\n await this._rpc({\n model: 'website',\n method: 'write',\n args: [\n [websiteId],\n {google_maps_api_key: valueAPIKey},\n ],\n });\n invalidated = true;\n dialog.close();\n } else {\n applyError.call(dialog.$el, res.message);\n }\n $button.prop(\"disabled\", false);\n }},\n {text: _t(\"Cancel\"), close: true}\n ],\n $content: $content,\n });\n dialog.on('closed', this, () => resolve(invalidated));\n dialog.open();\n });\n },\n /**\n * @private\n */\n async _validateGMapAPIKey(key) {\n try {\n const response = await fetch(`https://maps.googleapis.com/maps/api/staticmap?center=belgium&size=10x10&key=${encodeURIComponent(key)}`);\n const isValid = (response.status === 200);\n return {\n isValid: isValid,\n message: !isValid &&\n _t(\"Invalid API Key. The following error was returned by Google:\") + \" \" + (await response.text()),\n };\n } catch (err) {\n return {\n isValid: false,\n message: _t(\"Check your connection and try again\"),\n };\n }\n },\n /**\n * @override\n */\n _getScrollOptions(options = {}) {\n const finalOptions = this._super(...arguments);\n if (!options.offsetElements || !options.offsetElements.$top) {\n const $header = $('#top');\n if ($header.length) {\n finalOptions.offsetElements = finalOptions.offsetElements || {};\n finalOptions.offsetElements.$top = $header;\n }\n }\n return finalOptions;\n },\n /**\n * @private\n * @param {OdooEvent} ev\n * @param {string} gmapRequestEventName\n */\n async _handleGMapRequest(ev, gmapRequestEventName) {\n ev.stopPropagation();\n const reconfigured = await this._configureGMapAPI({\n reconfigure: ev.data.reconfigure,\n onlyIfUndefined: ev.data.configureIfNecessary,\n });\n this.getParent().trigger_up(gmapRequestEventName, {\n refetch: reconfigured,\n editableMode: true,\n onSuccess: key => ev.data.onSuccess(key),\n });\n },\n /**\n * @override\n */\n _updateRightPanelContent: function ({content, tab}) {\n this._super(...arguments);\n this.$('.o_we_customize_theme_btn').toggleClass('active', tab === this.tabs.THEME);\n },\n /**\n * Returns the animated text element wrapping the selection if it exists.\n *\n * @private\n * @return {Element|false}\n */\n _getAnimatedTextElement() {\n const editable = this.options.wysiwyg.$editable[0];\n const animatedTextNode = getTraversedNodes(editable).find(n => n.parentElement.closest(\".o_animated_text\"));\n return animatedTextNode ? animatedTextNode.parentElement.closest('.o_animated_text') : false;\n },\n /**\n * @override\n */\n _addToolbar() {\n this._super(...arguments);\n if (this.options.enableTranslation) {\n this._$toolbarContainer[0].querySelector(\":scope .o_we_animate_text\").classList.add(\"d-none\");\n }\n this.$('#o_we_editor_toolbar_container > we-title > span').after($(`\n
\n `));\n this._toggleAnimatedTextButton();\n this._toggleHighlightAnimatedTextButton();\n },\n /**\n * Activates the button to animate text if the selection is in an\n * animated text element or deactivates the button if not.\n *\n * @private\n */\n _toggleAnimatedTextButton() {\n if (!this._isValidSelection(window.getSelection())) {\n return;\n }\n const animatedText = this._getAnimatedTextElement();\n this.$('.o_we_animate_text').toggleClass('active', !!animatedText);\n this.$currentAnimatedText = animatedText ? $(animatedText) : $();\n },\n /**\n * Displays the button that allows to highlight the animated text if there\n * is animated text in the page.\n *\n * @private\n */\n _toggleHighlightAnimatedTextButton() {\n const $animatedText = this.getEditableArea().find('.o_animated_text');\n this.$('#o_we_editor_toolbar_container .o_we_highlight_animated_text').toggleClass('d-none', !$animatedText.length);\n },\n /**\n * @private\n * @param {Node} node\n * @return {Boolean}\n */\n _isValidSelection(sel) {\n return sel.rangeCount && [...this.getEditableArea()].some(el => el.contains(sel.anchorNode));\n },\n\n /**\n * The goal here is to disable parents editors for `s_popup` snippets\n * since they should not display their parents options.\n * TODO: Update in master to set the `o_no_parent_editor` class in the\n * snippet's XML.\n *\n * @override\n */\n _allowParentsEditors($snippet) {\n return this._super(...arguments) && !$snippet[0].classList.contains(\"s_popup\");\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * @private\n * @param {OdooEvent} ev\n */\n _onGMapAPIRequest(ev) {\n this._handleGMapRequest(ev, 'gmap_api_request');\n },\n /**\n * @private\n * @param {OdooEvent} ev\n */\n _onGMapAPIKeyRequest(ev) {\n this._handleGMapRequest(ev, 'gmap_api_key_request');\n },\n /**\n * @private\n */\n async _onThemeTabClick(ev) {\n // Note: nothing async here but start the loading effect asap\n let releaseLoader;\n try {\n const promise = new Promise(resolve => releaseLoader = resolve);\n this._execWithLoadingEffect(() => promise, false, 0);\n // loader is added to the DOM synchronously\n await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));\n // ensure loader is rendered: first call asks for the (already done) DOM update,\n // second call happens only after rendering the first \"updates\"\n\n if (!this.topFakeOptionEl) {\n let el;\n for (const [elementName, title] of this.optionsTabStructure) {\n const newEl = document.createElement(elementName);\n newEl.dataset.name = title;\n if (el) {\n el.appendChild(newEl);\n } else {\n this.topFakeOptionEl = newEl;\n }\n el = newEl;\n }\n this.bottomFakeOptionEl = el;\n this.el.appendChild(this.topFakeOptionEl);\n }\n\n // Need all of this in that order so that:\n // - the element is visible and can be enabled and the onFocus method is\n // called each time.\n // - the element is hidden afterwards so it does not take space in the\n // DOM, same as the overlay which may make a scrollbar appear.\n this.topFakeOptionEl.classList.remove('d-none');\n const editorPromise = this._activateSnippet($(this.bottomFakeOptionEl));\n releaseLoader(); // because _activateSnippet uses the same mutex as the loader\n releaseLoader = undefined;\n const editor = await editorPromise;\n this.topFakeOptionEl.classList.add('d-none');\n editor.toggleOverlay(false);\n\n this._updateRightPanelContent({\n tab: this.tabs.THEME,\n });\n } catch (e) {\n // Normally the loading effect is removed in case of error during the action but here\n // the actual activity is happening outside of the action, the effect must therefore\n // be cleared in case of error as well\n if (releaseLoader) {\n releaseLoader();\n }\n throw e;\n }\n },\n /**\n * @private\n */\n _onAnimateTextClick(ev) {\n const sel = window.getSelection();\n if (!this._isValidSelection(sel)) {\n return;\n }\n const editable = this.options.wysiwyg.$editable[0];\n const range = getDeepRange(editable, { splitText: true, select: true, correctTripleClick: true });\n if (this.$currentAnimatedText.length) {\n this.$currentAnimatedText.contents().unwrap();\n this.options.wysiwyg.odooEditor.historyResetLatestComputedSelection();\n this._toggleHighlightAnimatedTextButton();\n ev.target.classList.remove('active');\n this.options.wysiwyg.odooEditor.historyStep();\n } else {\n if (sel.getRangeAt(0).collapsed) {\n return;\n }\n const animatedTextEl = document.createElement('span');\n animatedTextEl.classList.add('o_animated_text', 'o_animate', 'o_animate_preview', 'o_anim_fade_in');\n let $snippet = null;\n try {\n range.surroundContents(animatedTextEl);\n $snippet = $(animatedTextEl);\n } catch (e) {\n // This try catch is needed because 'surroundContents' may\n // fail when the range has partially selected a non-Text node.\n if (range.commonAncestorContainer.textContent === range.toString()) {\n const $commonAncestor = $(range.commonAncestorContainer);\n $commonAncestor.wrapInner(animatedTextEl);\n $snippet = $commonAncestor.find('.o_animated_text');\n }\n }\n if ($snippet) {\n $snippet[0].normalize();\n this.trigger_up('activate_snippet', {\n $snippet: $snippet,\n previewMode: false,\n });\n this.options.wysiwyg.odooEditor.historyStep();\n } else {\n this.displayNotification({\n message: _t(\"The current text selection cannot be animated. Try clearing the format and try again.\"),\n type: 'danger',\n sticky: true,\n });\n }\n }\n },\n /**\n * @private\n */\n _onHighlightAnimatedTextClick(ev) {\n $('body').toggleClass('o_animated_text_highlighted');\n $(ev.target).toggleClass('fa-eye fa-eye-slash').toggleClass('text-success');\n },\n});\n\nweSnippetEditor.SnippetEditor.include({\n layoutElementsSelector: [\n weSnippetEditor.SnippetEditor.prototype.layoutElementsSelector,\n '.s_parallax_bg',\n '.o_bg_video_container',\n ].join(','),\n\n /**\n * @override\n */\n getName() {\n if (this.$target[0].closest('[data-oe-field=logo]')) {\n return _t(\"Logo\");\n }\n return this._super(...arguments);\n },\n});\n\n// Edit mode customizations of public widgets.\n\npublicWidget.registry.hoverableDropdown.include({\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n \n /**\n * Hides all opened dropdowns.\n *\n * TODO: Remove in master.\n * @private\n */\n _hideDropdowns() {\n for (const toggleEl of this.el.querySelectorAll('.dropdown.show .dropdown-toggle')) {\n $(toggleEl).dropdown('hide');\n }\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * Called when the page is clicked anywhere.\n * Closes the shown dropdown if the click is outside of it.\n *\n * TODO: Remove in master.\n * @private\n * @param {Event} ev\n */\n _onPageClick(ev) {\n if (ev.target.closest('.dropdown.show')) {\n return;\n }\n this._hideDropdowns();\n },\n /**\n * @override\n */\n _onMouseEnter(ev) {\n if (this.editableMode) {\n // Do not handle hover if another dropdown is opened.\n if (this.el.querySelector('.dropdown.show')) {\n return;\n }\n }\n this._super(...arguments);\n },\n /**\n * @override\n */\n _onMouseLeave(ev) {\n if (this.editableMode) {\n // Cancel handling from view mode.\n return;\n }\n this._super(...arguments);\n },\n});\n});\n", "/* globals google*/\nodoo.define('website.editor.snippets.options', function (require) {\n'use strict';\n\nconst {ColorpickerWidget} = require('web.Colorpicker');\nconst config = require('web.config');\nvar core = require('web.core');\nvar Dialog = require('web.Dialog');\nconst {Markup, sprintf} = require('web.utils');\nconst weUtils = require('web_editor.utils');\nvar options = require('web_editor.snippets.options');\nconst wLinkPopoverWidget = require('@website/js/widgets/link_popover_widget')[Symbol.for(\"default\")];\nconst wUtils = require('website.utils');\nconst {isImageSupportedForStyle} = require('web_editor.image_processing');\nrequire('website.s_popup_options');\nconst {Domain} = require('@web/core/domain');\n\nvar _t = core._t;\nvar qweb = core.qweb;\n\nconst InputUserValueWidget = options.userValueWidgetsRegistry['we-input'];\nconst SelectUserValueWidget = options.userValueWidgetsRegistry['we-select'];\nconst Many2oneUserValueWidget = options.userValueWidgetsRegistry['we-many2one'];\n\noptions.UserValueWidget.include({\n loadMethodsData() {\n this._super(...arguments);\n\n // Method names are sorted alphabetically by default. Exception here:\n // we make sure, customizeWebsiteVariable is considered after\n // customizeWebsiteViews so that the variable is used to show to active\n // value when both methods are used at the same time.\n // TODO find a better way.\n const indexVariable = this._methodsNames.indexOf('customizeWebsiteVariable');\n if (indexVariable >= 0) {\n const indexView = this._methodsNames.indexOf('customizeWebsiteViews');\n if (indexView >= 0) {\n this._methodsNames[indexVariable] = 'customizeWebsiteViews';\n this._methodsNames[indexView] = 'customizeWebsiteVariable';\n }\n }\n },\n});\n\nMany2oneUserValueWidget.include({\n /**\n * @override\n */\n async _getSearchDomain() {\n // Add the current website's domain if the model has a website_id field.\n // Note that the `_rpc` method is cached in Many2X user value widget,\n // see `_rpcCache`.\n const websiteIdField = await this._rpc({\n model: this.options.model,\n method: \"fields_get\",\n args: [[\"website_id\"]],\n });\n const modelHasWebsiteId = !!websiteIdField[\"website_id\"];\n if (modelHasWebsiteId && !this.options.domain.find(arr => arr[0] === \"website_id\")) {\n this.options.domain =\n Domain.and([this.options.domain, wUtils.websiteDomain(this)]).toList();\n }\n return this.options.domain;\n },\n});\n\nconst UrlPickerUserValueWidget = InputUserValueWidget.extend({\n custom_events: _.extend({}, InputUserValueWidget.prototype.custom_events || {}, {\n 'website_url_chosen': '_onWebsiteURLChosen',\n }),\n events: _.extend({}, InputUserValueWidget.prototype.events || {}, {\n 'click .o_we_redirect_to': '_onRedirectTo',\n }),\n\n /**\n * @override\n */\n start: async function () {\n await this._super(...arguments);\n const linkButton = document.createElement('we-button');\n const icon = document.createElement('i');\n icon.classList.add('fa', 'fa-fw', 'fa-external-link')\n linkButton.classList.add('o_we_redirect_to');\n linkButton.title = _t(\"Redirect to URL in a new tab\");\n linkButton.appendChild(icon);\n this.containerEl.appendChild(linkButton);\n this.el.classList.add('o_we_large');\n this.inputEl.classList.add('text-left');\n const options = {\n position: {\n collision: 'flip flipfit',\n },\n classes: {\n \"ui-autocomplete\": 'o_website_ui_autocomplete'\n },\n };\n wUtils.autocompleteWithPages(this, $(this.inputEl), options);\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * Called when the autocomplete change the input value.\n *\n * @private\n * @param {OdooEvent} ev\n */\n _onWebsiteURLChosen: function (ev) {\n this._value = this.inputEl.value;\n this._onUserValueChange(ev);\n },\n /**\n * Redirects to the URL the widget currently holds.\n *\n * @private\n */\n _onRedirectTo: function () {\n if (this._value) {\n window.open(this._value, '_blank');\n }\n },\n});\n\nconst FontFamilyPickerUserValueWidget = SelectUserValueWidget.extend({\n xmlDependencies: (SelectUserValueWidget.prototype.xmlDependencies || [])\n .concat(['/website/static/src/xml/website.editor.xml']),\n events: _.extend({}, SelectUserValueWidget.prototype.events || {}, {\n 'click .o_we_add_google_font_btn': '_onAddGoogleFontClick',\n 'click .o_we_delete_google_font_btn': '_onDeleteGoogleFontClick',\n }),\n fontVariables: [], // Filled by editor menu when all options are loaded\n\n /**\n * @override\n */\n start: async function () {\n const style = window.getComputedStyle(document.documentElement);\n const nbFonts = parseInt(weUtils.getCSSVariableValue('number-of-fonts', style));\n // User fonts served by google server.\n const googleFontsProperty = weUtils.getCSSVariableValue('google-fonts', style);\n this.googleFonts = googleFontsProperty ? googleFontsProperty.split(/\\s*,\\s*/g) : [];\n this.googleFonts = this.googleFonts.map(font => font.substring(1, font.length - 1)); // Unquote\n // Local user fonts.\n const googleLocalFontsProperty = weUtils.getCSSVariableValue('google-local-fonts', style);\n this.googleLocalFonts = googleLocalFontsProperty ?\n googleLocalFontsProperty.slice(1, -1).split(/\\s*,\\s*/g) : [];\n // If a same font exists both remotely and locally, we remove the remote\n // font to prioritize the local font. The remote one will never be\n // displayed or loaded as long as the local one exists.\n this.googleFonts = this.googleFonts.filter(font => {\n const localFonts = this.googleLocalFonts.map(localFont => localFont.split(\":\")[0]);\n return localFonts.indexOf(`'${font}'`) === -1;\n });\n this.allFonts = [];\n\n await this._super(...arguments);\n\n const fontEls = [];\n const methodName = this.el.dataset.methodName || 'customizeWebsiteVariable';\n const variable = this.el.dataset.variable;\n const themeFontsNb = nbFonts - (this.googleLocalFonts.length + this.googleFonts.length);\n _.times(nbFonts, fontNb => {\n const realFontNb = fontNb + 1;\n const fontEl = document.createElement('we-button');\n fontEl.classList.add(`o_we_option_font_${realFontNb}`);\n fontEl.dataset.variable = variable;\n fontEl.dataset[methodName] = weUtils.getCSSVariableValue(`font-number-${realFontNb}`, style);\n const font = weUtils.getCSSVariableValue(`font-number-${realFontNb}`, style);\n this.allFonts.push(font);\n fontEl.dataset[methodName] = font;\n fontEl.dataset.font = realFontNb;\n if (realFontNb <= themeFontsNb) {\n // Add the \"cloud\" icon next to the theme's default fonts\n // because they are served by Google.\n fontEl.appendChild(Object.assign(document.createElement('i'), {\n role: 'button',\n className: 'text-info ml-2 fa fa-cloud',\n title: _t(\"This font is hosted and served to your visitors by Google servers\"),\n }));\n }\n fontEls.push(fontEl);\n this.menuEl.appendChild(fontEl);\n });\n\n if (this.googleLocalFonts.length) {\n const googleLocalFontsEls = fontEls.splice(-this.googleLocalFonts.length);\n googleLocalFontsEls.forEach((el, index) => {\n $(el).append(core.qweb.render('website.delete_google_font_btn', {\n index: index,\n local: true,\n }));\n });\n }\n\n if (this.googleFonts.length) {\n const googleFontsEls = fontEls.splice(-this.googleFonts.length);\n googleFontsEls.forEach((el, index) => {\n $(el).append(core.qweb.render('website.delete_google_font_btn', {\n index: index,\n }));\n });\n }\n\n $(this.menuEl).append($(core.qweb.render('website.add_google_font_btn', {\n variable: variable,\n })));\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async setValue() {\n await this._super(...arguments);\n\n for (const className of this.menuTogglerEl.classList) {\n if (className.match(/^o_we_option_font_\\d+$/)) {\n this.menuTogglerEl.classList.remove(className);\n }\n }\n const activeWidget = this._userValueWidgets.find(widget => !widget.isPreviewed() && widget.isActive());\n if (activeWidget) {\n this.menuTogglerEl.classList.add(`o_we_option_font_${activeWidget.el.dataset.font}`);\n }\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * @private\n */\n _onAddGoogleFontClick: function (ev) {\n const variable = $(ev.currentTarget).data('variable');\n const dialog = new Dialog(this, {\n title: _t(\"Add a Google Font\"),\n $content: $(core.qweb.render('website.dialog.addGoogleFont')),\n buttons: [\n {\n text: _t(\"Save & Reload\"),\n classes: 'btn-primary',\n click: async () => {\n const inputEl = dialog.el.querySelector('.o_input_google_font');\n // if font page link (what is expected)\n let m = inputEl.value.match(/\\bspecimen\\/([\\w+]+)/);\n if (!m) {\n // if embed code (so that it works anyway if the user put the embed code instead of the page link)\n m = inputEl.value.match(/\\bfamily=([\\w+]+)/);\n if (!m) {\n inputEl.classList.add('is-invalid');\n return;\n }\n }\n\n let isValidFamily = false;\n\n try {\n // Font family is an encoded query parameter:\n // \"Open+Sans\" needs to remain \"Open+Sans\".\n const result = await fetch(\"https://fonts.googleapis.com/css?family=\" + m[1] + ':300,300i,400,400i,700,700i', {method: 'HEAD'});\n // Google fonts server returns a 400 status code if family is not valid.\n if (result.ok) {\n isValidFamily = true;\n }\n } catch (error) {\n console.error(error);\n }\n\n if (!isValidFamily) {\n inputEl.classList.add('is-invalid');\n return;\n }\n\n const font = m[1].replace(/\\+/g, ' ');\n const googleFontServe = dialog.el.querySelector('#google_font_serve').checked;\n const fontName = `'${font}'`;\n // If the font already exists, it will only be added if\n // the user chooses to add it locally when it is already\n // imported from the Google Fonts server.\n const fontExistsLocally = this.googleLocalFonts.some(localFont => localFont.split(':')[0] === fontName);\n const fontExistsOnServer = this.allFonts.includes(fontName);\n const preventFontAddition = fontExistsLocally || (fontExistsOnServer && googleFontServe);\n if (preventFontAddition) {\n inputEl.classList.add('is-invalid');\n // Show custom validity error message.\n inputEl.setCustomValidity(_t(\"This font already exists, you can only add it as a local font to replace the server version.\"));\n inputEl.reportValidity();\n return;\n }\n if (googleFontServe) {\n this.googleFonts.push(font);\n } else {\n this.googleLocalFonts.push(`'${font}': ''`);\n }\n this.trigger_up('google_fonts_custo_request', {\n values: {[variable]: `'${font}'`},\n googleFonts: this.googleFonts,\n googleLocalFonts: this.googleLocalFonts,\n });\n },\n },\n {\n text: _t(\"Discard\"),\n close: true,\n },\n ],\n });\n dialog.open();\n },\n /**\n * @private\n * @param {Event} ev\n */\n _onDeleteGoogleFontClick: async function (ev) {\n ev.preventDefault();\n const values = {};\n\n const save = await new Promise(resolve => {\n Dialog.confirm(this, _t(\"Deleting a font requires a reload of the page. This will save all your changes and reload the page, are you sure you want to proceed?\"), {\n confirm_callback: () => resolve(true),\n cancel_callback: () => resolve(false),\n });\n });\n if (!save) {\n return;\n }\n\n // Remove Google font\n const googleFontIndex = parseInt(ev.target.dataset.fontIndex);\n const isLocalFont = ev.target.dataset.localFont;\n let googleFontName;\n if (isLocalFont) {\n const googleFont = this.googleLocalFonts[googleFontIndex].split(':');\n // Remove double quotes\n googleFontName = googleFont[0].substring(1, googleFont[0].length - 1);\n values['delete-font-attachment-id'] = googleFont[1];\n this.googleLocalFonts.splice(googleFontIndex, 1);\n } else {\n googleFontName = this.googleFonts[googleFontIndex];\n this.googleFonts.splice(googleFontIndex, 1);\n }\n\n // Adapt font variable indexes to the removal\n const style = window.getComputedStyle(document.documentElement);\n _.each(FontFamilyPickerUserValueWidget.prototype.fontVariables, variable => {\n const value = weUtils.getCSSVariableValue(variable, style);\n if (value.substring(1, value.length - 1) === googleFontName) {\n // If an element is using the google font being removed, reset\n // it to the theme default.\n values[variable] = 'null';\n }\n });\n\n this.trigger_up('google_fonts_custo_request', {\n values: values,\n googleFonts: this.googleFonts,\n googleLocalFonts: this.googleLocalFonts,\n });\n },\n});\n\nconst GPSPicker = InputUserValueWidget.extend({\n events: { // Explicitly not consider all InputUserValueWidget events\n 'blur input': '_onInputBlur',\n },\n\n /**\n * @constructor\n */\n init() {\n this._super(...arguments);\n this._gmapCacheGPSToPlace = {};\n },\n /**\n * @override\n */\n async willStart() {\n await this._super(...arguments);\n this._gmapLoaded = await new Promise(resolve => {\n this.trigger_up('gmap_api_request', {\n editableMode: true,\n configureIfNecessary: true,\n onSuccess: key => {\n if (!key) {\n resolve(false);\n return;\n }\n\n // TODO see _notifyGMapError, this tries to trigger an error\n // early but this is not consistent with new gmap keys.\n this._nearbySearch('(50.854975,4.3753899)', !!key)\n .then(place => resolve(!!place));\n },\n });\n });\n if (!this._gmapLoaded && !this._gmapErrorNotified) {\n this.trigger_up('user_value_widget_critical');\n return;\n }\n },\n /**\n * @override\n */\n async start() {\n await this._super(...arguments);\n this.el.classList.add('o_we_large');\n if (!this._gmapLoaded) {\n return;\n }\n\n this._gmapAutocomplete = new google.maps.places.Autocomplete(this.inputEl, {types: ['geocode']});\n google.maps.event.addListener(this._gmapAutocomplete, 'place_changed', this._onPlaceChanged.bind(this));\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n getMethodsParams: function (methodName) {\n return Object.assign({gmapPlace: this._gmapPlace || {}}, this._super(...arguments));\n },\n /**\n * @override\n */\n async setValue() {\n await this._super(...arguments);\n if (!this._gmapLoaded) {\n return;\n }\n\n this._gmapPlace = await this._nearbySearch(this._value);\n\n if (this._gmapPlace) {\n this.inputEl.value = this._gmapPlace.formatted_address;\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @private\n * @param {string} gps\n * @param {boolean} [notify=true]\n * @returns {Promise}\n */\n async _nearbySearch(gps, notify = true) {\n if (this._gmapCacheGPSToPlace[gps]) {\n return this._gmapCacheGPSToPlace[gps];\n }\n\n const p = gps.substring(1).slice(0, -1).split(',');\n const location = new google.maps.LatLng(p[0] || 0, p[1] || 0);\n return new Promise(resolve => {\n const service = new google.maps.places.PlacesService(document.createElement('div'));\n service.nearbySearch({\n // Do a 'nearbySearch' followed by 'getDetails' to avoid using\n // GMap Geocoder which the user may not have enabled... but\n // ideally Geocoder should be used to get the exact location at\n // those coordinates and to limit billing query count.\n location: location,\n radius: 1,\n }, (results, status) => {\n const GMAP_CRITICAL_ERRORS = [google.maps.places.PlacesServiceStatus.REQUEST_DENIED, google.maps.places.PlacesServiceStatus.UNKNOWN_ERROR];\n if (status === google.maps.places.PlacesServiceStatus.OK) {\n service.getDetails({\n placeId: results[0].place_id,\n fields: ['geometry', 'formatted_address'],\n }, (place, status) => {\n if (status === google.maps.places.PlacesServiceStatus.OK) {\n this._gmapCacheGPSToPlace[gps] = place;\n resolve(place);\n } else if (GMAP_CRITICAL_ERRORS.includes(status)) {\n if (notify) {\n this._notifyGMapError();\n }\n resolve();\n }\n });\n } else if (GMAP_CRITICAL_ERRORS.includes(status)) {\n if (notify) {\n this._notifyGMapError();\n }\n resolve();\n } else {\n resolve();\n }\n });\n });\n },\n /**\n * Indicates to the user there is an error with the google map API and\n * re-opens the configuration dialog. For good measures, this also notifies\n * a critical error which normally removes the related snippet entirely.\n *\n * @private\n */\n _notifyGMapError() {\n // TODO this should be better to detect all errors. This is random.\n // When misconfigured (wrong APIs enabled), sometimes Google throw\n // errors immediately (which then reaches this code), sometimes it\n // throws them later (which then induces an error log in the console\n // and random behaviors).\n if (this._gmapErrorNotified) {\n return;\n }\n this._gmapErrorNotified = true;\n\n this.displayNotification({\n type: 'danger',\n sticky: true,\n message: _t(\"A Google Map error occurred. Make sure to read the key configuration popup carefully.\"),\n });\n this.trigger_up('gmap_api_request', {\n editableMode: true,\n reconfigure: true,\n onSuccess: () => {\n this._gmapErrorNotified = false;\n },\n });\n\n setTimeout(() => this.trigger_up('user_value_widget_critical'));\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * @private\n * @param {Event} ev\n */\n _onPlaceChanged(ev) {\n const gmapPlace = this._gmapAutocomplete.getPlace();\n if (gmapPlace && gmapPlace.geometry) {\n this._gmapPlace = gmapPlace;\n const location = this._gmapPlace.geometry.location;\n const oldValue = this._value;\n this._value = `(${location.lat()},${location.lng()})`;\n this._gmapCacheGPSToPlace[this._value] = gmapPlace;\n if (oldValue !== this._value) {\n this._onUserValueChange(ev);\n }\n }\n },\n /**\n * @override\n */\n _onInputBlur() {\n // As a stable fix: do not call the _super as we actually don't want\n // input focusout messing with the google map API. Because of this,\n // clicking on google map autocomplete suggestion on Firefox was not\n // working properly. This is kept as an empty function because of stable\n // policy (ensures custo can still extend this).\n // TODO review in master.\n },\n});\n\noptions.userValueWidgetsRegistry['we-urlpicker'] = UrlPickerUserValueWidget;\noptions.userValueWidgetsRegistry['we-fontfamilypicker'] = FontFamilyPickerUserValueWidget;\noptions.userValueWidgetsRegistry['we-gpspicker'] = GPSPicker;\n\n//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n\noptions.Class.include({\n xmlDependencies: (options.Class.prototype.xmlDependencies || [])\n .concat(['/website/static/src/xml/website.editor.xml']),\n custom_events: _.extend({}, options.Class.prototype.custom_events || {}, {\n 'google_fonts_custo_request': '_onGoogleFontsCustoRequest',\n }),\n specialCheckAndReloadMethodsNames: ['customizeWebsiteViews', 'customizeWebsiteVariable', 'customizeWebsiteColor'],\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @see this.selectClass for parameters\n */\n customizeWebsiteViews: async function (previewMode, widgetValue, params) {\n await this._customizeWebsite(previewMode, widgetValue, params, 'views');\n },\n /**\n * @see this.selectClass for parameters\n */\n customizeWebsiteVariable: async function (previewMode, widgetValue, params) {\n await this._customizeWebsite(previewMode, widgetValue, params, 'variable');\n },\n /**\n * @see this.selectClass for parameters\n */\n customizeWebsiteColor: async function (previewMode, widgetValue, params) {\n await this._customizeWebsite(previewMode, widgetValue, params, 'color');\n },\n /**\n * @see this.selectClass for parameters\n */\n async customizeWebsiteAssets(previewMode, widgetValue, params) {\n await this._customizeWebsite(previewMode, widgetValue, params, 'assets');\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _checkIfWidgetsUpdateNeedReload(widgets) {\n const needReload = await this._super(...arguments);\n if (needReload) {\n return needReload;\n }\n for (const widget of widgets) {\n const methodsNames = widget.getMethodsNames();\n const specialMethodsNames = [];\n for (const methodName of methodsNames) {\n if (this.specialCheckAndReloadMethodsNames.includes(methodName)) {\n specialMethodsNames.push(methodName);\n }\n }\n if (!specialMethodsNames.length) {\n continue;\n }\n const isDebugAssets = config.isDebug('assets');\n let paramsReload = isDebugAssets;\n if (!isDebugAssets) {\n for (const methodName of specialMethodsNames) {\n if (widget.getMethodsParams(methodName).reload) {\n paramsReload = true;\n break;\n }\n }\n }\n if (paramsReload) {\n return (isDebugAssets ? _t(\"It appears you are in debug=assets mode, all theme customization options require a page reload in this mode.\") : true);\n }\n }\n return false;\n },\n /**\n * @override\n */\n _computeWidgetState: async function (methodName, params) {\n switch (methodName) {\n case 'customizeWebsiteViews': {\n return this._getEnabledCustomizeValues(params.possibleValues, true);\n }\n case 'customizeWebsiteVariable': {\n return weUtils.getCSSVariableValue(params.variable);\n }\n case 'customizeWebsiteColor': {\n return weUtils.getCSSVariableValue(params.color);\n }\n case 'customizeWebsiteAssets': {\n return this._getEnabledCustomizeValues(params.possibleValues, false);\n }\n }\n return this._super(...arguments);\n },\n /**\n * @private\n */\n _customizeWebsite: async function (previewMode, widgetValue, params, type) {\n // Never allow previews for theme customizations\n if (previewMode) {\n return;\n }\n\n switch (type) {\n case 'views':\n await this._customizeWebsiteData(widgetValue, params, true);\n break;\n case 'variable':\n await this._customizeWebsiteVariable(widgetValue, params);\n break;\n case 'color':\n await this._customizeWebsiteColor(widgetValue, params);\n break;\n case 'assets':\n await this._customizeWebsiteData(widgetValue, params, false);\n break;\n default:\n if (params.customCustomization) {\n await params.customCustomization.call(this, widgetValue, params);\n }\n }\n\n if (params.reload || config.isDebug('assets') || params.noBundleReload) {\n // Caller will reload the page, nothing needs to be done anymore.\n return;\n }\n\n // Finally, only update the bundles as no reload is required\n await this._reloadBundles();\n\n // Some public widgets may depend on the variables that were\n // customized, so we have to restart them *all*.\n await new Promise((resolve, reject) => {\n this.trigger_up('widgets_start_request', {\n editableMode: true,\n onSuccess: () => resolve(),\n onFailure: () => reject(),\n });\n });\n },\n /**\n * @private\n */\n async _customizeWebsiteColor(color, params) {\n await this._customizeWebsiteColors({[params.color]: color}, params);\n },\n /**\n * @private\n */\n async _customizeWebsiteColors(colors, params) {\n colors = colors || {};\n\n const baseURL = '/website/static/src/scss/options/colors/';\n const colorType = params.colorType ? (params.colorType + '_') : '';\n const url = `${baseURL}user_${colorType}color_palette.scss`;\n\n const finalColors = {};\n for (const [colorName, color] of Object.entries(colors)) {\n finalColors[colorName] = color;\n if (color) {\n if (weUtils.isColorCombinationName(color)) {\n finalColors[colorName] = parseInt(color);\n } else if (!ColorpickerWidget.isCSSColor(color)) {\n finalColors[colorName] = `'${color}'`;\n }\n }\n }\n return this._makeSCSSCusto(url, finalColors, params.nullValue);\n },\n /**\n * @private\n */\n _customizeWebsiteVariable: async function (value, params) {\n return this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', {\n [params.variable]: value,\n }, params.nullValue);\n },\n /**\n * TODO Remove this function in master because it only stays here for\n * compatibility.\n *\n * @private\n */\n _customizeWebsiteViews: async function (xmlID, params) {\n this._customizeWebsiteData(xmlID, params, true);\n },\n /**\n * @private\n */\n async _customizeWebsiteData(value, params, isViewData) {\n const allDataKeys = this._getDataKeysFromPossibleValues(params.possibleValues);\n const enableDataKeys = value.split(/\\s*,\\s*/);\n const disableDataKeys = allDataKeys.filter(value => !enableDataKeys.includes(value));\n const resetViewArch = !!params.resetViewArch;\n\n return this._rpc({\n route: '/website/theme_customize_data',\n params: {\n 'is_view_data': isViewData,\n 'enable': enableDataKeys,\n 'disable': disableDataKeys,\n 'reset_view_arch': resetViewArch,\n },\n });\n },\n /**\n * TODO Remove this function in master because it only stays here for\n * compatibility.\n *\n * @private\n */\n _getXMLIDsFromPossibleValues: function (possibleValues) {\n this._getDataKeysFromPossibleValues(possibleValues);\n },\n /**\n * @private\n */\n _getDataKeysFromPossibleValues(possibleValues) {\n const allDataKeys = [];\n for (const dataKeysStr of possibleValues) {\n allDataKeys.push(...dataKeysStr.split(/\\s*,\\s*/));\n }\n return allDataKeys.filter((v, i, arr) => arr.indexOf(v) === i);\n },\n /**\n * @private\n * @param {Array} possibleValues\n * @param {Boolean} isViewData true = \"ir.ui.view\", false = \"ir.asset\"\n * @returns {String}\n */\n async _getEnabledCustomizeValues(possibleValues, isViewData) {\n const allDataKeys = this._getDataKeysFromPossibleValues(possibleValues);\n const enabledValues = await this._rpc({\n route: '/website/theme_customize_data_get',\n params: {\n 'keys': allDataKeys,\n 'is_view_data': isViewData,\n },\n });\n let mostValuesStr = '';\n let mostValuesNb = 0;\n for (const valuesStr of possibleValues) {\n const enableValues = valuesStr.split(/\\s*,\\s*/);\n if (enableValues.length > mostValuesNb\n && enableValues.every(value => enabledValues.includes(value))) {\n mostValuesStr = valuesStr;\n mostValuesNb = enableValues.length;\n }\n }\n return mostValuesStr; // Need to return the exact same string as in possibleValues\n },\n /**\n * @private\n */\n _makeSCSSCusto: async function (url, values, defaultValue = 'null') {\n return this._rpc({\n route: '/website/make_scss_custo',\n params: {\n 'url': url,\n 'values': _.mapObject(values, v => v || defaultValue),\n },\n });\n },\n /**\n * Refreshes all public widgets related to the given element.\n *\n * @private\n * @param {jQuery} [$el=this.$target]\n * @returns {Promise}\n */\n _refreshPublicWidgets: async function ($el) {\n return new Promise((resolve, reject) => {\n this.trigger_up('widgets_start_request', {\n editableMode: true,\n $target: $el || this.$target,\n onSuccess: resolve,\n onFailure: reject,\n });\n });\n },\n /**\n * @private\n */\n _reloadBundles: async function () {\n const bundles = await this._rpc({\n route: '/website/theme_customize_bundle_reload',\n });\n let $allLinks = $();\n const proms = _.map(bundles, (bundleURLs, bundleName) => {\n var $links = $('link[href*=\"' + bundleName + '\"]');\n $allLinks = $allLinks.add($links);\n var $newLinks = $();\n _.each(bundleURLs, url => {\n $newLinks = $newLinks.add($('', {\n type: 'text/css',\n rel: 'stylesheet',\n href: url,\n }));\n });\n\n const linksLoaded = new Promise(resolve => {\n let nbLoaded = 0;\n $newLinks.on('load error', () => { // If we have an error, just ignore it\n if (++nbLoaded >= $newLinks.length) {\n resolve();\n }\n });\n });\n $links.last().after($newLinks);\n return linksLoaded;\n });\n await Promise.all(proms).then(() => $allLinks.remove());\n },\n /**\n * @override\n */\n _select: async function (previewMode, widget) {\n await this._super(...arguments);\n\n if (!widget.$el.closest('[data-no-widget-refresh=\"true\"]').length) {\n // TODO the flag should be retrieved through widget params somehow\n await this._refreshPublicWidgets();\n }\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * @private\n * @param {OdooEvent} ev\n */\n _onGoogleFontsCustoRequest: function (ev) {\n const values = ev.data.values ? _.clone(ev.data.values) : {};\n const googleFonts = ev.data.googleFonts;\n const googleLocalFonts = ev.data.googleLocalFonts;\n if (googleFonts.length) {\n values['google-fonts'] = \"('\" + googleFonts.join(\"', '\") + \"')\";\n } else {\n values['google-fonts'] = 'null';\n }\n // check undefined, this is a backport, a custo might not pass this key\n if (googleLocalFonts !== undefined && googleLocalFonts.length) {\n values['google-local-fonts'] = \"(\" + googleLocalFonts.join(\", \") + \")\";\n } else {\n values['google-local-fonts'] = 'null';\n }\n this.trigger_up('snippet_edition_request', {exec: async () => {\n return this._makeSCSSCusto('/website/static/src/scss/options/user_values.scss', values);\n }});\n this.trigger_up('request_save', {\n reloadEditor: true,\n });\n },\n});\n\nfunction _getLastPreFilterLayerElement($el) {\n // Make sure parallax and video element are considered to be below the\n // color filters / shape\n const $bgVideo = $el.find('> .o_bg_video_container');\n if ($bgVideo.length) {\n return $bgVideo[0];\n }\n const $parallaxEl = $el.find('> .s_parallax_bg');\n if ($parallaxEl.length) {\n return $parallaxEl[0];\n }\n return null;\n}\n\noptions.registry.BackgroundToggler.include({\n /**\n * Toggles background video on or off.\n *\n * @see this.selectClass for parameters\n */\n toggleBgVideo(previewMode, widgetValue, params) {\n if (!widgetValue) {\n this.$target.find('> .o_we_bg_filter').remove();\n // TODO: use setWidgetValue instead of calling background directly when possible\n const [bgVideoWidget] = this._requestUserValueWidgets('bg_video_opt');\n const bgVideoOpt = bgVideoWidget.getParent();\n return bgVideoOpt._setBgVideo(false, '');\n } else {\n // TODO: use trigger instead of el.click when possible\n this._requestUserValueWidgets('bg_video_opt')[0].el.click();\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeWidgetState(methodName, params) {\n if (methodName === 'toggleBgVideo') {\n return this.$target[0].classList.contains('o_background_video');\n }\n return this._super(...arguments);\n },\n /**\n * TODO an overall better management of background layers is needed\n *\n * @override\n */\n _getLastPreFilterLayerElement() {\n const el = _getLastPreFilterLayerElement(this.$target);\n if (el) {\n return el;\n }\n return this._super(...arguments);\n },\n});\n\noptions.registry.BackgroundShape.include({\n /**\n * TODO need a better management of background layers\n *\n * @override\n */\n _getLastPreShapeLayerElement() {\n const el = this._super(...arguments);\n if (el) {\n return el;\n }\n return _getLastPreFilterLayerElement(this.$target);\n },\n /**\n * @override\n */\n _removeShapeEl(shapeEl) {\n this.trigger_up('widgets_stop_request', {\n $target: $(shapeEl),\n });\n return this._super(...arguments);\n },\n});\n\noptions.registry.ReplaceMedia.include({\n /**\n * Adds an anchor to the url.\n * Here \"anchor\" means a specific section of a page.\n *\n * @see this.selectClass for parameters\n */\n setAnchor(previewMode, widgetValue, params) {\n const linkEl = this.$target[0].parentElement;\n let url = linkEl.getAttribute('href');\n url = url.split('#')[0];\n linkEl.setAttribute('href', url + widgetValue);\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeWidgetState(methodName, params) {\n if (methodName === 'setAnchor') {\n const parentEl = this.$target[0].parentElement;\n if (parentEl.tagName === 'A') {\n const href = parentEl.getAttribute('href') || '';\n return href ? `#${href.split('#')[1]}` : '';\n }\n return '';\n }\n return this._super(...arguments);\n },\n /**\n * @override\n */\n async _computeWidgetVisibility(widgetName, params) {\n if (widgetName === 'media_link_anchor_opt') {\n const parentEl = this.$target[0].parentElement;\n const linkEl = parentEl.tagName === 'A' ? parentEl : null;\n const href = linkEl ? linkEl.getAttribute('href') : false;\n return href && href.startsWith('/');\n }\n return this._super(...arguments);\n },\n /**\n * Fills the dropdown with the available anchors for the page referenced in\n * the href.\n *\n * @override\n */\n async _renderCustomXML(uiFragment) {\n await this._super(...arguments);\n\n const oldURLWidgetEl = uiFragment.querySelector('[data-name=\"media_url_opt\"]');\n\n const URLWidgetEl = document.createElement('we-urlpicker');\n // Copy attributes\n for (const {name, value} of oldURLWidgetEl.attributes) {\n URLWidgetEl.setAttribute(name, value);\n }\n URLWidgetEl.title = _t(\"Hint: Type '/' to search an existing page and '#' to link to an anchor.\");\n oldURLWidgetEl.replaceWith(URLWidgetEl);\n\n const hrefValue = this.$target[0].parentElement.getAttribute('href');\n if (!hrefValue || !hrefValue.startsWith('/')) {\n return;\n }\n const urlWithoutAnchor = hrefValue.split('#')[0];\n const selectEl = document.createElement('we-select');\n selectEl.dataset.name = 'media_link_anchor_opt';\n selectEl.dataset.dependencies = 'media_url_opt';\n selectEl.dataset.noPreview = 'true';\n selectEl.setAttribute('string', _t(\"\u2319 Page Anchor\"));\n const anchors = await wUtils.loadAnchors(urlWithoutAnchor);\n for (const anchor of anchors) {\n const weButtonEl = document.createElement('we-button');\n weButtonEl.dataset.setAnchor = anchor;\n weButtonEl.textContent = anchor;\n selectEl.append(weButtonEl);\n }\n URLWidgetEl.after(selectEl);\n },\n});\n\noptions.registry.BackgroundVideo = options.Class.extend({\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Sets the target's background video.\n *\n * @see this.selectClass for parameters\n */\n background: function (previewMode, widgetValue, params) {\n if (previewMode === 'reset' && this.videoSrc) {\n return this._setBgVideo(false, this.videoSrc);\n }\n return this._setBgVideo(previewMode, widgetValue);\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeWidgetState: function (methodName, params) {\n if (methodName === 'background') {\n if (this.$target[0].classList.contains('o_background_video')) {\n return this.$('> .o_bg_video_container iframe').attr('src');\n }\n return '';\n }\n return this._super(...arguments);\n },\n /**\n * Updates the background video used by the snippet.\n *\n * @private\n * @see this.selectClass for parameters\n * @returns {Promise}\n */\n _setBgVideo: async function (previewMode, value) {\n this.$('> .o_bg_video_container').toggleClass('d-none', previewMode === true);\n\n if (previewMode !== false) {\n return;\n }\n\n this.videoSrc = value;\n var target = this.$target[0];\n target.classList.toggle('o_background_video', !!(value && value.length));\n if (value && value.length) {\n target.dataset.bgVideoSrc = value;\n } else {\n delete target.dataset.bgVideoSrc;\n }\n await this._refreshPublicWidgets();\n },\n});\n\noptions.registry.OptionsTab = options.Class.extend({\n GRAY_PARAMS: {EXTRA_SATURATION: \"gray-extra-saturation\", HUE: \"gray-hue\"},\n\n /**\n * @override\n */\n init() {\n this._super(...arguments);\n this.grayParams = {};\n this.grays = {};\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async updateUI() {\n // The bg-XXX classes have been updated (and could be updated by another\n // option like changing color palette) -> remove the inline style that\n // was added for gray previews.\n this.$el.find(\".o_we_gray_preview\").each((_, e) => {\n e.style.removeProperty(\"background-color\");\n });\n\n // If the gray palette has been generated by Odoo standard option,\n // the hue of all gray is the same and the saturation has been\n // increased/decreased by the same amount for all grays in\n // comparaison with BS grays. However the system supports any\n // gray palette.\n\n const hues = [];\n const saturationDiffs = [];\n let oneHasNoSaturation = false;\n for (let id = 100; id <= 900; id += 100) {\n const gray = weUtils.getCSSVariableValue(`${id}`);\n const grayRGB = ColorpickerWidget.convertCSSColorToRgba(gray);\n const grayHSL = ColorpickerWidget.convertRgbToHsl(grayRGB.red, grayRGB.green, grayRGB.blue);\n\n const baseGray = weUtils.getCSSVariableValue(`base-${id}`);\n const baseGrayRGB = ColorpickerWidget.convertCSSColorToRgba(baseGray);\n const baseGrayHSL = ColorpickerWidget.convertRgbToHsl(baseGrayRGB.red, baseGrayRGB.green, baseGrayRGB.blue);\n\n if (grayHSL.saturation > 0.01) {\n if (grayHSL.lightness > 0.01 && grayHSL.lightness < 99.99) {\n hues.push(grayHSL.hue);\n }\n if (grayHSL.saturation < 99.99) {\n saturationDiffs.push(grayHSL.saturation - baseGrayHSL.saturation);\n }\n } else {\n oneHasNoSaturation = true;\n }\n }\n this.grayHueIsDefined = !!hues.length;\n\n // Average of angles: we need to take the average of found hues\n // because even if grays are supposed to be set to the exact\n // same hue by the Odoo editor, there might be rounding errors\n // during the conversion from RGB to HSL as the HSL system\n // allows to represent more colors that the RGB hexadecimal\n // notation (also: hue 360 = hue 0 and should not be averaged to 180).\n // This also better support random gray palettes.\n this.grayParams[this.GRAY_PARAMS.HUE] = (!hues.length) ? 0 : Math.round((Math.atan2(\n hues.map(hue => Math.sin(hue * Math.PI / 180)).reduce((memo, value) => memo + value, 0) / hues.length,\n hues.map(hue => Math.cos(hue * Math.PI / 180)).reduce((memo, value) => memo + value, 0) / hues.length\n ) * 180 / Math.PI) + 360) % 360;\n\n // Average of found saturation diffs, or all grays have no\n // saturation, or all grays are fully saturated.\n this.grayParams[this.GRAY_PARAMS.EXTRA_SATURATION] = saturationDiffs.length\n ? saturationDiffs.reduce((memo, value) => memo + value, 0) / saturationDiffs.length\n : (oneHasNoSaturation ? -100 : 100);\n\n await this._super(...arguments);\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async customizeGray(previewMode, widgetValue, params) {\n // Gray parameters are used *on the JS side* to compute the grays that\n // will be saved in the database. We indeed need those grays to be\n // computed here for faster previews so this allows to not duplicate\n // most of the logic. Also, this gives flexibility to maybe allow full\n // customization of grays in custo and themes. Also, this allows to ease\n // migration if the computation here was to change: the user grays would\n // still be unchanged as saved in the database.\n\n this.grayParams[params.param] = parseInt(widgetValue);\n for (let i = 1; i < 10; i++) {\n const key = (100 * i).toString();\n this.grays[key] = this._buildGray(key);\n }\n\n // Preview UI update\n this.$el.find(\".o_we_gray_preview\").each((_, e) => {\n e.style.setProperty(\"background-color\", this.grays[e.getAttribute('variable')], \"important\");\n });\n\n // Save all computed (JS side) grays in database\n await this._customizeWebsite(previewMode, undefined, Object.assign({}, params, {\n customCustomization: () => { // TODO this could be prettier\n return this._customizeWebsiteColors(this.grays, Object.assign({}, params, {\n colorType: 'gray',\n }));\n },\n }));\n },\n /**\n * @see this.selectClass for parameters\n */\n async configureApiKey(previewMode, widgetValue, params) {\n return new Promise(resolve => {\n this.trigger_up('gmap_api_key_request', {\n editableMode: true,\n reconfigure: true,\n onSuccess: () => resolve(),\n });\n });\n },\n /**\n * @see this.selectClass for parameters\n */\n async customizeBodyBgType(previewMode, widgetValue, params) {\n if (widgetValue === 'NONE') {\n this.bodyImageType = 'image';\n return this.customizeBodyBg(previewMode, '', params);\n }\n // TODO improve: hack to click on external image picker\n this.bodyImageType = widgetValue;\n const widget = this._requestUserValueWidgets(params.imagepicker)[0];\n widget.enable();\n },\n /**\n * @override\n */\n async customizeBodyBg(previewMode, widgetValue, params) {\n // TODO improve: customize two variables at the same time...\n await this.customizeWebsiteVariable(previewMode, this.bodyImageType, {variable: 'body-image-type'});\n await this.customizeWebsiteVariable(previewMode, widgetValue ? `'${widgetValue}'` : '', {variable: 'body-image'});\n },\n /**\n * @see this.selectClass for parameters\n */\n async openCustomCodeDialog(previewMode, widgetValue, params) {\n const libsProm = this._loadLibs({\n jsLibs: [\n '/web/static/lib/ace/ace.js',\n '/web/static/lib/ace/mode-xml.js',\n '/web/static/lib/ace/mode-qweb.js',\n ],\n });\n\n let websiteId;\n this.trigger_up('context_get', {\n callback: (ctx) => {\n websiteId = ctx['website_id'];\n },\n });\n\n let website;\n const dataProm = this._rpc({\n model: 'website',\n method: 'read',\n args: [[websiteId], ['custom_code_head', 'custom_code_footer']],\n }).then(websites => {\n website = websites[0];\n });\n\n let fieldName, title, contentText;\n if (widgetValue === 'head') {\n fieldName = 'custom_code_head';\n title = _t('Custom head code');\n contentText = _t('Enter code that will be added into the of every page of your site.');\n } else {\n fieldName = 'custom_code_footer';\n title = _t('Custom end of body code');\n contentText = _t('Enter code that will be added before the of every page of your site.');\n }\n\n await Promise.all([libsProm, dataProm]);\n\n await new Promise(resolve => {\n const $content = $(core.qweb.render('website.custom_code_dialog_content', {\n contentText,\n }));\n const aceEditor = this._renderAceEditor($content.find('.o_ace_editor_container')[0], website[fieldName] || '');\n const dialog = new Dialog(this, {\n title,\n $content,\n buttons: [\n {\n text: _t(\"Save\"),\n classes: 'btn-primary',\n click: async () => {\n await this._rpc({\n model: 'website',\n method: 'write',\n args: [\n [websiteId],\n {[fieldName]: aceEditor.getValue()},\n ],\n });\n },\n close: true,\n },\n {\n text: _t(\"Discard\"),\n close: true,\n },\n ],\n });\n dialog.on('closed', this, resolve);\n dialog.open();\n });\n },\n /**\n * @see this.selectClass for parameters\n */\n async switchTheme(previewMode, widgetValue, params) {\n const save = await new Promise(resolve => {\n Dialog.confirm(this, _t(\"Changing theme requires to leave the editor. This will save all your changes, are you sure you want to proceed? Be careful that changing the theme will reset all your color customizations.\"), {\n confirm_callback: () => resolve(true),\n cancel_callback: () => resolve(false),\n });\n });\n if (!save) {\n return;\n }\n this.trigger_up('request_save', {\n reload: false,\n onSuccess: () => window.location.href = '/web#action=website.theme_install_kanban_action',\n });\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @private\n * @param {String} id\n * @returns {String} the adjusted color of gray\n */\n _buildGray(id) {\n const gray = weUtils.getCSSVariableValue(`base-${id}`);\n const grayRGB = ColorpickerWidget.convertCSSColorToRgba(gray);\n const hsl = ColorpickerWidget.convertRgbToHsl(grayRGB.red, grayRGB.green, grayRGB.blue);\n const adjustedGrayRGB = ColorpickerWidget.convertHslToRgb(this.grayParams[this.GRAY_PARAMS.HUE],\n Math.min(Math.max(hsl.saturation + this.grayParams[this.GRAY_PARAMS.EXTRA_SATURATION], 0), 100),\n hsl.lightness);\n return ColorpickerWidget.convertRgbaToCSSColor(adjustedGrayRGB.red, adjustedGrayRGB.green, adjustedGrayRGB.blue);\n },\n /**\n * @override\n */\n async _renderCustomXML(uiFragment) {\n await this._super(...arguments);\n const extraSaturationRangeEl = uiFragment.querySelector(`we-range[data-param=${this.GRAY_PARAMS.EXTRA_SATURATION}]`);\n if (extraSaturationRangeEl) {\n const baseGrays = _.range(100, 1000, 100).map(id => {\n const gray = weUtils.getCSSVariableValue(`base-${id}`);\n const grayRGB = ColorpickerWidget.convertCSSColorToRgba(gray);\n const hsl = ColorpickerWidget.convertRgbToHsl(grayRGB.red, grayRGB.green, grayRGB.blue);\n return {id: id, hsl: hsl};\n });\n const first = baseGrays[0];\n const maxValue = baseGrays.reduce((gray, value) => {\n return gray.hsl.saturation > value.hsl.saturation ? gray : value;\n }, first);\n const minValue = baseGrays.reduce((gray, value) => {\n return gray.hsl.saturation < value.hsl.saturation ? gray : value;\n }, first);\n extraSaturationRangeEl.dataset.max = 100 - minValue.hsl.saturation;\n extraSaturationRangeEl.dataset.min = -maxValue.hsl.saturation;\n }\n },\n /**\n * @override\n */\n async _checkIfWidgetsUpdateNeedWarning(widgets) {\n const warningMessage = await this._super(...arguments);\n if (warningMessage) {\n return warningMessage;\n }\n for (const widget of widgets) {\n if (widget.getMethodsNames().includes('customizeWebsiteVariable')\n && widget.getMethodsParams('customizeWebsiteVariable').variable === 'color-palettes-name') {\n const hasCustomizedColors = weUtils.getCSSVariableValue('has-customized-colors');\n if (hasCustomizedColors && hasCustomizedColors !== 'false') {\n return _t(\"Changing the color palette will reset all your color customizations, are you sure you want to proceed?\");\n }\n }\n }\n return '';\n },\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'customizeBodyBgType') {\n const bgImage = $('#wrapwrap').css('background-image');\n if (bgImage === 'none') {\n return \"NONE\";\n }\n return weUtils.getCSSVariableValue('body-image-type');\n }\n if (methodName === 'customizeGray') {\n // See updateUI override\n return this.grayParams[params.param];\n }\n return this._super(...arguments);\n },\n /**\n * @override\n */\n async _computeWidgetVisibility(widgetName, params) {\n if (widgetName === 'body_bg_image_opt') {\n return false;\n }\n if (params.param === this.GRAY_PARAMS.HUE) {\n return this.grayHueIsDefined;\n }\n return this._super(...arguments);\n },\n /**\n * @private\n * @param {DOMElement} node\n * @param {String} content text of the editor\n * @returns {Object}\n */\n _renderAceEditor(node, content) {\n const aceEditor = window.ace.edit(node);\n aceEditor.setTheme('ace/theme/monokai');\n aceEditor.setValue(content, 1);\n aceEditor.setOptions({\n minLines: 20,\n maxLines: Infinity,\n showPrintMargin: false,\n });\n aceEditor.renderer.setOptions({\n highlightGutterLine: true,\n showInvisibles: true,\n fontSize: 14,\n });\n\n const aceSession = aceEditor.getSession();\n aceSession.setOptions({\n mode: \"ace/mode/qweb\",\n useWorker: false,\n });\n return aceEditor;\n },\n /**\n * @override\n */\n async _renderCustomXML(uiFragment) {\n uiFragment.querySelectorAll('we-colorpicker').forEach(el => {\n el.dataset.lazyPalette = 'true';\n });\n },\n});\n\noptions.registry.ThemeColors = options.registry.OptionsTab.extend({\n /**\n * @override\n */\n async start() {\n // Checks for support of the old color system\n const style = window.getComputedStyle(document.documentElement);\n const supportOldColorSystem = weUtils.getCSSVariableValue('support-13-0-color-system', style) === 'true';\n const hasCustomizedOldColorSystem = weUtils.getCSSVariableValue('has-customized-13-0-color-system', style) === 'true';\n this._showOldColorSystemWarning = supportOldColorSystem && hasCustomizedOldColorSystem;\n\n return this._super(...arguments);\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async updateUIVisibility() {\n await this._super(...arguments);\n const oldColorSystemEl = this.el.querySelector('.o_old_color_system_warning');\n oldColorSystemEl.classList.toggle('d-none', !this._showOldColorSystemWarning);\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _renderCustomXML(uiFragment) {\n const paletteSelectorEl = uiFragment.querySelector('[data-variable=\"color-palettes-name\"]');\n const style = window.getComputedStyle(document.documentElement);\n const allPaletteNames = weUtils.getCSSVariableValue('palette-names', style).split(', ').map((name) => {\n return name.replace(/'/g, \"\");\n });\n for (const paletteName of allPaletteNames) {\n const btnEl = document.createElement('we-button');\n btnEl.classList.add('o_palette_color_preview_button');\n btnEl.dataset.customizeWebsiteVariable = `'${paletteName}'`;\n [1, 3, 2].forEach(c => {\n const colorPreviewEl = document.createElement('span');\n colorPreviewEl.classList.add('o_palette_color_preview');\n const color = weUtils.getCSSVariableValue(`o-palette-${paletteName}-o-color-${c}`, style);\n colorPreviewEl.style.backgroundColor = color;\n btnEl.appendChild(colorPreviewEl);\n });\n paletteSelectorEl.appendChild(btnEl);\n }\n\n for (let i = 1; i <= 5; i++) {\n const collapseEl = document.createElement('we-collapse');\n const ccPreviewEl = $(qweb.render('web_editor.color.combination.preview'))[0];\n ccPreviewEl.classList.add('text-center', `o_cc${i}`, 'o_we_collapse_toggler');\n collapseEl.appendChild(ccPreviewEl);\n const editionEls = $(qweb.render('website.color_combination_edition', {number: i}));\n for (const el of editionEls) {\n collapseEl.appendChild(el);\n }\n uiFragment.appendChild(collapseEl);\n }\n\n await this._super(...arguments);\n },\n});\n\noptions.registry.menu_data = options.Class.extend({\n /**\n * When the users selects a menu, a popover is shown with 4 possible\n * actions: follow the link in a new tab, copy the menu link, edit the menu,\n * or edit the menu tree.\n * The popover shows a preview of the menu link. Remote URL only show the\n * favicon.\n *\n * @override\n */\n start: function () {\n wLinkPopoverWidget.createFor(this, this.$target[0], { wysiwyg: $('#wrapwrap').data('wysiwyg') });\n return this._super(...arguments);\n },\n /**\n * When the users selects another element on the page, makes sure the\n * popover is closed.\n *\n * @override\n */\n onBlur: function () {\n this.$target.popover('hide');\n },\n});\n\noptions.registry.company_data = options.Class.extend({\n /**\n * Fetches data to determine the URL where the user can edit its company\n * data. Saves the info in the prototype to do this only once.\n *\n * @override\n */\n start: function () {\n var proto = options.registry.company_data.prototype;\n var prom;\n var self = this;\n if (proto.__link === undefined) {\n prom = this._rpc({route: '/web/session/get_session_info'}).then(function (session) {\n return self._rpc({\n model: 'res.users',\n method: 'read',\n args: [session.uid, ['company_id']],\n });\n }).then(function (res) {\n proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + encodeURIComponent(res && res[0] && res[0].company_id[0] || 1);\n });\n }\n return Promise.all([this._super.apply(this, arguments), prom]);\n },\n /**\n * When the users selects company data, opens a dialog to ask him if he\n * wants to be redirected to the company form view to edit it.\n *\n * @override\n */\n onFocus: function () {\n var self = this;\n var proto = options.registry.company_data.prototype;\n\n Dialog.confirm(this, _t(\"Do you want to edit the company data ?\"), {\n confirm_callback: function () {\n self.trigger_up('request_save', {\n reload: false,\n onSuccess: function () {\n window.location.href = proto.__link;\n },\n });\n },\n });\n },\n});\n\noptions.registry.Carousel = options.Class.extend({\n /**\n * @override\n */\n start: function () {\n this.$target.carousel('pause');\n this.$indicators = this.$target.find('.carousel-indicators');\n this.$controls = this.$target.find('.carousel-control-prev, .carousel-control-next, .carousel-indicators');\n\n // Prevent enabling the carousel overlay when clicking on the carousel\n // controls (indeed we want it to change the carousel slide then enable\n // the slide overlay) + See \"CarouselItem\" option.\n this.$controls.addClass('o_we_no_overlay');\n\n // Handle the sliding manually.\n this.__onControlClick = _.throttle(this._onControlClick.bind(this), 1000);\n this.$controls.on(\"click.carousel_option\", this.__onControlClick);\n\n return this._super.apply(this, arguments);\n },\n /**\n * @override\n */\n destroy: function () {\n this._super.apply(this, arguments);\n this.$target.off('.carousel_option');\n this.$controls.off(\".carousel_option\");\n },\n /**\n * @override\n */\n onBuilt: function () {\n this._assignUniqueID();\n },\n /**\n * @override\n */\n onClone: function () {\n this._assignUniqueID();\n },\n /**\n * @override\n */\n notify(name, data) {\n this._super(...arguments);\n if (name === 'add_slide') {\n this._addSlide().then(data.onSuccess);\n } else if (name === \"slide\") {\n this._slide(data.direction).then(data.onSuccess);\n }\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @see this.selectClass for parameters\n */\n addSlide(previewMode, widgetValue, params) {\n return this._addSlide();\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * Creates a unique ID for the carousel and reassign data-attributes that\n * depend on it.\n *\n * @private\n */\n _assignUniqueID: function () {\n const id = 'myCarousel' + Date.now();\n this.$target.attr('id', id);\n this.$target.find('[data-target]').attr('data-target', '#' + id);\n _.each(this.$target.find('[data-slide], [data-slide-to]'), function (el) {\n var $el = $(el);\n if ($el.attr('data-target')) {\n $el.attr('data-target', '#' + id);\n } else if ($el.attr('href')) {\n $el.attr('href', '#' + id);\n }\n });\n },\n /**\n * Adds a slide.\n *\n * @private\n */\n async _addSlide() {\n this.options.wysiwyg.odooEditor.historyPauseSteps();\n const $items = this.$target.find('.carousel-item');\n this.$controls.removeClass('d-none');\n const $active = $items.filter('.active');\n this.$indicators.append($('
  • ', {\n 'data-target': '#' + this.$target.attr('id'),\n }));\n this.$indicators.append(' ');\n // Need to remove editor data from the clone so it gets its own.\n $active.clone(false)\n .removeClass('active')\n .insertAfter($active);\n await this._slide(\"next\");\n this.options.wysiwyg.odooEditor.historyUnpauseSteps();\n },\n /**\n * Slides the carousel in the given direction.\n *\n * @private\n * @param {String|Number} direction the direction in which to slide:\n * - \"prev\": the previous slide;\n * - \"next\": the next slide;\n * - number: a slide number.\n * @returns {Promise}\n */\n _slide(direction) {\n this.trigger_up(\"disable_loading_effect\");\n let _slideTimestamp;\n this.$target.one(\"slide.bs.carousel\", () => {\n _slideTimestamp = window.performance.now();\n setTimeout(() => this.trigger_up('hide_overlay'));\n });\n\n return new Promise(resolve => {\n this.$target.one(\"slid.bs.carousel\", () => {\n // slid.bs.carousel is most of the time fired too soon by bootstrap\n // since it emulates the transitionEnd with a setTimeout. We wait\n // here an extra 20% of the time before retargeting edition, which\n // should be enough...\n const _slideDuration = (window.performance.now() - _slideTimestamp);\n setTimeout(() => {\n this.trigger_up(\"activate_snippet\", {\n $snippet: this.$target.find(\".carousel-item.active\"),\n ifInactiveOptions: true,\n });\n this.$target.trigger(\"active_slide_targeted\"); // TODO remove in master: kept for compatibility.\n this.trigger_up(\"enable_loading_effect\");\n resolve();\n }, 0.2 * _slideDuration);\n });\n\n this.$target.carousel(direction);\n });\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * Slides the carousel when clicking on the carousel controls. This handler\n * allows to put the sliding in the mutex, to avoid race conditions.\n *\n * @private\n * @param {Event} ev\n */\n _onControlClick(ev) {\n // Compute to which slide the carousel will slide.\n const controlEl = ev.currentTarget;\n let direction;\n if (controlEl.classList.contains(\"carousel-control-prev\")) {\n direction = \"prev\";\n } else if (controlEl.classList.contains(\"carousel-control-next\")) {\n direction = \"next\";\n } else {\n const indicatorEl = ev.target;\n if (!indicatorEl.matches(\"li\") || indicatorEl.classList.contains(\"active\")) {\n return;\n }\n direction = [...controlEl.children].indexOf(indicatorEl);\n }\n\n // Slide the carousel.\n this.trigger_up(\"snippet_edition_request\", {exec: async () => {\n await this._slide(direction);\n }});\n },\n});\n\noptions.registry.CarouselItem = options.Class.extend({\n isTopOption: true,\n forceNoDeleteButton: true,\n\n /**\n * @override\n */\n start: function () {\n this.$carousel = this.$target.closest('.carousel');\n this.$indicators = this.$carousel.find('.carousel-indicators');\n this.$controls = this.$carousel.find('.carousel-control-prev, .carousel-control-next, .carousel-indicators');\n\n var leftPanelEl = this.$overlay.data('$optionsSection')[0];\n var titleTextEl = leftPanelEl.querySelector('we-title > span');\n this.counterEl = document.createElement('span');\n titleTextEl.appendChild(this.counterEl);\n\n return this._super(...arguments);\n },\n /**\n * @override\n */\n destroy: function () {\n // Activate the active slide after removing a slide.\n if (this.hasRemovedSlide) {\n this.trigger_up(\"activate_snippet\", {\n $snippet: this.$carousel.find(\".carousel-item.active\"),\n ifInactiveOptions: true,\n });\n this.hasRemovedSlide = false;\n }\n this._super(...arguments);\n this.$carousel.off('.carousel_item_option');\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * Updates the slide counter.\n *\n * @override\n */\n updateUI: async function () {\n await this._super(...arguments);\n const $items = this.$carousel.find('.carousel-item');\n const $activeSlide = $items.filter('.active');\n const updatedText = ` (${$activeSlide.index() + 1}/${$items.length})`;\n this.counterEl.textContent = updatedText;\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @see this.selectClass for parameters\n */\n addSlideItem(previewMode, widgetValue, params) {\n return new Promise(resolve => {\n this.trigger_up(\"option_update\", {\n optionName: \"Carousel\",\n name: \"add_slide\",\n data: {\n onSuccess: () => resolve(),\n },\n });\n });\n },\n /**\n * Removes the current slide.\n *\n * @see this.selectClass for parameters.\n */\n async removeSlide(previewMode) {\n this.options.wysiwyg.odooEditor.historyPauseSteps();\n const $items = this.$carousel.find('.carousel-item');\n const newLength = $items.length - 1;\n if (!this.removing && newLength > 0) {\n // The active indicator is deleted to ensure that the other\n // indicators will still work after the deletion.\n const $toDelete = $items.filter('.active').add(this.$indicators.find('.active'));\n this.removing = true; // TODO remove in master: kept for stable.\n // Go to the previous slide.\n await new Promise(resolve => {\n this.trigger_up(\"option_update\", {\n optionName: \"Carousel\",\n name: \"slide\",\n data: {\n direction: \"prev\",\n onSuccess: () => resolve(),\n },\n });\n });\n // Remove the slide.\n $toDelete.remove();\n this.$controls.toggleClass(\"d-none\", newLength === 1);\n this.$carousel.trigger(\"content_changed\");\n this.removing = false;\n }\n this.options.wysiwyg.odooEditor.historyUnpauseSteps();\n this.hasRemovedSlide = true;\n },\n /**\n * Goes to next slide or previous slide.\n *\n * @see this.selectClass for parameters\n */\n slide(previewMode, widgetValue, params) {\n this.options.wysiwyg.odooEditor.historyPauseSteps();\n const direction = widgetValue === \"left\" ? \"prev\" : \"next\";\n return new Promise(resolve => {\n this.trigger_up(\"option_update\", {\n optionName: \"Carousel\",\n name: \"slide\",\n data: {\n direction: direction,\n onSuccess: () => {\n this.options.wysiwyg.odooEditor.historyUnpauseSteps();\n resolve();\n },\n },\n });\n });\n },\n});\n\noptions.registry.Parallax = options.Class.extend({\n /**\n * @override\n */\n async start() {\n this.parallaxEl = this.$target.find('> .s_parallax_bg')[0] || null;\n this._updateBackgroundOptions();\n\n this.$target.on('content_changed.ParallaxOption', this._onExternalUpdate.bind(this));\n\n return this._super(...arguments);\n },\n /**\n * @override\n */\n onFocus() {\n // Refresh the parallax animation on focus; at least useful because\n // there may have been changes in the page that influenced the parallax\n // rendering (new snippets, ...).\n // TODO make this automatic.\n if (this.parallaxEl) {\n this._refreshPublicWidgets();\n }\n },\n /**\n * @override\n */\n onMove() {\n this._refreshPublicWidgets();\n },\n /**\n * @override\n */\n destroy() {\n this._super(...arguments);\n this.$target.off('.ParallaxOption');\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Build/remove parallax.\n *\n * @see this.selectClass for parameters\n */\n async selectDataAttribute(previewMode, widgetValue, params) {\n await this._super(...arguments);\n if (params.attributeName !== 'scrollBackgroundRatio') {\n return;\n }\n\n const isParallax = (widgetValue !== '0');\n this.$target.toggleClass('parallax', isParallax);\n this.$target.toggleClass('s_parallax_is_fixed', widgetValue === '1');\n this.$target.toggleClass('s_parallax_no_overflow_hidden', (widgetValue === '0' || widgetValue === '1'));\n if (isParallax) {\n if (!this.parallaxEl) {\n this.parallaxEl = document.createElement('span');\n this.parallaxEl.classList.add('s_parallax_bg');\n this.$target.prepend(this.parallaxEl);\n }\n } else {\n if (this.parallaxEl) {\n this.parallaxEl.remove();\n this.parallaxEl = null;\n }\n }\n\n this._updateBackgroundOptions();\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _computeVisibility(widgetName) {\n return !this.$target.hasClass('o_background_video');\n },\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'selectDataAttribute' && params.parallaxTypeOpt) {\n const attrName = params.attributeName;\n const attrValue = (this.$target[0].dataset[attrName] || params.attributeDefaultValue).trim();\n switch (attrValue) {\n case '0':\n case '1': {\n return attrValue;\n }\n default: {\n return (attrValue.startsWith('-') ? '-1.5' : '1.5');\n }\n }\n }\n return this._super(...arguments);\n },\n /**\n * Updates external background-related option to work with the parallax\n * element instead of the original target when necessary.\n *\n * @private\n */\n _updateBackgroundOptions() {\n this.trigger_up('option_update', {\n optionNames: ['BackgroundImage', 'BackgroundPosition', 'BackgroundOptimize'],\n name: 'target',\n data: this.parallaxEl ? $(this.parallaxEl) : this.$target,\n });\n },\n\n //--------------------------------------------------------------------------\n // Handlers\n //--------------------------------------------------------------------------\n\n /**\n * Called on any snippet update to check if the parallax should still be\n * enabled or not.\n *\n * TODO there is probably a better system to implement to solve this issue.\n *\n * @private\n * @param {Event} ev\n */\n _onExternalUpdate(ev) {\n if (!this.parallaxEl) {\n return;\n }\n const bgImage = this.parallaxEl.style.backgroundImage;\n if (!bgImage || bgImage === 'none' || this.$target.hasClass('o_background_video')) {\n // The parallax option was enabled but the background image was\n // removed: disable the parallax option.\n const widget = this._requestUserValueWidgets('parallax_none_opt')[0];\n widget.enable();\n widget.getParent().close(); // FIXME remove this ugly hack asap\n }\n },\n});\n\noptions.registry.collapse = options.Class.extend({\n /**\n * @override\n */\n start: function () {\n var self = this;\n this.$target.on('shown.bs.collapse hidden.bs.collapse', '[role=\"tabpanel\"]', function () {\n self.trigger_up('cover_update');\n self.$target.trigger('content_changed');\n });\n return this._super.apply(this, arguments);\n },\n /**\n * @override\n */\n onBuilt: function () {\n this._createIDs();\n },\n /**\n * @override\n */\n onClone: function () {\n this._createIDs();\n },\n /**\n * @override\n */\n onMove: function () {\n this._createIDs();\n var $panel = this.$target.find('.collapse').removeData('bs.collapse');\n if ($panel.attr('aria-expanded') === 'true') {\n $panel.closest('.accordion').find('.collapse[aria-expanded=\"true\"]')\n .filter((i, el) => (el !== $panel[0]))\n .collapse('hide')\n .one('hidden.bs.collapse', function () {\n $panel.trigger('shown.bs.collapse');\n });\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * Associates unique ids on collapse elements.\n *\n * @private\n */\n _createIDs: function () {\n let time = new Date().getTime();\n const $tablist = this.$target.closest('[role=\"tablist\"]');\n const $tab = this.$target.find('[role=\"tab\"]');\n const $panel = this.$target.find('[role=\"tabpanel\"]');\n\n const setUniqueId = ($elem, label) => {\n let elemId = $elem.attr('id');\n if (!elemId || $('[id=\"' + elemId + '\"]').length > 1) {\n do {\n time++;\n elemId = label + time;\n } while ($('#' + elemId).length);\n $elem.attr('id', elemId);\n }\n return elemId;\n };\n\n const tablistId = setUniqueId($tablist, 'myCollapse');\n $panel.attr('data-parent', '#' + tablistId);\n $panel.data('parent', '#' + tablistId);\n\n const panelId = setUniqueId($panel, 'myCollapseTab');\n $tab.attr('data-target', '#' + panelId);\n $tab.data('target', '#' + panelId);\n },\n});\n\noptions.registry.WebsiteLevelColor = options.Class.extend({\n specialCheckAndReloadMethodsNames: options.Class.prototype.specialCheckAndReloadMethodsNames\n .concat(['customizeWebsiteLayer2Color']),\n\n /**\n * @see this.selectClass for parameters\n */\n async customizeWebsiteLayer2Color(previewMode, widgetValue, params) {\n if (previewMode) {\n return;\n }\n params.color = params.layerColor;\n params.variable = params.layerGradient;\n let color = undefined;\n let gradient = undefined;\n if (weUtils.isColorGradient(widgetValue)) {\n color = '';\n gradient = widgetValue;\n } else {\n color = widgetValue;\n gradient = '';\n }\n await this.customizeWebsiteVariable(previewMode, gradient, params);\n params.noBundleReload = false;\n return this.customizeWebsiteColor(previewMode, color, params);\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'customizeWebsiteLayer2Color') {\n params.variable = params.layerGradient;\n const gradient = await this._computeWidgetState('customizeWebsiteVariable', params);\n if (gradient) {\n return gradient.substring(1, gradient.length - 1); // Unquote\n }\n params.color = params.layerColor;\n return this._computeWidgetState('customizeWebsiteColor', params);\n }\n return this._super(...arguments);\n },\n});\n\noptions.registry.HeaderNavbar = options.Class.extend({\n /**\n * Particular case: we want the option to be associated on the header navbar\n * in XML so that the related options only appear on navbar click (not\n * header), in a different section, etc... but we still want the target to\n * be the header itself.\n *\n * @constructor\n */\n init() {\n this._super(...arguments);\n this.setTarget(this.$target.closest('#wrapwrap > header'));\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async updateUIVisibility() {\n await this._super(...arguments);\n\n // TODO improve this: this is a big hack so that the \"no mobile\n // hamburger\" option is disabled if it is ever hidden (because of the\n // selection of an hamburger template which is a foreign option). This\n // should be done another way in another place somehow...\n const noHamburgerWidget = this.findWidget('no_hamburger_opt');\n const noHamburgerHidden = noHamburgerWidget.$el.hasClass('d-none');\n if (noHamburgerHidden && noHamburgerWidget.isActive()) {\n this.findWidget('default_hamburger_opt').enable();\n }\n\n // TODO improve this: this is a big hack so that the label of the\n // hamburger option changes if the 'no_hamburger_opt' one is available\n // (= in that case the option controls only the *mobile* hamburger).\n const hamburgerTypeWidget = this.findWidget('header_hamburger_type_opt');\n const labelEl = hamburgerTypeWidget.el.querySelector('we-title');\n if (!this._originalHamburgerTypeLabel) {\n this._originalHamburgerTypeLabel = labelEl.textContent;\n }\n labelEl.textContent = noHamburgerHidden\n ? this._originalHamburgerTypeLabel\n : _t(\"Mobile menu\");\n },\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async start() {\n await this._super(...arguments);\n // TODO Remove in master.\n const signInOptionEl = this.el.querySelector('[data-customize-website-views=\"portal.user_sign_in\"]');\n signInOptionEl.dataset.noPreview = 'true';\n },\n /**\n * @private\n */\n async updateUI() {\n await this._super(...arguments);\n // For all header templates except those in the following array, change\n // the label of the option to \"Mobile Alignment\" (instead of\n // \"Alignment\") because it only impacts the mobile view.\n if (![\"'default'\", \"'hamburger'\", \"'sidebar'\", \"'magazine'\", \"'hamburger-full'\", \"'slogan'\"]\n .includes(weUtils.getCSSVariableValue(\"header-template\"))) {\n const alignmentOptionTitleEl = this.el.querySelector('[data-name=\"header_alignment_opt\"] we-title');\n alignmentOptionTitleEl.textContent = _t(\"Mobile Alignment\");\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * Needs to be done manually for now because data-dependencies\n * doesn't work with \"AND\" conditions.\n * TODO: improve this.\n *\n * @override\n */\n async _computeWidgetVisibility(widgetName, params) {\n switch (widgetName) {\n case 'option_logo_height_scrolled': {\n return !!this.$('.navbar-brand').length;\n }\n case 'no_hamburger_opt': {\n return !weUtils.getCSSVariableValue('header-template').includes('hamburger');\n }\n }\n if (widgetName === 'header_alignment_opt') {\n if (!this.$target[0].querySelector('.o_offcanvas_menu_toggler')) {\n // If mobile menu is \"Default\", hides the alignment option for\n // \"hamburger full\" and \"magazine\" header templates.\n return ![\"'hamburger-full'\", \"'magazine'\"].includes(weUtils.getCSSVariableValue('header-template'));\n }\n return true;\n }\n return this._super(...arguments);\n },\n});\n\nconst VisibilityPageOptionUpdate = options.Class.extend({\n pageOptionName: undefined,\n showOptionWidgetName: undefined,\n shownValue: '',\n\n /**\n * @override\n */\n async start() {\n await this._super(...arguments);\n // When entering edit mode via the URL (enable_editor) the WebsiteNavbar\n // is not yet ReadyForActions because it is waiting for its\n // sub-component EditPageMenu to start edit mode. Then invisible blocks\n // options start (so this option too). But for isShown() to work, the\n // navbar must be ReadyForActions. This is the reason why we can't wait\n // for isShown here, otherwise we would have a deadlock. On one hand the\n // navbar waiting for the invisible snippets options to be started to be\n // ReadyForActions and on the other hand this option which needs the\n // navbar to be ReadyForActions to be started.\n // TODO in master: Use the data-invisible system to get rid of this\n // piece of code.\n this._isShown().then(isShown => {\n this.trigger_up('snippet_option_visibility_update', {show: isShown});\n });\n },\n /**\n * @override\n */\n async onTargetShow() {\n if (await this._isShown()) {\n // onTargetShow may be called even if the element is already shown.\n // In most cases, this is not a problem but here it is as the code\n // that follows clicks on the visibility checkbox regardless of its\n // status. This avoids searching for that checkbox entirely.\n return;\n }\n // TODO improve: here we make a hack so that if we make the invisible\n // header appear for edition, its actual visibility for the page is\n // toggled (otherwise it would be about editing an element which\n // is actually never displayed on the page).\n const widget = this._requestUserValueWidgets(this.showOptionWidgetName)[0];\n widget.enable();\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @see this.selectClass for params\n */\n async visibility(previewMode, widgetValue, params) {\n const show = (widgetValue !== 'hidden');\n await new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'toggle_page_option',\n params: [{name: this.pageOptionName, value: show}],\n onSuccess: () => resolve(),\n onFailure: reject,\n });\n });\n this.trigger_up('snippet_option_visibility_update', {show: show});\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'visibility') {\n const shown = await this._isShown();\n return shown ? this.shownValue : 'hidden';\n }\n return this._super(...arguments);\n },\n /**\n * @private\n * @returns {boolean}\n */\n async _isShown() {\n return new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'get_page_option',\n params: [this.pageOptionName],\n onSuccess: v => resolve(!!v),\n onFailure: reject,\n });\n });\n },\n});\n\noptions.registry.TopMenuVisibility = VisibilityPageOptionUpdate.extend({\n pageOptionName: 'header_visible',\n showOptionWidgetName: 'regular_header_visibility_opt',\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Handles the switching between 3 differents visibilities of the header.\n *\n * @see this.selectClass for params\n */\n async visibility(previewMode, widgetValue, params) {\n await this._super(...arguments);\n await this._changeVisibility(widgetValue);\n // TODO this is hacky but changing the header visibility may have an\n // effect on features like FullScreenHeight which depend on viewport\n // size so we simulate a resize.\n $(window).trigger('resize');\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _changeVisibility(widgetValue) {\n const show = (widgetValue !== 'hidden');\n if (!show) {\n return;\n }\n const transparent = (widgetValue === 'transparent');\n await new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'toggle_page_option',\n params: [{name: 'header_overlay', value: transparent}],\n onSuccess: () => resolve(),\n onFailure: reject,\n });\n });\n if (!transparent) {\n return;\n }\n await new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'toggle_page_option',\n params: [{name: 'header_color', value: ''}],\n onSuccess: () => resolve(),\n onFailure: reject,\n });\n });\n },\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n const _super = this._super.bind(this);\n if (methodName === 'visibility') {\n this.shownValue = await new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'get_page_option',\n params: ['header_overlay'],\n onSuccess: v => resolve(v ? 'transparent' : 'regular'),\n onFailure: reject,\n });\n });\n }\n return _super(...arguments);\n },\n /**\n * @override\n */\n _computeWidgetVisibility(widgetName, params) {\n if (widgetName === 'header_visibility_opt') {\n return this.$target[0].classList.contains('o_header_sidebar') ? '' : 'true';\n }\n return this._super(...arguments);\n },\n /**\n * @override\n */\n _renderCustomXML(uiFragment) {\n // TODO in master: put this in the XML.\n const weSelectEl = uiFragment.querySelector('we-select#option_header_visibility');\n if (weSelectEl) {\n weSelectEl.dataset.name = 'header_visibility_opt';\n }\n },\n});\n\noptions.registry.topMenuColor = options.Class.extend({\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async selectStyle(previewMode, widgetValue, params) {\n await this._super(...arguments);\n const className = widgetValue ? (params.colorPrefix + widgetValue) : '';\n await new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'toggle_page_option',\n params: [{name: 'header_color', value: className}],\n onSuccess: resolve,\n onFailure: reject,\n });\n });\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeVisibility: async function () {\n const show = await this._super(...arguments);\n if (!show) {\n return false;\n }\n return new Promise((resolve, reject) => {\n this.trigger_up('action_demand', {\n actionName: 'get_page_option',\n params: ['header_overlay'],\n onSuccess: value => resolve(!!value),\n onFailure: reject,\n });\n });\n },\n});\n\n/**\n * Manage the visibility of snippets on mobile.\n */\noptions.registry.MobileVisibility = options.Class.extend({\n isTopOption: true,\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async updateUI() {\n await this._super(...arguments);\n const $button = this.$el.find('we-button');\n $button.attr('title', $button.hasClass('active') ? _t(\"Visible on mobile\") : _t(\"Hidden on mobile\"));\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Allows to show or hide the associated snippet in mobile display mode.\n *\n * @see this.selectClass for parameters\n */\n showOnMobile(previewMode, widgetValue, params) {\n // For compatibility with former implementation: remove the previously\n // added `d-md-*` class if any, as it should now be `d-lg-*`.\n if (widgetValue) {\n this.$target[0].classList.remove(`d-md-${this.$target.css('display')}`);\n }\n const classes = `d-none d-lg-${this.$target.css('display')}`;\n this.$target.toggleClass(classes, !widgetValue);\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'showOnMobile') {\n const classList = [...this.$target[0].classList];\n return classList.includes('d-none') &&\n classList.some(className => className.match(/^(d-md-|d-lg-)/g)) ? '' : 'true';\n }\n return await this._super(...arguments);\n },\n});\n\n/**\n * Hide/show footer in the current page.\n */\noptions.registry.HideFooter = VisibilityPageOptionUpdate.extend({\n pageOptionName: 'footer_visible',\n showOptionWidgetName: 'hide_footer_page_opt',\n shownValue: 'shown',\n});\n\n/**\n * Handles the edition of snippet's anchor name.\n */\noptions.registry.anchor = options.Class.extend({\n isTopOption: true,\n\n //--------------------------------------------------------------------------\n // Public\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n start: function () {\n // Generate anchor and copy it to clipboard on click, show the tooltip on success\n this.$button = this.$el.find('we-button');\n const clipboard = new ClipboardJS(this.$button[0], {text: () => this._getAnchorLink()});\n clipboard.on('success', () => {\n const message = sprintf(Markup(_t(\"Anchor copied to clipboard
    Link: %s\")), this._getAnchorLink());\n this.displayNotification({\n type: 'success',\n message: message,\n buttons: [{text: _t(\"Edit\"), click: () => this.openAnchorDialog(), primary: true}],\n });\n });\n\n return this._super.apply(this, arguments);\n },\n /**\n * @override\n */\n onClone: function () {\n this.$target.removeAttr('data-anchor');\n this.$target.filter(':not(.carousel)').removeAttr('id');\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n /**\n * @see this.selectClass for parameters\n */\n openAnchorDialog: function (previewMode, widgetValue, params) {\n var self = this;\n var buttons = [{\n text: _t(\"Save & copy\"),\n classes: 'btn-primary',\n click: function () {\n var $input = this.$('.o_input_anchor_name');\n var anchorName = self._text2Anchor($input.val());\n if (self.$target[0].id === anchorName) {\n // If the chosen anchor name is already the one used by the\n // element, close the dialog and do nothing else\n this.close();\n return;\n }\n\n const alreadyExists = !!document.getElementById(anchorName);\n this.$('.o_anchor_already_exists').toggleClass('d-none', !alreadyExists);\n $input.toggleClass('is-invalid', alreadyExists);\n if (!alreadyExists) {\n self._setAnchorName(anchorName);\n this.close();\n self.$button[0].click();\n }\n },\n }, {\n text: _t(\"Discard\"),\n close: true,\n }];\n if (this.$target.attr('id')) {\n buttons.push({\n text: _t(\"Remove\"),\n classes: 'btn-link ml-auto',\n icon: 'fa-trash',\n close: true,\n click: function () {\n self._setAnchorName();\n },\n });\n }\n new Dialog(this, {\n title: _t(\"Link Anchor\"),\n $content: $(qweb.render('website.dialog.anchorName', {\n currentAnchor: decodeURIComponent(this.$target.attr('id')),\n })),\n buttons: buttons,\n }).open();\n },\n /**\n * @private\n * @param {String} value\n */\n _setAnchorName: function (value) {\n if (value) {\n this.$target.attr({\n 'id': value,\n 'data-anchor': true,\n });\n } else {\n this.$target.removeAttr('id data-anchor');\n }\n this.$target.trigger('content_changed');\n },\n /**\n * Returns anchor text.\n *\n * @private\n * @returns {string}\n */\n _getAnchorLink: function () {\n if (!this.$target[0].id) {\n const $titles = this.$target.find('h1, h2, h3, h4, h5, h6');\n const title = $titles.length > 0 ? $titles[0].innerText : this.data.snippetName;\n const anchorName = this._text2Anchor(title);\n let n = '';\n while (document.getElementById(anchorName + n)) {\n n = (n || 1) + 1;\n }\n this._setAnchorName(anchorName + n);\n }\n return `${window.location.pathname}#${this.$target[0].id}`;\n },\n /**\n * Creates a safe id/anchor from text.\n *\n * @private\n * @param {string} text\n * @returns {string}\n */\n _text2Anchor: function (text) {\n return encodeURIComponent(text.trim().replace(/\\s+/g, '-'));\n },\n});\n\noptions.registry.HeaderBox = options.registry.Box.extend({\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async selectStyle(previewMode, widgetValue, params) {\n if ((params.variable || params.color)\n && ['border-width', 'border-style', 'border-color', 'border-radius', 'box-shadow'].includes(params.cssProperty)) {\n if (previewMode) {\n return;\n }\n if (params.cssProperty === 'border-color') {\n return this.customizeWebsiteColor(previewMode, widgetValue, params);\n }\n return this.customizeWebsiteVariable(previewMode, widgetValue, params);\n }\n return this._super(...arguments);\n },\n /**\n * @override\n */\n async setShadow(previewMode, widgetValue, params) {\n if (params.variable) {\n if (previewMode) {\n return;\n }\n const defaultShadow = this._getDefaultShadow(widgetValue, params.shadowClass);\n return this.customizeWebsiteVariable(previewMode, defaultShadow || 'none', params);\n }\n return this._super(...arguments);\n },\n});\n\noptions.registry.CookiesBar = options.registry.SnippetPopup.extend({\n xmlDependencies: (options.registry.SnippetPopup.prototype.xmlDependencies || []).concat(\n ['/website/static/src/xml/website.cookies_bar.xml']\n ),\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Change the cookies bar layout.\n *\n * @see this.selectClass for parameters\n */\n selectLayout: function (previewMode, widgetValue, params) {\n let websiteId;\n this.trigger_up('context_get', {\n callback: function (ctx) {\n websiteId = ctx['website_id'];\n },\n });\n\n const $template = $(qweb.render(`website.cookies_bar.${widgetValue}`, {\n websiteId: websiteId,\n }));\n\n const $content = this.$target.find('.modal-content');\n const selectorsToKeep = [\n '.o_cookies_bar_text_button',\n '.o_cookies_bar_text_policy',\n '.o_cookies_bar_text_title',\n '.o_cookies_bar_text_primary',\n '.o_cookies_bar_text_secondary',\n ];\n\n if (this.$savedSelectors === undefined) {\n this.$savedSelectors = [];\n }\n\n for (const selector of selectorsToKeep) {\n const $currentLayoutEls = $content.find(selector).contents();\n const $newLayoutEl = $template.find(selector);\n if ($currentLayoutEls.length) {\n // save value before change, eg 'title' is not inside 'discrete' template\n // but we want to preserve it in case of select another layout later\n this.$savedSelectors[selector] = $currentLayoutEls;\n }\n const $savedSelector = this.$savedSelectors[selector];\n if ($newLayoutEl.length && $savedSelector && $savedSelector.length) {\n $newLayoutEl.empty().append($savedSelector);\n }\n }\n\n $content.empty().append($template);\n },\n});\n\n/**\n * Allows edition of 'cover_properties' in website models which have such\n * fields (blogs, posts, events, ...).\n */\noptions.registry.CoverProperties = options.Class.extend({\n /**\n * @constructor\n */\n init: function () {\n this._super.apply(this, arguments);\n\n this.$image = this.$target.find('.o_record_cover_image');\n this.$filter = this.$target.find('.o_record_cover_filter');\n },\n /**\n * @override\n */\n start: function () {\n this.$filterValueOpts = this.$el.find('[data-filter-value]');\n\n return this._super.apply(this, arguments);\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Handles a background change.\n *\n * @see this.selectClass for parameters\n */\n background: async function (previewMode, widgetValue, params) {\n if (widgetValue === '') {\n this.$image.css('background-image', '');\n this.$target.removeClass('o_record_has_cover');\n } else {\n this.$image.css('background-image', `url('${widgetValue}')`);\n this.$target.addClass('o_record_has_cover');\n const $defaultSizeBtn = this.$el.find('.o_record_cover_opt_size_default');\n $defaultSizeBtn.click();\n $defaultSizeBtn.closest('we-select').click();\n }\n\n if (!previewMode) {\n this._updateSavingDataset();\n }\n },\n /**\n * @see this.selectClass for parameters\n */\n filterValue: function (previewMode, widgetValue, params) {\n this.$filter.css('opacity', widgetValue || 0);\n this.$filter.toggleClass('oe_black', parseFloat(widgetValue) !== 0);\n\n if (!previewMode) {\n this._updateSavingDataset();\n }\n },\n /**\n * @override\n */\n selectStyle: async function (previewMode, widgetValue, params) {\n await this._super(...arguments);\n\n if (!previewMode) {\n this._updateSavingDataset(widgetValue);\n }\n },\n /**\n * @override\n */\n selectClass: async function (previewMode, widgetValue, params) {\n await this._super(...arguments);\n\n if (!previewMode) {\n this._updateSavingDataset();\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _computeWidgetState: function (methodName, params) {\n switch (methodName) {\n case 'filterValue': {\n return parseFloat(this.$filter.css('opacity')).toFixed(1);\n }\n case 'background': {\n const background = this.$image.css('background-image');\n if (background && background !== 'none') {\n return background.match(/^url\\([\"']?(.+?)[\"']?\\)$/)[1];\n }\n return '';\n }\n }\n return this._super(...arguments);\n },\n /**\n * @override\n */\n _computeWidgetVisibility: function (widgetName, params) {\n if (params.coverOptName) {\n return this.$target.data(`use_${params.coverOptName}`) === 'True';\n }\n return this._super(...arguments);\n },\n /**\n * TODO: update in master to set data-name values in XML.\n *\n * @override\n */\n async _renderCustomXML(uiFragment) {\n uiFragment.querySelectorAll('[data-cover-opt-name]').forEach(el => {\n el.dataset.name = `${el.dataset.coverOptName}_opt`;\n });\n },\n /**\n * @private\n */\n _updateColorDataset(bgColorStyle = '', bgColorClass = '') {\n this.$target[0].dataset.bgColorStyle = bgColorStyle;\n this.$target[0].dataset.bgColorClass = bgColorClass;\n },\n /**\n * Updates the cover properties dataset used for saving.\n *\n * @private\n */\n _updateSavingDataset(colorValue) {\n const [colorPickerWidget, sizeWidget, textAlignWidget] = this._requestUserValueWidgets('bg_color_opt', 'size_opt', 'text_align_opt');\n // TODO: `o_record_has_cover` should be handled using model field, not\n // resize_class to avoid all of this.\n // Get values from DOM (selected values in options are only available\n // after updateUI)\n const sizeOptValues = sizeWidget.getMethodsParams('selectClass').possibleValues;\n let coverClass = [...this.$target[0].classList].filter(\n value => sizeOptValues.includes(value)\n ).join(' ');\n const bg = this.$image.css('background-image');\n if (bg && bg !== 'none') {\n coverClass += \" o_record_has_cover\";\n }\n const textAlignOptValues = textAlignWidget.getMethodsParams('selectClass').possibleValues;\n const textAlignClass = [...this.$target[0].classList].filter(\n value => textAlignOptValues.includes(value)\n ).join(' ');\n const filterEl = this.$target[0].querySelector('.o_record_cover_filter');\n const filterValue = filterEl && filterEl.style.opacity;\n // Update saving dataset\n this.$target[0].dataset.coverClass = coverClass;\n this.$target[0].dataset.textAlignClass = textAlignClass;\n this.$target[0].dataset.filterValue = filterValue || 0.0;\n // TODO there is probably a better way and this should be refactored to\n // use more standard colorpicker+imagepicker structure\n const ccValue = colorPickerWidget._ccValue;\n const colorOrGradient = colorPickerWidget._value;\n const isGradient = weUtils.isColorGradient(colorOrGradient);\n const isCSSColor = !isGradient && ColorpickerWidget.isCSSColor(colorOrGradient);\n const colorNames = [];\n if (ccValue) {\n colorNames.push(ccValue);\n }\n if (colorOrGradient && !isGradient && !isCSSColor) {\n colorNames.push(colorOrGradient);\n }\n const bgColorClass = weUtils.computeColorClasses(colorNames).join(' ');\n const bgColorStyle = isCSSColor ? `background-color: ${colorOrGradient};` :\n isGradient ? `background-color: rgba(0, 0, 0, 0); background-image: ${colorOrGradient};` : '';\n this._updateColorDataset(bgColorStyle, bgColorClass);\n },\n});\n\noptions.registry.ScrollButton = options.Class.extend({\n /**\n * @override\n */\n start: async function () {\n await this._super(...arguments);\n this.$button = this.$('.o_scroll_button');\n },\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * @see this.selectClass for parameters\n */\n async showScrollButton(previewMode, widgetValue, params) {\n if (widgetValue) {\n this.$button.show();\n } else {\n if (previewMode) {\n this.$button.hide();\n } else {\n this.$button.detach();\n }\n }\n },\n /**\n * Toggles the scroll down button.\n */\n toggleButton: function (previewMode, widgetValue, params) {\n if (widgetValue) {\n if (!this.$button.length) {\n const anchor = document.createElement('a');\n anchor.classList.add(\n 'o_scroll_button',\n 'mb-3',\n 'rounded-circle',\n 'align-items-center',\n 'justify-content-center',\n 'mx-auto',\n 'bg-primary',\n 'o_not_editable',\n );\n anchor.href = '#';\n anchor.contentEditable = \"false\";\n anchor.title = _t(\"Scroll down to next section\");\n const arrow = document.createElement('i');\n arrow.classList.add('fa', 'fa-angle-down', 'fa-3x');\n anchor.appendChild(arrow);\n this.$button = $(anchor);\n }\n this.$target.append(this.$button);\n } else {\n this.$button.detach();\n }\n },\n /**\n * @override\n */\n async selectClass(previewMode, widgetValue, params) {\n await this._super(...arguments);\n // If a \"d-lg-block\" class exists on the section (e.g., for mobile\n // visibility option), it should be replaced with a \"d-lg-flex\" class.\n // This ensures that the section has the \"display: flex\" property\n // applied, which is the default rule for both \"height\" option classes.\n if (params.possibleValues.includes(\"o_half_screen_height\")) {\n if (widgetValue) {\n this.$target[0].classList.replace(\"d-lg-block\", \"d-lg-flex\");\n } else if (this.$target[0].classList.contains(\"d-lg-flex\")) {\n // There are no known cases, but we still make sure that the\n //
    element doesn't have a \"display: flex\" originally.\n this.$target[0].classList.remove(\"d-lg-flex\");\n const sectionStyle = window.getComputedStyle(this.$target[0]);\n const hasDisplayFlex = sectionStyle.getPropertyValue(\"display\") === \"flex\";\n this.$target[0].classList.add(hasDisplayFlex ? \"d-lg-flex\" : \"d-lg-block\");\n }\n }\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n _renderCustomXML(uiFragment) {\n // TODO adapt in master. This sets up a different UI for the image\n // gallery snippet: for this one, we allow to force a specific height\n // in auto mode. It was done in stable as without it, the default height\n // is difficult to understand for the user as it depends on screen\n // height of the one who edited the website and not on the added images.\n // It was also a regression as in <= 11.0, this was a possibility.\n if (this.$target[0].dataset.snippet !== 's_image_gallery') {\n return;\n }\n let minHeightEl = uiFragment.querySelector('[data-name=\"minheight_auto_opt\"]');\n if (!minHeightEl) {\n return;\n }\n minHeightEl = minHeightEl.parentElement;\n minHeightEl.setAttribute('string', _t(\"Min-Height\"));\n const heightEl = document.createElement('we-input');\n heightEl.setAttribute('string', _t(\"\u2514 Height\"));\n heightEl.dataset.dependencies = 'minheight_auto_opt';\n heightEl.dataset.unit = 'px';\n heightEl.dataset.selectStyle = '';\n heightEl.dataset.cssProperty = 'height';\n // For this setting, we need to always force the style (= if the block\n // is naturally 800px tall and the user enters 800px for this setting,\n // we set 800px as inline style anyway). Indeed, this snippet's style\n // is based on the height that is forced but once the related public\n // widgets are started, the inner carousel items receive a min-height\n // which makes it so the snippet \"natural\" height is equal to the\n // initially forced height... so if the style is not forced, it would\n // ultimately be removed by mistake thinking it is not necessary.\n // Note: this is forced as not important as we still need the height to\n // be reset to 'auto' in mobile (generic css rules).\n heightEl.dataset.forceStyle = '';\n uiFragment.appendChild(heightEl);\n },\n /**\n * @override\n */\n _computeWidgetState: function (methodName, params) {\n switch (methodName) {\n case 'toggleButton':\n return !!this.$button.parent().length;\n }\n return this._super(...arguments);\n },\n});\n\noptions.registry.ConditionalVisibility = options.Class.extend({\n /**\n * @constructor\n */\n init() {\n this._super(...arguments);\n this.optionsAttributes = [];\n },\n /**\n * @override\n */\n async start() {\n await this._super(...arguments);\n\n for (const widget of this._userValueWidgets) {\n const params = widget.getMethodsParams();\n if (params.saveAttribute) {\n this.optionsAttributes.push({\n saveAttribute: params.saveAttribute,\n attributeName: params.attributeName,\n // If callWith dataAttribute is not specified, the default\n // field to check on the record will be .value for values\n // coming from another widget than M2M.\n callWith: params.callWith || 'value',\n });\n }\n }\n },\n /**\n * @override\n */\n async onTargetHide() {\n this.$target[0].classList.add('o_conditional_hidden');\n },\n /**\n * @override\n */\n async onTargetShow() {\n this.$target[0].classList.remove('o_conditional_hidden');\n },\n // Todo: remove me in master.\n /**\n * @override\n */\n cleanForSave() {},\n\n //--------------------------------------------------------------------------\n // Options\n //--------------------------------------------------------------------------\n\n /**\n * Inserts or deletes record's id and value in target's data-attributes\n * if no ids are selected, deletes the attribute.\n *\n * @see this.selectClass for parameters\n */\n selectRecord(previewMode, widgetValue, params) {\n const recordsData = JSON.parse(widgetValue);\n if (recordsData.length) {\n this.$target[0].dataset[params.saveAttribute] = widgetValue;\n } else {\n delete this.$target[0].dataset[params.saveAttribute];\n }\n\n this._updateCSSSelectors();\n },\n /**\n * Selects a value for target's data-attributes.\n * Should be used instead of selectRecord if the visibility is not related\n * to database values.\n *\n * @see this.selectClass for parameters\n */\n selectValue(previewMode, widgetValue, params) {\n if (widgetValue) {\n const widgetValueIndex = params.possibleValues.indexOf(widgetValue);\n const value = [{value: widgetValue, id: widgetValueIndex}];\n this.$target[0].dataset[params.saveAttribute] = JSON.stringify(value);\n } else {\n delete this.$target[0].dataset[params.saveAttribute];\n }\n\n this._updateCSSSelectors();\n },\n /**\n * Opens the toggler when 'conditional' is selected.\n *\n * @override\n */\n async selectDataAttribute(previewMode, widgetValue, params) {\n await this._super(...arguments);\n\n if (params.attributeName === 'visibility') {\n const targetEl = this.$target[0];\n if (widgetValue === 'conditional') {\n const collapseEl = this.$el.children('we-collapse')[0];\n this._toggleCollapseEl(collapseEl);\n } else {\n // TODO create a param to allow doing this automatically for genericSelectDataAttribute?\n delete targetEl.dataset.visibility;\n\n for (const attribute of this.optionsAttributes) {\n delete targetEl.dataset[attribute.saveAttribute];\n delete targetEl.dataset[`${attribute.saveAttribute}Rule`];\n }\n }\n this.trigger_up('snippet_option_visibility_update', {show: true});\n } else if (!params.isVisibilityCondition) {\n return;\n }\n\n this._updateCSSSelectors();\n },\n\n //--------------------------------------------------------------------------\n // Private\n //--------------------------------------------------------------------------\n\n /**\n * @override\n */\n async _computeWidgetState(methodName, params) {\n if (methodName === 'selectRecord') {\n return this.$target[0].dataset[params.saveAttribute] || '[]';\n }\n if (methodName === 'selectValue') {\n const selectedValue = this.$target[0].dataset[params.saveAttribute];\n return selectedValue ? JSON.parse(selectedValue)[0].value : params.attributeDefaultValue;\n }\n return this._super(...arguments);\n },\n /**\n * Reads target's attributes and creates CSS selectors.\n * Stores them in data-attributes to then be reapplied by\n * content/inject_dom.js (ideally we should saved them in a