odoo.define('website_sale.cart', function (require) { 'use strict'; var publicWidget = require('web.public.widget'); var core = require('web.core'); var _t = core._t; var timeout; publicWidget.registry.websiteSaleCartLink = publicWidget.Widget.extend({ selector: '#top_menu a[href$="/shop/cart"]', events: { 'mouseenter': '_onMouseEnter', 'mouseleave': '_onMouseLeave', 'click': '_onClick', }, /** * @constructor */ init: function () { this._super.apply(this, arguments); this._popoverRPC = null; }, /** * @override */ start: function () { this.$el.popover({ trigger: 'manual', animation: true, html: true, title: function () { return _t("My Cart"); }, container: 'body', placement: 'auto', template: '' }); return this._super.apply(this, arguments); }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * @private * @param {Event} ev */ _onMouseEnter: function (ev) { var self = this; clearTimeout(timeout); $(this.selector).not(ev.currentTarget).popover('hide'); timeout = setTimeout(function () { if (!self.$el.is(':hover') || $('.mycart-popover:visible').length) { return; } self._popoverRPC = $.get("/shop/cart", { type: 'popover', }).then(function (data) { self.$el.data("bs.popover").config.content = data; self.$el.popover("show"); $('.popover').on('mouseleave', function () { self.$el.trigger('mouseleave'); }); }); }, 300); }, /** * @private * @param {Event} ev */ _onMouseLeave: function (ev) { var self = this; setTimeout(function () { if ($('.popover:hover').length) { return; } if (!self.$el.is(':hover')) { self.$el.popover('hide'); } }, 1000); }, /** * @private * @param {Event} ev */ _onClick: function (ev) { // When clicking on the cart link, prevent any popover to show up (by // clearing the related setTimeout) and, if a popover rpc is ongoing, // wait for it to be completed before going to the link's href. Indeed, // going to that page may perform the same computation the popover rpc // is already doing. clearTimeout(timeout); if (this._popoverRPC && this._popoverRPC.state() === 'pending') { ev.preventDefault(); var href = ev.currentTarget.href; this._popoverRPC.then(function () { window.location.href = href; }); } }, }); }); odoo.define('website_sale.website_sale_category', function (require) { 'use strict'; var publicWidget = require('web.public.widget'); publicWidget.registry.websiteSaleCategory = publicWidget.Widget.extend({ selector: '#o_shop_collapse_category', events: { 'click .fa-chevron-right': '_onOpenClick', 'click .fa-chevron-down': '_onCloseClick', }, //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- /** * @private * @param {Event} ev */ _onOpenClick: function (ev) { var $fa = $(ev.currentTarget); $fa.parent().siblings().find('.fa-chevron-down:first').click(); $fa.parents('li').find('ul:first').show('normal'); $fa.toggleClass('fa-chevron-down fa-chevron-right'); }, /** * @private * @param {Event} ev */ _onCloseClick: function (ev) { var $fa = $(ev.currentTarget); $fa.parent().find('ul:first').hide('normal'); $fa.toggleClass('fa-chevron-down fa-chevron-right'); }, }); }); odoo.define('website_sale.website_sale', function (require) { 'use strict'; var core = require('web.core'); var config = require('web.config'); var publicWidget = require('web.public.widget'); var VariantMixin = require('sale.VariantMixin'); var wSaleUtils = require('website_sale.utils'); const wUtils = require('website.utils'); require("web.zoomodoo"); publicWidget.registry.WebsiteSale = publicWidget.Widget.extend(VariantMixin, { selector: '.oe_website_sale', events: _.extend({}, VariantMixin.events || {}, { 'change form .js_product:first input[name="add_qty"]': '_onChangeAddQuantity', 'mouseup .js_publish': '_onMouseupPublish', 'touchend .js_publish': '_onMouseupPublish', 'change .oe_cart input.js_quantity[data-product-id]': '_onChangeCartQuantity', 'click .oe_cart a.js_add_suggested_products': '_onClickSuggestedProduct', 'click a.js_add_cart_json': '_onClickAddCartJSON', 'click .a-submit': '_onClickSubmit', 'change form.js_attributes input, form.js_attributes select': '_onChangeAttribute', 'mouseup form.js_add_cart_json label': '_onMouseupAddCartLabel', 'touchend form.js_add_cart_json label': '_onMouseupAddCartLabel', 'click .show_coupon': '_onClickShowCoupon', 'submit .o_wsale_products_searchbar_form': '_onSubmitSaleSearch', 'change select[name="country_id"]': '_onChangeCountry', 'change #shipping_use_same': '_onChangeShippingUseSame', 'click .toggle_summary': '_onToggleSummary', 'click #add_to_cart, #buy_now, #products_grid .o_wsale_product_btn .a-submit': 'async _onClickAdd', 'click input.js_product_change': 'onChangeVariant', 'change .js_main_product [data-attribute_exclusions]': 'onChangeVariant', 'change oe_optional_products_modal [data-attribute_exclusions]': 'onChangeVariant', }), /** * @constructor */ init: function () { this._super.apply(this, arguments); this._changeCartQuantity = _.debounce(this._changeCartQuantity.bind(this), 500); this._changeCountry = _.debounce(this._changeCountry.bind(this), 500); this.isWebsite = true; delete this.events['change .main_product:not(.in_cart) input.js_quantity']; delete this.events['change [data-attribute_exclusions]']; }, /** * @override */ start() { const def = this._super(...arguments); this._applyHashFromSearch(); _.each(this.$('div.js_product'), function (product) { $('input.js_product_change', product).first().trigger('change'); }); // This has to be triggered to compute the "out of stock" feature and the hash variant changes this.triggerVariantChange(this.$el); this.$('select[name="country_id"]').change(); core.bus.on('resize', this, function () { if (config.device.size_class === config.device.SIZES.XL) { $('.toggle_summary_div').addClass('d-none d-xl-block'); } }); this._startZoom(); window.addEventListener('hashchange', () => { this._applyHash(); this.triggerVariantChange(this.$el); }); return def; }, /** * The selector is different when using list view of variants. * * @override */ getSelectedVariantValues: function ($container) { var combination = $container.find('input.js_product_change:checked') .data('combination'); if (combination) { return combination; } return VariantMixin.getSelectedVariantValues.apply(this, arguments); }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- _applyHash: function () { var hash = window.location.hash.substring(1); if (hash) { var params = $.deparam(hash); if (params['attr']) { var attributeIds = params['attr'].split(','); var $inputs = this.$('input.js_variant_change, select.js_variant_change option'); _.each(attributeIds, function (id) { var $toSelect = $inputs.filter('[data-value_id="' + id + '"]'); if ($toSelect.is('input[type="radio"]')) { $toSelect.prop('checked', true); } else if ($toSelect.is('option')) { $toSelect.prop('selected', true); } }); this._changeColorAttribute(); } } }, /** * Sets the url hash from the selected product options. * * @private */ _setUrlHash: function ($parent) { var $attributes = $parent.find('input.js_variant_change:checked, select.js_variant_change option:selected'); var attributeIds = _.map($attributes, function (elem) { return $(elem).data('value_id'); }); history.replaceState(undefined, undefined, '#attr=' + attributeIds.join(',')); }, /** * Set the checked color active. * * @private */ _changeColorAttribute: function () { $('.css_attribute_color').removeClass("active") .filter(':has(input:checked)') .addClass("active"); }, /** * @private */ _changeCartQuantity: function ($input, value, $dom_optional, line_id, productIDs) { _.each($dom_optional, function (elem) { $(elem).find('.js_quantity').text(value); productIDs.push($(elem).find('span[data-product-id]').data('product-id')); }); $input.data('update_change', true); this._rpc({ route: "/shop/cart/update_json", params: { line_id: line_id, product_id: parseInt($input.data('product-id'), 10), set_qty: value }, }).then(function (data) { $input.data('update_change', false); var check_value = parseInt($input.val() || 0, 10); if (isNaN(check_value)) { check_value = 1; } if (value !== check_value) { $input.trigger('change'); return; } if (!data.cart_quantity) { return window.location = '/shop/cart'; } wSaleUtils.updateCartNavBar(data); $input.val(data.quantity); $('.js_quantity[data-line-id='+line_id+']').val(data.quantity).html(data.quantity); if (data.warning) { var cart_alert = $('.oe_cart').parent().find('#data_warning'); if (cart_alert.length === 0) { $('.oe_cart').prepend(''); } else { cart_alert.html(' ' + data.warning); } $input.val(data.quantity); } }); }, /** * @private */ _changeCountry: function () { if (!$("#country_id").val()) { return; } this._rpc({ route: "/shop/country_infos/" + $("#country_id").val(), params: { mode: $("#country_id").attr('mode'), }, }).then(function (data) { // placeholder phone_code $("input[name='phone']").attr('placeholder', data.phone_code !== 0 ? '+'+ data.phone_code : ''); // populate states and display var selectStates = $("select[name='state_id']"); // dont reload state at first loading (done in qweb) if (selectStates.data('init')===0 || selectStates.find('option').length===1) { if (data.states.length || data.state_required) { selectStates.html(''); _.each(data.states, function (x) { var opt = $('