1/** 2 * Front JS 3 */ 4 5jQuery( document ).ready( 6 function ( $ ) { 7 8 /** 9 * Init the colorpicker input 10 */ 11 initColorpicker = function() { 12 // Customizable args for wpColorPicker function. 13 var colorPicker_opt = { 14 color: false, // If Iris is attached to an input element, it will first try to pick up its value attribute. Otherwise, you can supply a color of any type that Color.js supports. (Hex, rgb, and hsl are good bets.). 15 mode: 'hsl', // Iris can sport a variety of looks. It supports hsl and ââ¬Ëhsvââ¬â¢ modes depending on your needs. 16 controls: { 17 horiz: 's', // horizontal defaults to saturation. 18 vert: 'l', // vertical defaults to lightness. 19 strip: 'h' // right strip defaults to hue. 20 }, 21 hide: true, // Hide colorpickers by default. 22 target: false, // a DOM element / jQuery selector that the element will be appended within. Only used when called on an input. 23 width: 180, // the width of the collection of UI elements. 24 palettes: false, // show a palette of basic colors beneath the square. 25 change: function(event, ui) { 26 let pickerContainer = $( this ).closest( '.wp-picker-container' ); 27 let pickerInputWrap = pickerContainer.find( '.wp-picker-input-wrap' ); 28 let placeholderElement = pickerContainer.find( '.wp-picker-custom-placeholder' );
29 let clearElement = pickerContainer.find( '.wp-picker-default-custom' ); 30 let colorPickerShow = pickerContainer.find( '.wp-color-picker' ).data( 'addon-colorpicker-show' ); 31 let defaultColor = pickerContainer.find( '.wp-color-picker' ).data( 'default-color' ); 32 let colorPickerInput = pickerInputWrap.find( 'input.wp-color-picker' ); 33 let addonOpt = $(colorPickerInput[0]); 34 35 36 // Placeholder option to hide or not the necessary elements. 37 if ( 'placeholder' === colorPickerShow ) { 38 if ( '' !== ui.color.toString() || 'undefined' !== ui.color.toString() ) { 39 pickerInputWrap.find( '.wp-color-picker' ).show(); 40 placeholderElement.hide(); 41 clearElement.show(); 42 placeholderElement.css( 'line-height', '3.0' ); 43 } 44 } 45 46 clearElement.removeClass( 'default_color' ); 47 if ( defaultColor !== ui.color.toString() ) { 48 clearElement.addClass( 'default_color' ); 49 } 50 51 $( document ).trigger( 'wapo-colorpicker-change', addonOpt ); 52 53 }, 54 clear: function(event, ui) { 55 56 let pickerContainer = $( this ).closest( '.wp-picker-container' ); 57 let pickerInputWrap = pickerContainer.find( '.wp-picker-input-wrap' ); 58 let placeholderElement = pickerContainer.find( '.wp-picker-custom-placeholder' );
59 let clearElement = pickerContainer.find( '.wp-picker-default-custom' ); 60 let colorPickerShow = pickerContainer.find( '.wp-color-picker' ).data( 'addon-colorpicker-show' ); 61 let colorPickerInput = pickerInputWrap.find( 'input.wp-color-picker' ); 62 let addonOpt = $(colorPickerInput[0]); 63 64 // Placeholder option to hide or not the necessary elements. 65 if ( 'placeholder' === colorPickerShow ) { 66 pickerInputWrap.find( '.wp-color-picker' ).hide(); 67 placeholderElement.show(); 68 clearElement.hide(); 69 placeholderElement.css( 'line-height', '0' ); 70 } 71 72 $( document ).trigger( 'wapo-colorpicker-clear', addonOpt[0] ); 73 74 } 75 }; 76 77 function inicializeAddonColorpickers() { 78 79 // Initialize each colorpicker with wpColorPicker function. 80 $( '.yith-wapo-block .yith-wapo-addon-type-colorpicker .wp-color-picker' ).each( 81 function() { 82 $( this ).wpColorPicker( colorPicker_opt ); 83 84 let pickerContainer = $( this ).closest( '.wp-picker-container' ); 85 let pickerText = pickerContainer.find( 'button .wp-color-result-text' ); 86 let clearButton = pickerContainer.find( '.wp-picker-default' ); 87 let pickerInputWrap = pickerContainer.find( '.wp-picker-input-wrap' ); 88 let colorPickerShow = $( this ).data( 'addon-colorpicker-show' ); 89 let placeholder = $( this ).data( 'addon-placeholder' ); 90 91 // Hide always the picker text 92 pickerText.html( '' ); 93 94 // Create an custom element to show the custom Clear button. 95 let wrap_main1 = $( this ).parents( '.wp-picker-container' ), 96 wrap1 = wrap_main1.find( '.wp-picker-input-wrap' ); 97 98 if ( ! wrap_main1.hasClass( 'yith-wapo-colorpicker-initialized' ) ) { 99 wrap_main1.addClass( 'yith-wapo-colorpicker-initialized' ); 100 } 101 102 if ( ! wrap1.find( '.wp-picker-default-custom' ).length ) { 103 var button = $( '<span/>' ).attr( 104 { 105 class: 'wp-picker-default-custom' 106 } 107 ); 108 wrap1.find( '.wp-picker-default, .wp-picker-clear' ).wrap( button ); 109 } 110 111 // If it's placeholder option, create a custom element to show the placeholder label. 112 if ( 'placeholder' === colorPickerShow ) { 113 pickerInputWrap.find( '.wp-color-picker' ).hide(); 114 if ( ! pickerInputWrap.find( '.wp-picker-custom-placeholder' ).length ) { 115 var placeholder_el = $( '<span/>' ).attr( 116 { 117 class: 'wp-picker-custom-placeholder', 118 } 119 ); 120 placeholder_el.html( placeholder ); 121 pickerInputWrap.find( '.screen-reader-text' ).before( placeholder_el ); 122 } 123 let clearElement = pickerContainer.find( '.wp-picker-default-custom' ); 124 let placeholderElement = pickerContainer.find( '.wp-picker-custom-placeholder' ); 125 126 clearElement.hide(); 127 placeholderElement.css( 'line-height', '0' ); 128 } 129 130 clearButton.trigger( 'click' ); 131 132 } 133 ); 134 } 135 136 $( document ).on( 'yith-wapo-after-reload-addons', inicializeAddonColorpickers ); 137 $( document ).on( 'yith-wapo-after-reload-addons', initDatePickers ); 138 139 checkColorPickerOnInput = function() { 140 $( document ).on( 141 'click', 142 function (e) { 143 if ( ! $( e.target ).is( '.yith-wapo-colorpicker-container .iris-picker, .yith-wapo-colorpicker-container .iris-picker-inner' ) ) { 144 let initializedColorPickers = $( '.yith-wapo-colorpicker-container .yith-wapo-colorpicker-initialized .wp-color-picker' ); 145 if ( initializedColorPickers.length > 0 ) { 146 initializedColorPickers.iris( 'hide' ); 147 } 148 return; 149 } 150 } 151 ); 152 $( '.yith-wapo-colorpicker-container .yith-wapo-colorpicker-initialized .wp-color-picker' ).click( 153 function ( event ) { 154 $( this ).iris( 'show' ); 155 return; 156 } 157 ); 158 }; 159 160 inicializeAddonColorpickers(); 161 checkColorPickerOnInput(); 162 163 }; 164 165 /** 166 * Init the datepicker input 167 */ 168 initDatePickers = function() { 169 170 // Initialize each colorpicker with wpColorPicker function. 171 $( '.yith-wapo-block .yith_wapo_date.datepicker' ).each( 172 function() { 173 let datepicker_input = $( this ); 174 initDatePicker( datepicker_input ); 175 } 176 ); 177 178 179 }; 180 181 initTimePicker = function( datepicker_input ) { 182 let params = datepicker_input.data( 'params' ),
183 timeData = params.time_data ? params.time_data : '', 184 showTimeSelector = params.show_time_selector ? params.show_time_selector : ''; 185 186 if ( typeof timeData === 'object' && timeData !== null ) { 187 timeData = Object.values(timeData); 188 } 189 if ( showTimeSelector ) { 190 setTimeout( function() { 191 if ( ! $('#wapo-datepicker-time').length ) { 192 var timeDataHTML = '', 193 tempTimeEl = datepicker_input.closest( '.date-container' ).find( '.temp-time' ).text(); 194 $( timeData ).each(function( index, value ) { 195 if ( value !== tempTimeEl ) { 196 timeDataHTML += '<option>' + value + '</option>' 197 } else { 198 timeDataHTML += '<option selected>' + value + '</option>' 199 } 200 } ); 201 var timeHTML = '<div id="wapo-datepicker-time"><label>' + yith_wapo.i18n.datepickerSetTime + '</label><select id="wapo-datepicker-time-select">' + 202 timeDataHTML 203 + '</select></div>' 204 + '<div id="wapo-datepicker-save"><button>' + yith_wapo.i18n.datepickerSaveButton + '</button></div>'; 205 206 $( timeHTML ).appendTo('#ui-datepicker-div'); 207 } 208 }, 10 ); 209 } 210 } 211 212 initDatePicker = function( datepicker_input ) { 213 214 var params = datepicker_input.data( 'params' ), 215 minimumDate = '', 216 maximumDate = '', 217 startYear = params.start_year ? params.start_year : '', 218 endYear = params.end_year ? params.end_year : '', 219 defaultDateSelected = params.default_date ? params.default_date : '', 220 dateFormat = params.date_format ? params.date_format : '', 221 additional_opts = params.additional_opts ? params.additional_opts : ''; 222 223 if ( startYear ) { 224 minimumDate = new Date( params.start_year, '00', '01' ); 225 } 226 if ( endYear ) { 227 maximumDate = new Date( params.end_year, '11', '31' ); 228 } 229 230 // datepicker options: https://api.jqueryui.com/datepicker/ 231 232 var datePicker_opts = { 233 minDate: minimumDate, 234 maxDate: maximumDate, 235 defaultDate: defaultDateSelected, 236 dateFormat: dateFormat, 237 beforeShowDay: function( date ) { 238 let params = datepicker_input.data( 'params' ), 239 selectableDaysOpt = params.selectable_days_opt ? params.selectable_days_opt : '', 240 selectableDays = params.selectable_days ? params.selectable_days : '', 241 selectedItems = params.selected_items ? params.selected_items : '', 242 enabled = params.enable_disable_date_rules ? params.enable_disable_date_rules : '', 243 returnValue = true; 244 245 enabled = ( 'enable' === enabled ) ? 1 : 0; 246 247 if ( enabled ) { 248 returnValue = false; 249 } 250 251 // Selectable days (MIN/MAX) 252 if ( 'days' === selectableDaysOpt || 'date' === selectableDaysOpt ) { 253 let currentDate = date.getDate() + '-' + ( date.getMonth() + 1 ) + '-' + date.getFullYear(); 254 if ( -1 === $.inArray( currentDate, selectableDays ) ) { 255 returnValue = false; 256 return false; 257 } 258 //Disable days before current day. 259 } else if ( 'before' === selectableDaysOpt ) { 260 let currentDate = date.getTime(); 261 let todayDate = new Date(); 262 let currentHour = new Date().getHours(); 263 todayDate.setHours( 0, 0, 0, 0 ); // Set date to midnight 264 todayDate = todayDate.getTime(); 265 if ( currentDate < todayDate || ( yith_wapo.disableCurrentDayBasedOnTime && currentDate <= todayDate && currentHour >= yith_wapo.datepickerHourToCheck ) ){ 266 return false; 267 } 268 } 269 270 // Selected Items (Specific days) 271 if ( selectedItems.length > 0 ) { 272 selectedItems = JSON.parse( selectedItems ); 273 $.each( selectedItems, function( i, items ) { 274 if ( 'days' === i ) { 275 let currentDate = new Date( date ); 276 $.each( items, function ( i, item ) { 277 let [ item_y, item_m, item_d ] = item.split('-'); 278 let selectedDay = new Date( parseInt(item_y), parseInt(item_m) - 1, parseInt(item_d) ); 279 if (currentDate.toDateString() === selectedDay.toDateString()) { 280 returnValue = !! enabled; 281 return false; 282 } 283 }); 284 } else if ( 'daysweek' === i ) { 285 let dayWeek = date.getDay(); 286 $.each( items, function ( i, item ) { 287 $.each( item, function (e, day) { 288 if (dayWeek == day) { 289 returnValue = !! enabled; 290 return false;
291 } 292 }); 293 }); 294 } else if ( 'months' === i ) { 295 let dateMonth = date.getMonth(); 296 $.each( items, function( i, item ) { 297 $.each( item, function( e, month ) { 298 if ( dateMonth == month -1 ) { 299 returnValue = !! enabled; 300 return false; 301 } 302 } ); 303 } ); 304 } else if ( 'years' === i ) { 305 let dateYear = date.getFullYear(); 306 $.each( items, function( i, item ) { 307 $.each( item, function( e, year ) { 308 if ( dateYear == year ) { 309 returnValue = !! enabled; 310 return false; 311 } 312 } ); 313 } ); 314 } 315 }); 316 } 317 318 // Enable or disable specific date using WP-Hooks 319 const { addonId, optionId } = datepicker_input.attr( 'name' ).match( /\[(?<addonId>[\d]+)-(?<optionId>[\d]+)\]$/ ).groups; 320 returnValue = wp.hooks.applyFilters( 'yith_wapo_addon_datepicker_before_show_day', returnValue, date, addonId, optionId ); 321 322 if ( returnValue ) { 323 return [true]; 324 } 325 return [false]; 326 }, 327 beforeShow: function ( datepicker ) { 328 initTimePicker( datepicker_input ); 329 }, 330 onSelect: function( dateText, obj ) { 331 if ( $( obj.dpDiv ).find( '#wapo-datepicker-time-select' ).length > 0 ) { 332 let timeSelected = $( obj.dpDiv ).find( '#wapo-datepicker-time-select' ).val(), 333 tempTimeEl = $( this ).closest( '.date-container' ).find( '.temp-time' ); 334 $( this ).val( dateText + ' ' + timeSelected ); 335 tempTimeEl.text( timeSelected ); 336 } 337 338 let addonOpt = $( '#' + obj.id ); 339 addonOpt.change(); 340 341 }, 342 onChangeMonthYear: function( year, month, inst ) { 343 initTimePicker( datepicker_input ); 344 }, 345 onUpdateDatepicker: function( ins ) { 346 $( '#ui-datepicker-div' ).attr( 'wapo-option-id', ins.id ); 347 }, 348 onClose: function( date, ins ) { 349 $( this ).trigger( 'yith_wapo_date_field_updated', date ); 350 }, 351 } 352 353 // additional parameters added with the yith_wapo_datepicker_options filter 354 datePicker_opts = Object.assign( datePicker_opts, additional_opts ); 355 356 datepicker_input.datepicker( 357 datePicker_opts 358 ); 359 360 } 361 362 initColorpicker(); 363 initDatePickers(); 364 } 365); 366 367jQuery( document ).ready( 368 function ($) { 369 var firstVariationLoading = false; 370 var wapoDOM = { 371 editProductCartLink : '.yith-wapo-edit-product-cart', 372 individualAddons : '.yith-wapo-individual-addons', 373 cartPopup : '.yith-wapo-popup', 374 popupOverlay : '.yith-wapo-overlay', 375 popupClose : '.yith-wapo-close', 376 popupWrapper : '.yith-wapo-wrapper', 377 popupContent : '.yith-wapo-content', 378 popupFooter : '.yith-wapo-footer', 379 addToCartButton : '.yith-wapo-popup .single_add_to_cart_button', 380 popupForm : '.yith-wapo-popup form.cart', 381 popupVariationForm : '.yith-wapo-popup form.variations_form', 382 addonsContainer : '#yith-wapo-container', 383 hiddenItemKey : '.yith-wapo-cart-item-key', 384 addonImageInput : '#yith_wapo_product_img', 385 formCart : 'form.cart', 386 totalPriceTable : '#wapo-total-price-table', 387 wcProductGallery : '.woocommerce-product-gallery', 388 variationForm : 'form.variations_form', 389 totalsBox : { 390 productPrice : '#wapo-total-product-price', 391 optionsPrice : '#wapo-total-options-price', 392 orderPrice : '#wapo-total-order-price' 393 } 394 } 395 396 var wapoArgs = { 397 numDecimals : parseInt(yith_wapo.number_decimals) 398 } 399 400 var pa_modal = $( wapoDOM.cartPopup ); 401 402 initJS = function() { 403 if ( $( wapoDOM.variationForm ).length < 1 ) { // If it's a variable product, we don't calculate price initially. Only on found_variation trigger. 404 calculateTotalAddonsPrice(); 405 } 406 checkDefaultOptionsOnLoad(); 407 moveEditProductLinkOnCart(); 408 409 $( document ).on( 'updated_wc_div', moveEditProductLinkOnCart ); // Move link after updating cart. 410 $( document ).on( 'click', wapoDOM.addToCartButton, addToCart ); 411 $( document ).on( 'click', wapoDOM.editProductCartLink, openEditAddonsModal ); 412 $( document ).on( 'click', wapoDOM.popupClose + ',' + wapoDOM.popupOverlay, closeModal ); 413 $( window ).on( "resize", centerEditModal ); 414 415 } 416 417 addToCart = function(e) { 418 419 e.preventDefault(); 420 e.stopPropagation(); 421 422 if ( ! checkAddonsRequirements() ) { 423 return false;
424 } 425 426 var cartItemKey = $( wapoDOM.hiddenItemKey ).val(); 427 var productId = $( wapoDOM.popupVariationForm ).find( '.product_id' ).val(); 428 var variationId = $( wapoDOM.popupVariationForm ).find( '.variation_id' ).val(); 429 430 var individualCartItems = $( '.yith-wapo-individual-addons.parent-key-' + cartItemKey ); 431 var individualCartKeys = []; 432 433 $.each( individualCartItems, function() { 434 individualCartKeys.push( $(this).data( 'cart-item-key' ) ); 435 } ); 436 437 var data = { 438 'action' : 'ywapo_update_addons_on_cart_item', 439 'cart_item_key' : cartItemKey, 440 'individual_item_key' : individualCartKeys, 441 'product_id' : productId, 442 'variation_id' : variationId, 443 'addons' : $( wapoDOM.popupForm ).serializeArray(), 444 'security' : yith_wapo.addons_nonce, 445 } 446 447 $.ajax( 448 { 449 url: yith_wapo.ajaxurl, 450 type: 'post', 451 data: data, 452 beforeSend: function(){ 453 454 }, 455 error: function (xhr, status, error) { 456 // Code to handle errors. 457 console.error('AJAX request failed: ', status, error); 458 }, 459 success: function (response) { 460 if (response) { 461 if ( response['success'] ) { 462 closeModal(); 463 464 if ( yith_wapo.wc_blocks.has_cart_block ) { 465 // WC Blocks cart update. 466 const CART_STORE_KEY = window.wc.wcBlocksData.CART_STORE_KEY; 467 468 // Returns an action object used in signalling that the resolution cache should be invalidated for the entire store. 469 window.wp.data.dispatch( CART_STORE_KEY ).invalidateResolutionForStore(); 470 // Return and update cart data. 471 window.wp.data.select( CART_STORE_KEY ).getCartData(); 472 473 } else { 474 $( document ).trigger( 'wc_update_cart' ); 475 } 476 } 477 } 478 } 479 } 480 ); 481 } 482 483 centerEditModal = function() { 484 485 var t = $( wapoDOM.popupWrapper ), 486 window_w = $(window).width(), 487 window_h = $(window).height(), 488 width = yith_wapo.isMobile ? $(window).width() : $(window).width() / 1.5, 489 height = yith_wapo.isMobile ? $(window).height() : $(window).height() / 1.3; 490 491 t.css({ 492 'left' : ( ( window_w/2 ) - ( width/2 ) ), 493 'top' : ( ( window_h/2 ) - ( height/2 ) ), 494 'width' : width + 'px', 495 'height' : height + 'px' 496 }); 497 }; 498 499 loadProduct = function( link ) { 500 501 502 $( wapoDOM.popupWrapper ).block({ message: null, overlayCSS: { opacity: 0 } }); 503 504 var productId = link.data( 'product_id' ); 505 var variationId = link.data( 'variation_id' ); 506 var cartItemKey = link.data( 'cart-item-key' ); 507 508 var data = { 509 'action' : 'ywapo_load_product_template', 510 'product_id' : productId, 511 'variation_id' : variationId, 512 'cart_item_key' : cartItemKey, 513 'security' : yith_wapo.addons_nonce, 514 } 515 516 $.ajax( 517 { 518 url : yith_wapo.ajaxurl, 519 type : 'post', 520 data : data, 521 error: function(xhr, status, error) { 522 // Code to handle errors 523 console.error('AJAX request failed: ', status, error); 524 }, 525 beforeSend: function() { 526 link.block({ 527 message : null, 528 overlayCSS: { 529 background: '#fff url(' + yith_wapo.loader + ') no-repeat center/30%', 530 opacity : 0.5, 531 cursor : 'none' 532 } 533 }); 534 }, 535 success : function( response ) { 536 if ( response && response['success'] ) { 537 538 centerEditModal(); 539 540 var variation = response['variation'] && '' !== response['variation'] ? response['variation'] : undefined; 541 var quantity = response['quantity'] ?? ''; 542 var wcclData = response['wccl-data'] ?? ''; 543 var addonImageURL = response['addon_image'] ?? ''; 544 var quantities = response['quantities'] ?? ''; 545 var addonsSelected = response['addons'] ?? ''; 546 547 // Add the product page to the content. 548 $( wapoDOM.popupContent ).html( response['html'] ); 549 550 if ( typeof wc_add_to_cart_variation_params !== 'undefined' ) { 551 $(wapoDOM.popupVariationForm).each(function () { 552 $(this).wc_variation_form(); 553 }); 554 } 555 556 // Init product gallery.
557 $( wapoDOM.wcProductGallery ).wc_product_gallery(); 558 559 // Set variations. 560 if ( variation != undefined ) { 561 setVariation( variation ); 562 } 563 564 // Init datepickers. 565 initDatePickers(); 566 567 // Init colorpickers. 568 initColorpicker(); 569 570 // Color and Label module or plugin. 571 if ( wcclData !== '' ) { 572 // TODO: Fix wccl. 573 $.yith_wccl(wcclData); 574 } 575 576 moveAddToCartButton(); 577 578 var itemKey = link.data( 'cart-item-key' ); 579 $( wapoDOM.hiddenItemKey ).val( itemKey ); 580 581 addonsSelection( addonsSelected ); 582 583 if ( quantities ) { 584 addonsQuantity( quantities ); 585 } 586 587 if ( quantity > 0 ) { 588 setQuantity( quantity ); 589 } 590 591 if ( addonImageURL ) { 592 replaceAddonImage( addonImageURL ); 593 } 594 595 // Calculate add-on prices. 596 calculateTotalAddonsPrice(); 597 598 } else { 599 console.log( response ); 600 } 601 }, 602 complete : function ( e, response ) { 603 if ( 'success' === response ) { 604 // Open modal at the end of process. 605 pa_modal.addClass('open'); 606 $( 'body' ).addClass( 'yith-wapo-modal-is-open' ); 607 } 608 609 $( wapoDOM.popupWrapper ).unblock({ message: null }); 610 link.unblock({ message: null }); 611 } 612 } 613 ); 614 } 615 616 moveAddToCartButton = function() { 617 var button = $( wapoDOM.popupContent ).find( '.single_add_to_cart_button' ); 618 if ( button ) { 619 $( wapoDOM.popupFooter ).find( '.yith-wapo-add-to-cart' ).prepend( button ); 620 } 621 } 622 623 replaceAddonImage = function( addonImageURL ) { 624 625 var defaultPath = yith_wapo.replace_image_path; 626 var zoomMagnifier = '.yith_magnifier_zoom_magnifier, .zoomWindowContainer .zoomWindow';; 627 628 $( defaultPath ).attr( 'src', addonImageURL ); 629 $( defaultPath ).attr( 'srcset', addonImageURL ); 630 $( defaultPath ).attr( 'data-src', addonImageURL ); 631 $( defaultPath ).attr( 'data-large_image', addonImageURL ); 632 $( zoomMagnifier ).css( 'background-image', 'url(' + addonImageURL + ')' ); 633 634 $( wapoDOM.addonImageInput ).val( addonImageURL ); 635 636 // Reset gallery position. 637 if ( $( '.woocommerce-product-gallery .woocommerce-product-gallery__image' ).length > 0 ) { 638 $( wapoDOM.wcProductGallery ).trigger( 'woocommerce_gallery_reset_slide_position' ); 639 } 640 641 $( wapoDOM.wcProductGallery ).trigger( 'woocommerce_gallery_init_zoom' ); 642 643 $( document ).trigger( 'yith-wapo-after-replace-image' ); 644 645 } 646 647 openEditAddonsModal = function(e) { 648 e.preventDefault(); 649 e.stopPropagation(); 650 651 loadProduct( $(this) ); 652 653 } 654 655 closeModal = function() { 656 pa_modal.removeClass('open'); 657 $( 'body' ).removeClass( 'yith-wapo-modal-is-open' ); 658 659 setTimeout( // Wait for closing transition and then we clear the HTML. 660 function() {
661 $( wapoDOM.popupContent ).html(''); 662 $( wapoDOM.popupFooter ).find( '.yith-wapo-add-to-cart' ).html(''); 663 }, 664 400 665 ) 666 } 667 668 moveEditProductLinkOnCart = function() { 669 $( wapoDOM.editProductCartLink ).each( function() { 670 var link = $(this); 671 var productNameRow = link.closest( '.product-name' ); 672 673 productNameRow.append( link ); 674 675 } ); 676 } 677 678 setVariation = function(t_variation) { 679 var form = $( 'form.variations_form.cart' ); 680 681 for ( let attribute in t_variation.attributes ) { 682 683 let attribute_sel = $("select[name='" + attribute + "']"); 684 let value = t_variation.attributes[attribute]; 685 if ( value && attribute_sel.length > 0 ) { 686 attribute_sel.val( value ); 687 } 688 } 689 } 690 691 addonsSelection = function( addons ) { 692 var filesIndex = 0; 693 for ( var i in addons ) { 694 var addonObj = addons[i]; 695 696 for ( var addonIndex in addonObj ) { 697 var addonVal = addonObj[addonIndex]; 698 var addonOpts = addonIndex.split('-'); 699 700 let addonID = addonOpts[0] ?? ''; 701 let addonOption = addonOpts[1] ?? ''; 702 703 var addon = $( '#yith-wapo-addon-' + addonID ).get(0); 704 var addonType = $( addon ).data( 'addon-type' ); 705 706 if ( '' === addonVal ) { 707 continue; 708 } 709 710 var addonOpt = ''; 711 712 switch (addonType) { 713 714 case 'checkbox': 715 case 'label': 716 case 'color': 717 case 'product': 718 addonOpt = $( addon ).find( '#yith-wapo-' + addonID + '-' + addonOption ); 719 if ( ! addonOpt.is(':checked') ) { 720 $(addonOpt).prop('checked', true).trigger('change'); 721 } 722 break; 723 case 'radio': 724 addonOpt = $( addon ).find( '#yith-wapo-' + addonID + '-' + addonVal ); 725 addonOpt.trigger('click'); 726 break; 727 case 'text': 728 case 'textarea': 729 case 'number': 730 case 'date': 731 addonOpt = $( addon ).find( '#yith-wapo-' + addonID + '-' + addonOption ); 732 addonOpt.val( addonVal ); 733 break; 734 case 'colorpicker': 735 addonOpt = $( addon ).find( '#yith-wapo-' + addonID + '-' + addonOption ); 736 addonOpt.val( addonVal ); 737 addonOpt.trigger('change'); 738 break; 739 case 'select': 740 addonOpt = addonOpt = $( addon ).find( '#yith-wapo-' + addonID ); 741 addonOpt.val( addonVal ); 742 break; 743 case 'file': 744 var option = $( '#yith-wapo-option-' + addonID + '-' + addonOption ); 745 746 loadUploadedFile( 747 { 748 'addon' : $(addon), 749 'option' : option, 750 'addonVal' : addonVal, 751 'addonIndex' : addonIndex, 752 'index' : filesIndex, 753 'fileSize' : '' 754 } 755 ); 756 757 filesIndex++; 758 break; 759 } 760 } // end second for loop 761 } // end first for loop 762 763 maybeHideImageUploaded(); 764 765 } 766 767 loadUploadedFile = function(data) { 768 769 let addon = data.addon ?? ''; 770 let option = data.option ?? ''; 771 let addonVal = data.addonVal ?? ''; 772 let addonIndex = data.addonIndex ?? ''; 773 let index = data.index ?? ''; 774 let size = data.fileSize ?? ''; 775 776 addonVal = $.isArray( addonVal ) ? addonVal[0] : addonVal; 777 778 let name = addonVal.split('/').reverse()[0] ?? ''; 779 let isImage = addonVal.endsWith( 'jpeg' ) || addonVal.endsWith( 'jpg' ) || addonVal.endsWith( 'png' ); 780 781 var fileTemplate = wp.template( 'yith-wapo-uploaded-file-template' ); 782 var uploadedFileContainer = $( option ).find( '.yith-wapo-uploaded-file' ); 783 784 var fileData = { 785 'fileIndex': index, 786 'fileName' : name, 787 'fileSize' : size, 788 'optionId' : addonIndex, 789 'image' : isImage ? addonVal : '', 790 'addonVal' : addonVal, 791 }; 792 793 uploadedFileContainer.append( 794 fileTemplate( fileData ) 795 ); 796 797 uploadedFileContainer.closest( '.file-container' ).find( '.upload-parent' ).val(1); 798 799 uploadedFileContainer.show(); 800 maybeHideUploadButton( option ); 801 802 } 803 maybeHideImageUploaded = function() { 804 805 $( '.yith-wapo-addon-type-file .yith-wapo-img-uploaded' ).each( function() { 806 if ( ! $(this).attr('src') ) { 807 $(this).hide(); 808 } 809 } ); 810 811 } 812 maybeHideUploadButton = function( option ) { 813 814 let maxMultiple = option.data( 'max-multiple' ); 815 816 if ( option.find( '.yith-wapo-uploaded-file-element' ).length >= maxMultiple ) { 817 option.find( '.yith-wapo-ajax-uploader-container' ).hide(); 818 } 819 820 } 821 822 checkAddonsRequirements = function() { 823 let numbersCheck = checkNumbersTotalValues(), 824 minMaxResult = checkRequiredMinMax(); 825 826 if ( ! numbersCheck ) { // if it's not true, do not allow to add to cart. 827 return false;
828 } 829 830 if ( ! minMaxResult && ! yith_wapo.disable_scroll_on_required_mix_max ) { 831 $( 'html, body, .yith-wapo-content' ).animate( { scrollTop: $( wapoDOM.addonsContainer ).offset().top - 20 }, 500 ); 832 } 833 834 return minMaxResult; 835 }, 836 checkRequiredFields = function (action) { 837 838 var isRequired = false; 839 var hideButton = false; 840 var buttonClasses = yith_wapo.dom.single_add_to_cart_button; 841 $('form.cart .yith-wapo-addon:not(.hidden):visible input, form.cart .yith-wapo-addon:not(.hidden):visible select, form.cart .yith-wapo-addon:not(.hidden):visible textarea').each( 842 function () { 843 let element = $(this), 844 elementType = element.attr('type'); 845 let parent = element.closest('.yith-wapo-option'); 846 let addon = element.closest('.yith-wapo-addon'); 847 let toggle_addon = element.closest('div.yith-wapo-addon.wapo-toggle'); 848 let toggle_addon_title = toggle_addon.find('.wapo-addon-title.toggle-closed'); 849 let addonTitle = addon.find('.wapo-addon-title'); 850 851 if ('file' === element.attr('type') || element.hasClass('wapo-product-qty')) { 852 return; 853 } 854 855 if ( 856 element.attr('required') && 857 ('checkbox' === elementType || 'radio' === elementType ) 858 && !element.closest('.yith-wapo-option').hasClass('selected') 859 || element.attr('required') 860 && 'checkbox' !== elementType 861 && 'radio' !== elementType 862 && (element.val() == '' || element.val() == 'Required') 863 ) { 864 if (action === 'highlight') { 865 // Add required message. 866 showRequiredMessage(element); 867 addonTitle.addClass('wapo-error'); 868 869 870 // Open toggle. 871 if (toggle_addon_title) { 872 toggle_addon_title.click(); 873 } 874 } 875 876 hideButton = true; 877 isRequired = true; 878 } else { 879 880 if ( ( 'text' === elementType || 'textarea' === elementType ) && element.val() != '' ) { 881 isRequired = !checkTextInputLimit(element); // Check min/max length for inputs texts and textarea ONLY if the input IS NOT EMPTY. 882 } 883 884 // Restart default required status. 885 restartRequiredElement(element); 886 } 887 } 888 ); 889 890 return !isRequired; 891 }, 892 893 /** Print the required element with the message and colors */ 894 showRequiredMessage = function (element) { 895 let option = element.closest('.yith-wapo-option'); 896 if (option.find('.required-error').length < 1) { 897 option.append( 898 '<div class="required-error">' + 899 '<small class="required-message">' + yith_wapo.messages.requiredMessage + '</small>' + 900 '</div>' 901 ); 902 903 option.addClass('required-color'); 904 } 905 906 }, 907 908 /** Restart the required element (removing it) and remove Color classes */ 909 restartRequiredElement = function( element ) { 910 let option = element.closest( '.yith-wapo-option' ); 911 element.closest( '.yith-wapo-option' ).find( '.required-error' ).remove(); 912 option.removeClass( 'required-color' ); 913 }, 914 915 /** 916 * Conditional Logic 917 */ 918 conditionalLogicCheck = function( lastFinalConditions = {} ) { 919 var finalConditions = {}; 920 921 $( 'form.cart .yith-wapo-addon.conditional_logic' ).each( 922 function() { 923 924 var AddonConditionSection = false, 925 AddonVariationSection = false; 926 927 var logicDisplay = $(this).data('conditional_logic_display'); // show / hide 928 929 // Applied to conditions. 930 var logicDisplayIf = $(this).data('conditional_logic_display_if'); // all / any 931 932 var ruleAddon = String($(this).data('conditional_rule_addon')), 933 ruleAddonIs = String($(this).data('conditional_rule_addon_is')), 934 ruleVariation = String($(this).data('conditional_rule_variations')); 935 936 ruleAddon = (typeof ruleAddon !== 'undefined' && ruleAddon !== "0" && ruleAddon !== '') ? ruleAddon.split('|') : false; // addon number 937 ruleAddonIs = (typeof ruleAddonIs !== 'undefined' && ruleAddonIs !== '') ? ruleAddonIs.split('|') : false; // selected / not-selected / empty / not-empty 938 ruleVariation = (typeof ruleVariation !== 'undefined' && ruleVariation !== '') ? ruleVariation.split('|') : false; // variations number 939 940 if (!ruleVariation && (!ruleAddon || !ruleAddonIs)) { // Show addon if no variations conditions or addons conditions. 941 942 AddonConditionSection = true; 943 AddonVariationSection = true; 944 logicDisplay = 'show'; 945 946 } else { 947 948 // ConditionLogic. 949 if (ruleAddon && ruleAddonIs) { 950 951 switch (logicDisplayIf) { 952 953 case 'all': 954 AddonConditionSection = conditionalLogicAllRules(ruleAddon, ruleAddonIs); 955 break; 956 957 case 'any': 958 AddonConditionSection = conditionalLogicAnyRules(ruleAddon, ruleAddonIs); 959 break; 960 961 } 962 963 } else { 964 AddonConditionSection = true; 965 } 966 967 if (AddonConditionSection && ruleVariation) { // Prevent check variations if addons condition fails. 968 var variationProduct = $('.variation_id').val(); 969 if (-1 !== $.inArray(String(variationProduct), ruleVariation)) { 970 AddonVariationSection = true; 971 } 972 973 } else { 974 AddonVariationSection = true; 975 } 976 977 } 978 979 switch (logicDisplay) { 980 981 case 'show' : 982 983 if (AddonVariationSection && AddonConditionSection) { // Both conditions true --> apply logic Display 984 finalConditions[$(this).attr('id')] = 'not-hidden'; 985 } else { 986 finalConditions[$(this).attr('id')] = 'hidden'; 987 } 988 break; 989 990 case 'hide' : 991 if (AddonVariationSection && AddonConditionSection) { // Both conditions true --> apply logic Display 992 finalConditions[$(this).attr('id')] = 'hidden'; 993 } else { 994 finalConditions[$(this).attr('id')] = 'not-hidden'; 995 } 996 } 997 } 998 ); 999 1000 $.each( 1001 finalConditions, 1002 function( id, mode ) { 1003 let element = $( '#' + id ); 1004 1005 var isHidden = element.hasClass( 'hidden' ); 1006 1007 if ( 'not-hidden' === mode ) { 1008 1009 // Todo: We avoid out of stock to change disabled value. 1010 if( yith_wapo.conditionalDisplayEffect === 'slide' ){ 1011 element.slideDown().removeClass( 'hidden' ).find( '.yith-wapo-option:not(.out-of-stock) .yith-wapo-option-value' ).attr( 'disabled', false ); 1012 }else{ 1013 element.fadeIn().removeClass( 'hidden' ).find( '.yith-wapo-option:not(.out-of-stock) .yith-wapo-option-value' ).attr( 'disabled', false ); 1014 } 1015 1016 // Re-enable select add-ons if it was hidden 1017 if ( element.hasClass( 'yith-wapo-addon-type-select' ) ){ 1018 element.find( '.yith-wapo-option-value' ).attr( 'disabled', false ); 1019 } 1020 1021 // Check the min_max after disable value. 1022 checkMinMax( element ); 1023 1024 } else if ( yith_wapo.conditionalDisplayEffect === 'slide' ) { 1025 element.slideUp().addClass( 'hidden' ).find( '.yith-wapo-option-value' ).attr( 'disabled', true ); 1026 } else { 1027 element.hide().addClass( 'hidden' ).find( '.yith-wapo-option-value' ).attr( 'disabled', true ); 1028 } 1029 1030 } 1031 ); 1032 1033 if ( JSON.stringify( finalConditions ) !== JSON.stringify( lastFinalConditions ) ) { 1034 conditionalLogicCheck( finalConditions ); 1035 } 1036 1037 $(document).trigger('wapo_after_conditional_logic_check'); 1038 1039 } 1040 1041 /** 1042 * Conditional Rule AND 1043 * 1044 * @param ruleAddon 1045 * @param ruleAddonIs 1046 * @returns {boolean} 1047 */ 1048 conditionalLogicAllRules = function( ruleAddon, ruleAddonIs ) { 1049 var conditional = true; 1050 1051 for ( var x = 0; x < ruleAddon.length; x++ ) { 1052 1053 if ( ruleAddon[x] == 0 || ! ruleAddon[x] ) { 1054 continue; 1055 } 1056 1057 var ruleAddonSplit = ruleAddon[x].split( '-' ); 1058 var AddonSelected = false;
1059 var AddonNotEmpty = false; 1060 1061 // variation check 1062 if ( typeof ruleAddonSplit[1] != 'undefined' ) { 1063 1064 AddonSelected = ( // Selector or checkbox 1065 $( '#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1] ).is( ':checked' ) 1066 || $( 'select#yith-wapo-' + ruleAddonSplit[0] ).val() == ruleAddonSplit[1] 1067 ) && ! $( '#yith-wapo-addon-' + ruleAddonSplit[0] ).hasClass( 'hidden' ); 1068 1069 var typeText = $( 'input#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1] ).val(); // text 1070 var typeTextarea = $( 'textarea#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1] ).val(); // textarea 1071 AddonNotEmpty = ( 1072 ( typeof typeText != 'undefined' && typeText !== '' ) 1073 || ( typeof typeTextarea != 'undefined' && typeTextarea !== '' ) 1074 ) && ! $( '#yith-wapo-addon-' + ruleAddonSplit[0] ).hasClass( 'hidden' ); 1075 1076 // addon check 1077 } else { 1078 AddonSelected = ( 1079 $( '#yith-wapo-addon-' + ruleAddon[x] + ' input:checkbox:checked' ).length > 0 1080 || $( '#yith-wapo-addon-' + ruleAddon[x] + ' input:radio:checked' ).length > 0 1081 || $( '#yith-wapo-addon-' + ruleAddon[x] + ' option:selected' ).length > 0 1082 && 'default' != $( '#yith-wapo-addon-' + ruleAddon[x] + ' option:selected' ).val() // Check if not default value of Select Add-on 1083 ); 1084 AddonSelected = AddonSelected && ! $( '#yith-wapo-addon-' + ruleAddon[x] ).hasClass( 'hidden' ); 1085 1086 var typeText = 'undefined'; 1087 $( '#yith-wapo-addon-' + ruleAddonSplit[0] + ' input, #yith-wapo-addon-' + ruleAddonSplit[0] + ' textarea' ).each( 1088 function( index ){ 1089 if ( $( this ).val() !== '' ) { 1090 typeText = true; 1091 return; 1092 } 1093 } 1094 ); 1095 AddonNotEmpty = ( 1096 ( typeText != 'undefined' && typeText !== '') 1097 // || (typeof typeTextarea != 'undefined' && typeTextarea !== '') 1098 ) && ! $( '#yith-wapo-addon-' + ruleAddonSplit[0] ).hasClass( 'hidden' ); 1099 } 1100 1101 switch ( ruleAddonIs[x] ) { 1102 case 'selected' : 1103 if ( ! AddonSelected ) { 1104 conditional = false;
1105 } 1106 break; 1107 case 'not-selected': 1108 if ( AddonSelected ) { 1109 conditional = false; 1110 } 1111 break; 1112 1113 case 'empty' : 1114 if ( AddonNotEmpty ) { 1115 conditional = false; 1116 } 1117 break; 1118 1119 case 'not-empty' : 1120 if ( ! AddonNotEmpty ) { 1121 conditional = false; 1122 } 1123 1124 break; 1125 } 1126 1127 if ( ! conditional ) { 1128 break; 1129 } 1130 1131 } 1132 1133 return conditional; 1134 1135 } 1136 1137 /** 1138 * Conditional Rule OR 1139 * 1140 * @param ruleAddon 1141 * @param ruleAddonIs 1142 * @returns {boolean} 1143 */ 1144 conditionalLogicAnyRules = function (ruleAddon, ruleAddonIs) { 1145 1146 var conditional = false; 1147 1148 for (var x = 0; x < ruleAddon.length; x++) { 1149 1150 if (ruleAddon[x] == 0 || !ruleAddon[x]) { 1151 continue; 1152 } 1153 var ruleAddonSplit = ruleAddon[x].split('-'); 1154 1155 // variation check 1156 if (typeof ruleAddonSplit[1] != 'undefined') { 1157 1158 AddonSelected = ( // Selector or checkbox 1159 $('#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1]).is(':checked') 1160 || $('select#yith-wapo-' + ruleAddonSplit[0]).val() == ruleAddonSplit[1] 1161 ) && !$('#yith-wapo-addon-' + ruleAddonSplit[0]).hasClass('hidden'); 1162 1163 var typeText = $('input#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1]).val(); // text 1164 var typeTextarea = $('textarea#yith-wapo-' + ruleAddonSplit[0] + '-' + ruleAddonSplit[1]).val(); // textarea 1165 AddonNotEmpty = ( 1166 (typeof typeText != 'undefined' && typeText !== '') 1167 || (typeof typeTextarea != 'undefined' && typeTextarea !== '') 1168 ) && !$('#yith-wapo-addon-' + ruleAddonSplit[0]).hasClass('hidden'); 1169 1170 // addon check 1171 } else { 1172 AddonSelected = ( 1173 $('#yith-wapo-addon-' + ruleAddon[x] + ' input:checkbox:checked').length > 0 1174 || $('#yith-wapo-addon-' + ruleAddon[x] + ' input:radio:checked').length > 0 1175 || $('#yith-wapo-addon-' + ruleAddon[x] + ' option:selected').length > 0 1176 && 'default' != $('#yith-wapo-addon-' + ruleAddon[x] + ' option:selected').val() // Check if not default value of Select Add-on 1177 ); 1178 AddonSelected = AddonSelected && !$('#yith-wapo-addon-' + ruleAddon[x]).hasClass('hidden'); 1179 1180 var typeText = 'undefined'; 1181 $('#yith-wapo-addon-' + ruleAddonSplit[0] + ' input, #yith-wapo-addon-' + ruleAddonSplit[0] + ' textarea').each( 1182 function (index) { 1183 if ($(this).val() !== '') { 1184 typeText = true; 1185 return; 1186 } 1187 } 1188 ); 1189 AddonNotEmpty = ( 1190 (typeText != 'undefined' && typeText !== '') 1191 // || (typeof typeTextarea != 'undefined' && typeTextarea !== '') 1192 ) && !$('#yith-wapo-addon-' + ruleAddonSplit[0]).hasClass('hidden'); 1193 } 1194 1195 switch (ruleAddonIs[x]) { 1196 case 'selected' : 1197 if (AddonSelected) { 1198 conditional = true; 1199 } 1200 break; 1201 case 'not-selected': 1202 if (!AddonSelected) { 1203 conditional = true; 1204 } 1205 break; 1206 1207 case 'empty' : 1208 if (!AddonNotEmpty) { 1209 conditional = true; 1210 } 1211 break; 1212 1213 case 'not-empty' : 1214 if (AddonNotEmpty) { 1215 conditional = true; 1216 } 1217 1218 break; 1219 } 1220 if (conditional) { 1221 break; 1222 } 1223 } 1224 1225 return conditional; 1226 } 1227 1228 calculateAddonsPrice = function () { 1229 var firstFreeOptions = 0, 1230 currentAddonID = 0, 1231 totalPrice = 0, 1232 totalPriceDefault = 0, 1233 quantity = $(yith_wapo.productQuantitySelector).val(), // Quantity of the Add to cart form. 1234 totals = {}; 1235 1236 if (!quantity > 0) { 1237 quantity = 1; 1238 } 1239 1240 $('form.cart .yith-wapo-addon:not(.hidden):visible input, form.cart .yith-wapo-addon:not(.hidden):visible select, form.cart .yith-wapo-addon:not(.hidden):visible textarea').each( 1241 function () { 1242 1243 let option = $(this), 1244 defaultProductPrice = parseFloat($(wapoDOM.addonsContainer).attr('data-product-price')), 1245 optionID = option.data('addon-id'); 1246 1247 if (optionID) { 1248 let optionType = option.attr('type'), 1249 priceMethod = option.data('price-method'), 1250 price = 0, 1251 defaultTotalPrice = 0, 1252 priceType = '', 1253 addon = option.parents('.yith-wapo-addon'), 1254 addonType = addon.data('addon-type'), 1255 addonQty = 1; 1256 1257 if ('number' === optionType && 0 == option.val()) { 1258 return totalPrice; 1259 } 1260 1261 if (option.is('textarea')) { 1262 optionType = 'textarea'; 1263 } 1264 1265 if (option.is(':checked') || option.find(':selected').is('option') 1266 || (option.is('input:not([type=checkbox])') && option.is('input:not([type=radio])') && option.val() != '') 1267 || (option.is('textarea') && option.val() != '') 1268 ) { 1269 1270 if (option.is('select')) { 1271 option = option.find(':selected'); 1272 } 1273 1274 if ('number' === optionType) { 1275 1276 cleanError( option.closest( '.yith-wapo-option' ) ); 1277 1278 let dataMin = parseInt( option.attr( 'min' ) ); 1279 let dataMax = parseInt( option.attr( 'max' ) ); 1280 let numberVal = parseInt( option.val() ); 1281 1282 if ( numberVal > dataMax || numberVal < dataMin ) { 1283 let message = yith_wapo.messages.moreThanMax + ' ' + dataMax; 1284 1285 if ( numberVal < dataMin ) { 1286 message = yith_wapo.messages.lessThanMin + ' ' + dataMin; 1287 } 1288 1289 setError( option.closest( '.yith-wapo-option' ), message ); 1290 1291 return; 1292 } 1293 1294 checkMultipliedPrice(option); 1295 } 1296 1297 if ('text' === optionType || 'textarea' === optionType) { 1298 checkMultipliedLength(option); 1299 } 1300 1301 if (currentAddonID != optionID) { 1302 currentAddonID = option.data('addon-id'); 1303 firstFreeOptions = option.data('first-free-options'); 1304 } 1305 1306 if (option.data('first-free-enabled') == 'yes' && firstFreeOptions > 0) { 1307 firstFreeOptions--; 1308 } else { 1309 if (typeof option.data('price-type') != 'undefined' && '' !== option.data('price-type')) { 1310 priceType = option.data('price-type'); // Percentage or fixed. 1311 } 1312 1313 let dataPriceSale = option.attr('data-price-sale'), 1314 dataPrice = option.attr('data-price'), 1315 defaultSalePrice = option.attr('data-default-sale-price'), 1316 defaultPrice = option.attr('data-default-price'); 1317 1318 if (typeof dataPriceSale != 'undefined' && '' !== dataPriceSale && dataPriceSale >= 0 && 'multiplied' !== priceType) { 1319 price = parseFloat(dataPriceSale); 1320 defaultTotalPrice = parseFloat(defaultSalePrice); 1321 } else if (typeof dataPrice != 'undefined' && '' !== dataPrice) { 1322 price = parseFloat( dataPrice ); 1323 defaultTotalPrice = parseFloat( defaultPrice ); 1324 } 1325 1326 if ('percentage' === priceType && 'discount' !== priceMethod) { 1327 price = (price * defaultProductPrice) / 100; 1328 defaultTotalPrice = (defaultTotalPrice * defaultProductPrice) / 100; 1329 } 1330 1331 if ('product' === addonType) { 1332 if (!option.hasClass('.yith-wapo-option')) { 1333 option = option.parents('.yith-wapo-option'); 1334 addonQty = option.find('.wapo-product-qty'); 1335 if (addonQty) { 1336 addonQty = addonQty.val(); 1337 if (addonQty > 1) { 1338 price = price * addonQty; 1339 defaultTotalPrice = defaultTotalPrice * addonQty; 1340 } 1341 } 1342 } 1343 } 1344 1345 // Multiply price by quantity. Not multiplied for Sell individually add-ons ( it will be 1 on cart ). 1346 if (quantity > 1 && !addon.hasClass('sell_individually')) { 1347 price = price * quantity; 1348 defaultTotalPrice = defaultTotalPrice * quantity; 1349 } 1350 1351 price = parseFloat( price ); 1352 1353 totalPrice += price; 1354 totalPriceDefault += defaultTotalPrice; 1355 } 1356 } 1357 } 1358 } 1359 ); 1360 1361 totals = { 1362 'totalPrice' : parseFloat(totalPrice.toFixed(yith_wapo.number_decimals)), 1363 'totalPriceDefault': parseFloat(totalPriceDefault.toFixed(yith_wapo.number_decimals)) 1364 }; 1365 1366 return totals; 1367 }; 1368 1369 setTotalBoxPrices = function (defaultProductPrice, totalPrice, replacePrice = true, totalDefaultOptionsPrice = 0) { 1370 var quantity = $(yith_wapo.productQuantitySelector).val(); 1371 1372 if (!quantity > 0) { 1373 quantity = 1; 1374 } 1375 1376 var totalProductPrice = defaultProductPrice * quantity, 1377 totalOptionsPrice = parseFloat(totalPrice), 1378 totalOrderPrice = (parseFloat(totalProductPrice) + totalOptionsPrice).toFixed( wapoArgs.numDecimals ); 1379 1380 // Price without formatting. 1381 var total_ProductPrice = totalProductPrice, 1382 total_OptionsPrice = totalOptionsPrice; 1383 1384 if ( ! yith_wapo.includeShortcodePriceSuffix ) { 1385 1386 var totalOptionsPriceFormatted = floatToWcPrice(totalOptionsPrice); // Format price. 1387 var totalOrderPriceFormatted = floatToWcPrice(totalOrderPrice); // Format price. 1388 1389 $(wapoDOM.totalsBox.optionsPrice).html(totalOptionsPriceFormatted + ' ' + yith_wapo.priceSuffix ); 1390 $(wapoDOM.totalsBox.orderPrice).html(totalOrderPriceFormatted + ' ' + yith_wapo.priceSuffix ); 1391 1392 var response = { 1393 'order_price_suffix': totalOrderPriceFormatted, 1394 'order_price_raw' : parseInt( $(wapoDOM.addonsContainer).attr('data-default-product-price') ) + parseInt( totalOptionsPrice ), 1395 }; 1396 1397 replaceProductPrice(totalOrderPrice, totalOrderPriceFormatted); 1398 1399 $(document).trigger('wapo-after-calculate-product-price', response); 1400 1401 1402 }
1402 else { // Do the ajax calculation only if customer has {price_including_tax} / {price_excluding_tax} in WooCommerce > Tax > Price display suffix option. 1403 1404 let savedOrderPrice = $(wapoDOM.addonsContainer).attr('data-order-price'); 1405 1406 // If previous order price is equal to the current calculated order price, it won't process the Ajax Calls. 1407 if (yith_wapo.preventAjaxCallOnUnchangedTotals && parseFloat(savedOrderPrice) === parseFloat(totalOrderPrice)) { 1408 $(wapoDOM.totalPriceTable).css('opacity', '1'); 1409 return false; 1410 } 1411 1412 var suffixData = { 1413 'product_id': parseInt($(wapoDOM.addonsContainer).attr('data-product-id')), 1414 'options_price': totalOptionsPrice, 1415 'options_default_price': totalDefaultOptionsPrice, 1416 'total_order_price': totalOrderPrice, 1417 'currency': yith_wapo.woocommerce_currency 1418 }; 1419 1420 1421 // Update totals and table (AJAX call). 1422 calculateProductPrice(suffixData); 1423 } 1424 1425 $(wapoDOM.totalPriceTable).css('opacity', '1'); 1426 1427 $(document).trigger('yith_wapo_product_price_updated', [total_ProductPrice + total_OptionsPrice]); 1428 }, 1429 1430 replaceProductPrice = function (totalOrderPrice, totalOrderPriceHtml) { 1431 1432 if ('yes' !== yith_wapo.replace_price_in_product_without_addons && (!$(wapoDOM.addonsContainer).length || !$(wapoDOM.addonsContainer).find('.yith-wapo-block').length)) { 1433 return; 1434 } 1435 1436 if ( yith_wapo.hide_order_price_if_zero && 0 === totalOrderPrice ) { 1437 totalOrderPriceHtml = ''; 1438 } 1439 1440 if ('yes' === yith_wapo.replace_product_price && !isNaN(parseFloat(totalOrderPrice)) && $(yith_wapo.replace_product_price_class).length > 0) { 1441 $(yith_wapo.replace_product_price_class).html('<span class="woocommerce-Price-amount amount"><bdi>' + totalOrderPriceHtml + '</bdi></span>'); 1442 } 1443 }, 1444 1445 calculateProductPrice = function (suffixData) { 1446 1447 $(document).trigger('wapo-before-calculate-product-price'); 1448 1449 var data_price_suffix = { 1450 'action': 'update_totals_with_suffix', 1451 'data': suffixData, 1452 'security': yith_wapo.addons_nonce, 1453 }; 1454 1455 $.ajax( 1456 { 1457 url: yith_wapo.ajaxurl, 1458 type: 'post', 1459 data: data_price_suffix, 1460 success: function (response) { 1461 if (response) { 1462 let totalProductPrice = response['price_html'], 1463 totalOptionsPriceHTML = response['options_price_suffix'], 1464 totalOrderPriceHTML = response['order_price_suffix'], 1465 totalOrderPrice = suffixData.total_order_price; 1466 1467 $(wapoDOM.totalsBox.productPrice).html(totalProductPrice); 1468 $(wapoDOM.totalsBox.optionsPrice).html(totalOptionsPriceHTML); 1469 $(wapoDOM.totalsBox.orderPrice).html(totalOrderPriceHTML); 1470 1471 $(wapoDOM.addonsContainer).attr('data-order-price', totalOrderPrice); 1472 1473 replaceProductPrice(totalOrderPrice, totalOrderPriceHTML); 1474 1475 $(document).trigger('wapo-after-calculate-product-price', response); 1476 } 1477 } 1478 } 1479 ); 1480 }, 1481 1482 calculateTotalAddonsPrice = function (replacePrice = true) { 1483 1484 //Check logical conditions before calculate prices. 1485 conditionalLogicCheck(); 1486 1487 if ('yes' === yith_wapo.hide_button_required) { 1488 var buttonClasses = yith_wapo.dom.single_add_to_cart_button; 1489 1490 $(buttonClasses).hide(); 1491 1492 var requiredFields = checkRequiredFields('hide'); // First check required options. True means all required options are already selected. 1493 var requiredMinMax = checkRequiredMinMax('hide'); 1494 1495 if ( requiredFields && requiredMinMax ) { 1496 $(buttonClasses).fadeIn(); 1497 } 1498 } 1499 1500 $(wapoDOM.totalPriceTable).css('opacity', '0.5'); 1501 1502 var totalPrice = 0; 1503 var totalDefaultPrice = 0; 1504 var defaultProductPrice = parseFloat($(wapoDOM.addonsContainer).attr('data-product-price')); 1505 var totalPriceBoxOption = yith_wapo.total_price_box_option; 1506 1507 let selectedGifCardAmountButton = $('button.ywgc-amount-buttons.selected_button'); 1508 1509 if (selectedGifCardAmountButton.length > 0) { 1510 defaultProductPrice = selectedGifCardAmountButton.data('price'); 1511 } 1512 1513 var pricesCalculated = calculateAddonsPrice(); 1514 1515 totalPrice = pricesCalculated.totalPrice; 1516 totalDefaultPrice = pricesCalculated.totalPriceDefault; 1517 1518 // Plugin option "Total price box". 1519 if ('hide_options' === totalPriceBoxOption) { 1520 if (0 !== totalPrice) { 1521 $('#wapo-total-price-table .hide_options tr.wapo-total-options').fadeIn(); 1522 } else { 1523 $('#wapo-total-price-table .hide_options tr.wapo-total-options').hide(); 1524 } 1525 } 1526 1527 setTotalBoxPrices(defaultProductPrice, totalPrice, replacePrice, totalDefaultPrice); 1528 1529 }; 1530 1531 productQuantityChange = function () { 1532 let inputNumber = $(this), 1533 inputVal = inputNumber.val(), 1534 productId = inputNumber.closest('.yith-wapo-option').data('product-id'), 1535 addToCartLink = inputNumber.closest('.option-add-to-cart').find('.add_to_cart_button'), 1536 productQuantity = 1, 1537 hrefCreated = ''; 1538
1539 if (addToCartLink.length && productId) { 1540 if (inputVal > 1) { 1541 productQuantity = inputVal; 1542 } 1543 1544 hrefCreated = '?add-to-cart=' + productId + '&quantity=' + productQuantity; 1545 1546 addToCartLink.attr('href', hrefCreated); 1547 } 1548 1549 }; 1550 1551 floatToWcPrice = function ( price ){ 1552 1553 var default_args = { 1554 decimal_sep: yith_wapo.decimal_sep, 1555 currency_position: yith_wapo.currency_position, 1556 currency_symbol: yith_wapo.currency_symbol, 1557 trim_zeros: yith_wapo.total_thousand_sep, 1558 num_decimals: parseInt(yith_wapo.number_decimals), 1559 html: true 1560 }; 1561 1562 if (default_args.num_decimals > 0) { 1563 var wc_price_length = parseInt(price).toString().length; 1564 var wc_int_end_sep = parseInt(wc_price_length) + parseInt(default_args.num_decimals); 1565 price = price.toString().substr(0, wc_int_end_sep + 1); 1566 price = parseFloat(price).toFixed(yith_wapo.number_decimals); 1567 } else { 1568 price = parseInt(price); 1569 } 1570 1571 price = price.toString().replace('.', default_args.decimal_sep).replace( /(\d)(?=(\d{3})+(?!\d))/g, '$1' + default_args.trim_zeros ); 1572 1573 var formatted_price = price; 1574 var formatted_symbol = default_args.html ? '<span class="woocommerce-Price-currencySymbol">' + default_args.currency_symbol + '</span>' : default_args.currency_symbol; 1575 if ('left' === default_args.currency_position) { 1576 formatted_price = formatted_symbol + formatted_price; 1577 } else if ('right' === default_args.currency_position) { 1578 formatted_price = formatted_price + formatted_symbol; 1579 } else if ('left_space' === default_args.currency_position) { 1580 formatted_price = formatted_symbol + ' ' + formatted_price; 1581 } else if ('right_space' === default_args.currency_position) { 1582 formatted_price = formatted_price + ' ' + formatted_symbol; 1583 } 1584 formatted_price = default_args.html ? '<span class="woocommerce-Price-amount amount">' + formatted_price + '</span>' : formatted_price; 1585 1586 return formatted_price; 1587 1588 } 1589 1590 wcPriceToFloat = function (wc_price) { 1591 let price = wc_price.replace(/(?![\.\,])\D/g, '') 1592 .replace(yith_wapo.total_thousand_sep, '') 1593 .replace(yith_wapo.decimal_sep, '.'); 1594 1595 return parseFloat(price); 1596 }, 1597 getDefaultProductPrice = function () { 1598 if ( yith_wapo.enableGetDefaultVariationPrice ) { 1599 let product_id = $( '.variations_form.cart' ).data( 'product_id' ); 1600 let data = { 1601 'action' : 'get_default_variation_price', 1602 'product_id' : parseInt( product_id ), 1603 'security' : yith_wapo.addons_nonce, 1604 }; 1605 $.ajax( 1606 { 1607 url : yith_wapo.ajaxurl, 1608 type : 'post', 1609 data : data, 1610 success : function( response ) { 1611 if ( response ) { 1612 let defaultProductPrice = response['price_html']; 1613 let container = $( wapoDOM.addonsContainer ); 1614 container.attr( 'data-product-price', response['current_price'] ); 1615 container.attr( 'data-product-id', product_id ); 1616 1617 if ( 'yes' === yith_wapo.replace_product_price && container.find('.yith-wapo-block').length ) { 1618 $( yith_wapo.replace_product_price_class ).html( defaultProductPrice ); 1619 } 1620 1621 } 1622 }, 1623 complete: function (){ 1624 } 1625 } 1626 ); 1627 } 1628 }, 1629 1630 /** 1631 * Check the default options selected on load page to replace the image. 1632 */ 1633 checkDefaultOptionsOnLoad = function () { 1634 let optionsSelected = $('.yith-wapo-addon:not(.conditional_logic):not(.hidden) .yith-wapo-option.selected'); 1635 $(optionsSelected).each( 1636 function () { 1637 let option = $(this); 1638 replaceImageAction(option); 1639 } 1640 ); 1641 }, 1642 1643 resetAddons = function (event, params) { 1644 1645 if ('yith_wccl' === params) { 1646 return; 1647 } 1648 1649 if (!firstVariationLoading) { 1650 firstVariationLoading = true; 1651 return; 1652 } 1653 1654 getDefaultProductPrice(); 1655 1656 $(document).trigger('yith-wapo-reset-addons'); 1657 1658 }, 1659 1660 foundVariation = function (event, variation) { 1661 // Skip found_variation in Composite products or YITH Color and Label loop form 1662 if (event.target.classList.value === 'variations_form cart in_loop initialized' || $('form.cart.ywcp').length) return; 1663 1664 updateContainerProductPrice(variation); 1665 1666 $(document).trigger('yith-wapo-reload-addons'); 1667 }, 1668 1669 reloadAddons = function (event, productPrice = '') { 1670 var addons = $(wapoDOM.formCart).serializeArray(), 1671 data = { 1672 'action' : 'live_print_blocks', 1673 'addons' : addons, 1674 'currency' : yith_wapo.woocommerce_currency, 1675 'current_language': yith_wapo.currentLanguage, 1676 'security' : yith_wapo.addons_nonce, 1677 }; 1678 1679 if (productPrice != '') { 1680 data.price = productPrice; 1681 } 1682 1683 $.ajax( 1684 { 1685 url: yith_wapo.ajaxurl, 1686 type: 'post', 1687 data: data, 1688 beforeSend: function () { 1689 $(wapoDOM.addonsContainer).css('opacity', '0.5'); 1690 }, 1691 error: function (xhr, status, error) { 1692 // Code to handle errors. 1693 console.error('AJAX request failed: ', status, error); 1694 }, 1695 success: function (response) { 1696 let html = response['html'] ?? ''; 1697 var quantities = response['quantities'] ?? ''; 1698 1699 if (html !== '') { 1700 $(wapoDOM.addonsContainer).html(html); 1701 } 1702 1703 let addonsSelected = response['addons'] ?? ''; 1704 if (addonsSelected !== '') { 1705 addonsSelection(addonsSelected); 1706 } 1707 if (quantities) { 1708 addonsQuantity(quantities); 1709 } 1710 1711 $( 'form.cart #yith-wapo-container select' ).trigger('change'); 1712 1713 $('form.cart').trigger('yith-wapo-after-reload-addons'); 1714 1715 }, 1716 complete: function (event) { 1717 1718 $(wapoDOM.addonsContainer).attr('data-order-price', 0); 1719 calculateTotalAddonsPrice(); 1720 1721 $(wapoDOM.addonsContainer).css('opacity', '1'); 1722 1723 } 1724 } 1725 ); 1726 }, 1727 1728 addonsQuantity = function (quantities) { 1729 1730 $.each($(wapoDOM.addonsContainer).find('.qty.wapo-product-qty'), function () { 1731 let separator = 'yith_wapo_product_qty'; 1732 let qtyID = $(this).attr('id'); 1733 let identificator = qtyID.replace(separator, '').replace('[', '').replace(']', ''); 1734 1735 if (identificator in quantities) { 1736 let qty = quantities[identificator]; 1737 $(this).val(qty).trigger('change'); 1738 } 1739 }); 1740 1741 }, 1742 1743 setQuantity = function (quantity) {
1744 $( 'div.quantity input.input-text.qty:not(.wapo-product-qty) ' ).val( quantity ); 1745 } 1746 1747 removeUploadedFile = function (ev) { 1748 let element = ev.target, 1749 uploadedFileContainer = $(element).closest('.yith-wapo-uploaded-file-element'), 1750 maxMultiple = $(element).closest('.yith-wapo-option').data('max-multiple'), 1751 lengthUploads = $(element).closest('.yith-wapo-ajax-uploader').find('.yith-wapo-uploaded-file-element').length, 1752 uploaderContainer = $(element).closest('.yith-wapo-ajax-uploader').find('.yith-wapo-ajax-uploader-container'), 1753 parentInputHidden = $(element).closest('.yith-wapo-option').find('input[type="hidden"].upload-parent'), 1754 fileInput = $(element).closest('.yith-wapo-option').find('input[type="file"]'); 1755 1756 uploadedFileContainer.remove(); 1757 1758 fileInput.val(''); 1759 1760 if ('undefined' === typeof maxMultiple || lengthUploads - 1 < maxMultiple) { // If max is not defined or length is less than max, show the Upload button. 1761 uploaderContainer.fadeIn(); 1762 } 1763 if (lengthUploads - 1 <= 0) { 1764 parentInputHidden.val(''); 1765 calculateTotalAddonsPrice(); 1766 } 1767 1768 }, 1769 1770 maybeCalculateTotals = function(addonOpt) { 1771 1772 let isQuantityButton = false; 1773 if (addonOpt.is('input[type="number"]')) { 1774 isQuantityButton = addonOpt.attr('name').indexOf('yith_wapo_product_qty') == 0 && addonOpt.hasClass('qty') || // Is quantity field from add-on type Product. 1775 'quantity' === addonOpt.attr('name') && addonOpt.hasClass('qty') // Is WooCommerce quantity field. 1776 ? true : false; 1777 } 1778 1779 let selectOpt = ''; 1780 if (addonOpt.is('select')) { 1781 selectOpt = addonOpt.closest('.yith-wapo-addon.yith-wapo-addon-type-select'); 1782 } 1783 1784 // Has price or is the WC Qty Button or is a Select add-on (selector is unique). 1785 if (addonOptHasPrice(addonOpt) || isQuantityButton || (selectOpt && 'select' === selectOpt.data('addon-type'))) { 1786 calculateTotalAddonsPrice(); 1787 } 1788 }, 1789 1790 addonOptHasPrice = function(addonOpt = null) { 1791 1792 let price = getOptionPrice(addonOpt), 1793 sale_price = getOptionSalePrice(addonOpt); 1794 1795 if ((typeof price === 'undefined' || '' === price) && (typeof sale_price === 'undefined' || '' === sale_price)) { 1796 return false; 1797 } 1798 1799 return true; 1800 }, 1801 1802 /** Get price of an option */ 1803 getOptionPrice = function(addonOpt) { 1804 1805 let price = addonOpt.data('price'); 1806 1807 return price; 1808 1809 }, 1810 1811 /** Get sale price of an option */ 1812 getOptionSalePrice = function(addonOpt) { 1813 1814 let price = addonOpt.data('sale-price'); 1815 1816 return price; 1817 1818 }, 1819 1820 /** 1821 * Check min and max values for the sum of add-ons type Number. 1822 */ 1823 checkNumbersTotalValues = function () { 1824 1825 var numberAddons = $('#yith-wapo-container .yith-wapo-addon-type-number:not(.hidden).numbers-check'), 1826 isError = false; 1827 1828 numberAddons.each(function (index) { 1829 1830 let numberAddon = $(this), 1831 numberMin = numberAddon.data('numbers-min'), 1832 numberMax = numberAddon.data('numbers-max'), 1833 totalCount = 0, 1834 errorCheck = false, 1835 errorMessage = '', 1836 optionsElement = numberAddon.find('.options'); 1837 1838 // Reset 1839 if (optionsElement.hasClass('error-message')) { 1840 optionsElement.removeClass('error-message'); 1841 } 1842 numberAddon.find('.yith-wapo-numbers-error-message').remove(); 1843 1844 numberAddon.find('input[type="number"]').each(function () { 1845 let number = $(this).val(); 1846 if ('undefined' === number || '' === number) { 1847 return true; // continue 1848 } 1849 totalCount += parseFloat(number); 1850 }); 1851 1852 if ('undefined' !== typeof numberMin && totalCount < numberMin) { 1853 errorCheck = true; 1854 errorMessage = yith_wapo.messages.minErrorMessage + ' ' + numberMin; 1855 } 1856 ; 1857 1858 if ('undefined' !== typeof numberMax && totalCount > numberMax) { 1859 errorCheck = true; 1860 errorMessage = yith_wapo.messages.maxErrorMessage + ' ' + numberMax; 1861 } 1862 ; 1863 1864 if (errorCheck) { 1865 optionsElement.addClass('error-message'); 1866 numberAddon.append($('<small class="yith-wapo-numbers-error-message">' + errorMessage + '</small>')); 1867 isError = true; 1868 $('html, body').animate({scrollTop: numberAddon.offset().top - 50}, 500); 1869 } 1870 1871 }); 1872 1873 $(document).trigger('yith_wapo_check_number_total_values'); 1874 1875 if (isError) { 1876 return false;
1877 } 1878 1879 return true; 1880 1881 }; 1882 1883 replaceImageAction = function( optionWrapper, reset = false ) { 1884 1885 var defaultPath = yith_wapo.replace_image_path; 1886 var zoomMagnifier = '.yith_magnifier_zoom_magnifier, .zoomWindowContainer .zoomWindow'; 1887 var replaceImageURL = optionWrapper.data( 'replace-image' ); 1888 1889 if ( null === replaceImageURL || ! reset && $( defaultPath ).attr( 'src' ) === replaceImageURL ) { 1890 return; 1891 } 1892 1893 if ( typeof optionWrapper.data( 'replace-image' ) !== 'undefined' && optionWrapper.data( 'replace-image' ) != '' ) { 1894 1895 // save original image for the reset 1896 if ( typeof( $( defaultPath ).attr( 'wapo-original-img' ) ) == 'undefined' ) { 1897 $( defaultPath ).attr( 'wapo-original-img', $( defaultPath ).attr( 'src' ) ); 1898 if ( $( zoomMagnifier ).length ) { 1899 $( zoomMagnifier ).attr( 'wapo-original-img', $( zoomMagnifier ).css( 'background-image' ).slice( 4, -1 ).replace( /"/g, "" ) ); 1900 } 1901 } 1902 1903 $( wapoDOM.addonImageInput ).val( replaceImageURL ); 1904 1905 $( zoomMagnifier ).css( 'background-image', 'url(' + replaceImageURL + ')' ); 1906 1907 replaceAddonImage( replaceImageURL ); 1908 1909 } 1910 1911 if ( reset && typeof( $( defaultPath ).attr( 'wapo-original-img' ) ) != 'undefined' ) { 1912 1913 var originalImage = $( defaultPath ).attr( 'wapo-original-img' ); 1914 var originalZoom = $( zoomMagnifier ).attr( 'wapo-original-img' ); 1915 1916 var currentImage = $( wapoDOM.addonImageInput ).val(); 1917 1918 $( wapoDOM.addonImageInput ).val( '' ); 1919 1920 $( wapoDOM.addonsContainer ).find( '.yith-wapo-addon:not(.yith-wapo-addon-type-select):not(.conditional_logic.hidden) .yith-wapo-option.selected, .yith-wapo-addon-type-select:not(.conditional_logic.hidden) .yith-wapo-option-value' ).each( 1921 function( index, element ) { 1922 let option = $( element ); 1923 option = option.is( 'select' ) ? option.find( ':selected' ) : option; 1924 // Check if one option is still selected and has a image to replace, then do not change to default image. 1925 if ( option.data( 'replace-image' ) && ( option.hasClass( 'selected' ) || option.is( 'option' ) ) ) { 1926 originalImage = option.data( 'replace-image' ); 1927 $( wapoDOM.addonImageInput ).val( originalImage ); 1928 } 1929 } 1930 ); 1931 1932 if ( currentImage === originalImage ) { // If the replaced image is the same, we stop the execution. 1933 return; 1934 } 1935 1936 $( zoomMagnifier ).css( 'background-image', 'url(' + originalZoom + ')' ); 1937 1938 replaceAddonImage( originalImage ); 1939 1940 } 1941 } 1942 1943 1944 checkMaxSelected = function (element) { 1945 1946 var option = element.closest('.yith-wapo-option'), 1947 addon = element.closest('.yith-wapo-addon'), 1948 maxVal = addon.data('max'), 1949 optionsSelected = addon.find('.yith-wapo-option.selected').length; 1950 1951 if ('' === maxVal || 0 === maxVal) { 1952 return true; 1953 } 1954 1955 if (option.hasClass('selected')) { 1956 optionsSelected--; 1957 } else { 1958 optionsSelected++; 1959 } 1960 1961 if (optionsSelected > maxVal) { 1962 return false; 1963 } 1964 1965 return true; 1966 1967 }, 1968 1969 addonImageClicked = function () { 1970 var addon = $(this).closest( '.yith-wapo-addon' ); 1971 if ( 'label' === addon.data( 'addon-type' ) ) { 1972 return; 1973 } 1974 var option = $(this).closest( '.yith-wapo-option' ); 1975 1976 option.find( "input[name^='yith_wapo']" ).click(); 1977 }, 1978 1979 checkboxOnChange = function() { 1980 let checkboxInput = $( this ); 1981 let checkboxButton = checkboxInput.closest( '.checkboxbutton' ); 1982 let checkboxOption = checkboxInput.closest( '.yith-wapo-option' ); 1983 let checkboxOptions = checkboxOption.parent(); 1984 1985 let resetImage = false; 1986 1987 if ( checkboxOption.data( 'replace-image' ).length <= 0 ) { 1988 resetImage = true; 1989 } 1990 1991 let isChecked = checkboxInput.attr( 'checked' ); 1992 1993 if ( 'checked' !== isChecked ) { 1994 1995 // Single selection 1996 if ( checkboxOption.hasClass( 'selection-single' ) ) { 1997 // Disable all. 1998 checkboxOptions.find( 'input' ).attr( 'checked', false ).prop( 'checked', false );; 1999 checkboxOptions.find( '.selected, .checked' ).removeClass( 'selected checked' ); 2000 } 2001 2002 // Enable only the current option. 2003 checkboxInput.attr( 'checked', true ).prop( 'checked', true ); 2004 checkboxOption.addClass( 'selected' ); 2005 checkboxButton.addClass( 'checked' ); 2006 2007 } else { 2008 checkboxInput.attr( 'checked', false ).prop( 'checked', false ); 2009 checkboxOption.removeClass( 'selected' ); 2010 checkboxButton.removeClass( 'checked' ); 2011 2012 resetImage = true; 2013 } 2014 2015 replaceImageAction( checkboxOption, resetImage ); 2016 2017 } 2018 2019 inputOnChange = function() { 2020 2021 var input = $(this); 2022 var option = input.closest( '.yith-wapo-option' ); 2023 var disabled = false;
2024 2025 // Selection type single or multiple. 2026 2027 if ( '' !== input.val() && option.hasClass( 'selection-single' ) ) { 2028 disabled = true; 2029 option.siblings().find( 'input' ).prop( 'disabled', true ); 2030 } 2031 2032 option.siblings().find( 'input' ).prop( 'disabled', disabled ); 2033 2034 } 2035 2036 textareaOnChange = function() { 2037 2038 var input = $(this); 2039 var option = input.closest( '.yith-wapo-option' ); 2040 var disabled = false; 2041 2042 // Selection type single or multiple. 2043 2044 if ( '' !== input.val() && option.hasClass( 'selection-single' ) ) { 2045 disabled = true; 2046 option.siblings().find( 'textarea' ).prop( 'disabled', true ); 2047 } 2048 2049 option.siblings().find( 'textarea' ).prop( 'disabled', disabled ); 2050 2051 } 2052 2053 labelsOnChange = function() { 2054 let reset = false; 2055 /** 2056 * Check max value available. 2057 * @type {boolean} 2058 */ 2059 var maxSelected = checkMaxSelected($(this)); 2060 2061 if (!maxSelected) { 2062 $(this).prop('checked', false); 2063 return false; 2064 } 2065 2066 var optionWrapper = $(this).closest('.yith-wapo-option'); 2067 2068 if ( optionWrapper.data( 'replace-image' ).length <= 0 ) { 2069 reset = true; 2070 } 2071 2072 if ($(this).is(':checked')) { 2073 2074 2075 // Single selection 2076 if (optionWrapper.hasClass('selection-single')) { 2077 // Disable all 2078 optionWrapper.parent().find('input').prop('checked', false); 2079 optionWrapper.parent().find('.selected').removeClass('selected'); 2080 } 2081 optionWrapper.addClass('selected'); 2082 // Check checkbox of selected one. 2083 optionWrapper.find('input').prop('checked', true); 2084 2085 } else { 2086 reset = true; 2087 optionWrapper.removeClass('selected'); 2088 } 2089 2090 // Replace image 2091 replaceImageAction(optionWrapper, reset); 2092 2093 } 2094 2095 colorOnChange = function() { 2096 let reset = false; 2097 var optionWrapper = $(this).closest('.yith-wapo-option'); 2098 2099 if ( optionWrapper.data( 'replace-image' ).length <= 0 ) { 2100 reset = true; 2101 } 2102 2103 if ( $( this ).is( ':checked' ) ) { 2104 optionWrapper.addClass( 'selected' ); 2105 2106 // Single selection 2107 if ( optionWrapper.hasClass( 'selection-single' ) ) { 2108 // Disable all 2109 optionWrapper.parent().find( 'input' ).prop( 'checked', false ); 2110 optionWrapper.parent().find( '.selected' ).removeClass( 'selected' ); 2111 // Enable only the current option 2112 optionWrapper.find( 'input' ).prop( 'checked', true ); 2113 optionWrapper.addClass( 'selected' ); 2114 } 2115 2116 } else { 2117 reset = true; 2118 optionWrapper.removeClass( 'selected' ); 2119 } 2120 // Reset image. 2121 replaceImageAction( optionWrapper, reset ); 2122 2123 }, 2124 productOnChange = function() { 2125 2126 var optionWrapper = $(this).closest('.yith-wapo-option'); 2127 2128 if ( $( this ).is( ':checked' ) ) { 2129 2130 optionWrapper.addClass( 'selected' ); 2131 2132 // Single selection. 2133 if ( optionWrapper.hasClass( 'selection-single' ) ) { 2134 // Disable all. 2135 optionWrapper.parent().find( 'input' ).prop( 'checked', false ); 2136 optionWrapper.parent().find( '.selected' ).removeClass( 'selected' ); 2137 // Enable only the current option. 2138 optionWrapper.find( 'input' ).prop( 'checked', true ); 2139 optionWrapper.addClass( 'selected' ); 2140 } 2141 2142 } else { 2143 optionWrapper.removeClass( 'selected' ); 2144 } 2145 2146 }, 2147 2148 radioOnChange = function() { 2149 let reset = false; 2150 var optionWrapper = $(this).closest('.yith-wapo-option'); 2151 2152 if ( optionWrapper.data( 'replace-image' ).length <= 0 ) { 2153 reset = true; 2154 } 2155 2156 optionWrapper.addClass( 'selected' ); 2157 2158 // Remove selected siblings 2159 optionWrapper.siblings().removeClass( 'selected' ); 2160 2161 // Replace image 2162 replaceImageAction( optionWrapper, reset ); 2163 2164 }, 2165 2166 selectOnChange = function() { 2167 let optionWrapper = $( this ).closest( '.yith-wapo-addon' ); 2168 let selectedOption = $( this ).find( 'option:selected' ); 2169 let optionImageBlock = optionWrapper.find( 'div.image-container' ); 2170 let resetImage = false; 2171 2172 // Description & Image. 2173 var optionImage = selectedOption.data( 'image' ); 2174 var optionDescription = selectedOption.data( 'description' ); 2175 var option_desc = optionWrapper.find( '.option-description' ); 2176 2177 if ( typeof optionImage !== 'undefined' && optionImage ) { 2178 optionImage = '<img src="' + optionImage + '" style="max-width: 100%">'; 2179 optionImageBlock.html( optionImage ); 2180 } 2181 2182 if ( 'default' === selectedOption.val() || '' === optionImage ) { 2183 optionImageBlock.hide(); 2184 } else { 2185 optionImageBlock.fadeIn(); 2186 } 2187 2188 if ( 'undefined' === typeof optionDescription ) { 2189 option_desc.empty(); 2190 } else { 2191 option_desc.html( optionDescription ); 2192 } 2193 2194 // Replace image 2195 if ( ! selectedOption.data( 'replace-image' ) ){ 2196 resetImage = true; 2197 } 2198 2199 replaceImageAction( selectedOption, resetImage ); 2200 2201 }, 2202 2203 fileOnChange = function (e) { 2204 $(this).closest('.yith-wapo-ajax-uploader').css('opacity', '1'); 2205 2206 var input = $(this), 2207 uploaderElement = input.closest('.yith-wapo-option').find('.yith-wapo-ajax-uploader'), 2208 fileList = input[0].files, 2209 correctFiles = checkBeforeUploadFiles(uploaderElement, fileList); //Boolean 2210 2211 if (correctFiles) { 2212 uploadFiles(fileList, uploaderElement); 2213 } 2214 } 2215 2216 toggleElement = function (e) { 2217 e.preventDefault(); 2218 let addon_title = $(this).find('.wapo-addon-title'); 2219 let addon_el = addon_title.closest('.yith-wapo-addon'); 2220
2221 if (addon_el.hasClass('toggle-open')) { 2222 addon_el.removeClass('toggle-open').addClass('toggle-closed'); 2223 } else { 2224 addon_el.removeClass('toggle-closed').addClass('toggle-open'); 2225 } 2226 if (addon_title.hasClass('toggle-open')) { 2227 addon_title.removeClass('toggle-open').addClass('toggle-closed'); 2228 } else { 2229 addon_title.removeClass('toggle-closed').addClass('toggle-open'); 2230 } 2231 2232 addon_el.find('.options-container').toggle('fast'); 2233 2234 $(document).trigger('yith_proteo_inizialize_html_elements'); 2235 }, 2236 2237 updateContainerProductPrice = function (variation) { 2238 2239 // Do not allow updating the price if product bundle form exists. 2240 if ($('.cart.yith-wcpb-bundle-form').length || variation.variation_id !== parseInt($('.variation_id').val())) { 2241 return; 2242 } 2243 2244 let container = $(wapoDOM.addonsContainer), 2245 new_product_price = 0; 2246 if (typeof (variation.display_price) !== 'undefined') { 2247 new_product_price = variation.display_price; 2248 // Check if variation price and price_html are different, use the last one 2249 if ('yes' === yith_wapo.use_price_html_on_variations && typeof (variation.price_html) !== 'undefined') { 2250 let new_product_price_html = $(variation.price_html).find('> .amount bdi').text(); 2251 new_product_price_html = wcPriceToFloat(new_product_price_html); 2252 if (!isNaN(new_product_price_html) && new_product_price !== new_product_price_html) { 2253 new_product_price = new_product_price_html; 2254 } 2255 } 2256 } 2257 container.attr('data-product-price', new_product_price); 2258 container.attr( 'data-default-product-price', variation.default_variation_price); 2259 container.attr('data-product-id', variation.variation_id); 2260 2261 }, 2262 2263 yithWcMeasurementPC_Compatibility = function() { 2264 var price = $( '#price_calculator.wc-measurement-price-calculator-price-table .product_price .amount' ).text(); 2265 price = wcPriceToFloat( price ); 2266 2267 if ( ! isNaN( price ) ) { 2268 let container = $( wapoDOM.addonsContainer ); 2269 2270 container.attr( 'data-product-price', price ); 2271 $( document ).trigger( 'yith-wapo-reload-addons', [ price ] ); 2272 } 2273 }, 2274 ajaxUploaderOnDrop = function (e) { 2275 e.stopPropagation(); 2276 e.preventDefault(); 2277 2278 $(this).css('opacity', '1'); 2279 2280 var input = $(this).closest('.yith-wapo-option').find('input.file'), 2281 uploaderElement = $(this), 2282 fileList = e.originalEvent.dataTransfer.files, 2283 correctFiles = checkBeforeUploadFiles(uploaderElement, fileList); //Boolean 2284 2285 if (correctFiles) { 2286 uploadFiles(fileList, uploaderElement); 2287 } 2288 }, 2289 checkBeforeUploadFiles = function (uploaderElement, fileList) { 2290 let countAlreadyUploaded = $(uploaderElement).find('.yith-wapo-uploaded-file-element').length, 2291 totalUploads = countAlreadyUploaded + fileList.length, 2292 maxUploadsAllowed = $(uploaderElement).closest('.yith-wapo-option').data('max-multiple'), 2293 allowMultiple = $(uploaderElement).closest('.yith-wapo-option').hasClass('allow-multiple'); 2294 2295 if (!allowMultiple && totalUploads > 1) { 2296 alert(yith_wapo.messages.maxFilesAllowed + '1'); 2297 return false; 2298 } 2299 2300 if ('undefined' !== typeof maxUploadsAllowed && totalUploads > maxUploadsAllowed) { 2301 alert(yith_wapo.messages.maxFilesAllowed + maxUploadsAllowed); 2302 return false; 2303 } 2304 2305 for (var item in fileList) { 2306 if ($.isNumeric(item)) { 2307 2308 let file = fileList[item], 2309 message = '', 2310 isError = false; 2311 2312 if (!yith_wapo.upload_allowed_file_types.includes(file.name.split('.').pop().toLowerCase())) { 2313 //message = 'Error - not supported extension!'; 2314 message = yith_wapo.messages.noSupportedExtension; 2315 isError = true; 2316 } 2317 2318 if (parseFloat(file.size) >= parseFloat(yith_wapo.upload_max_file_size * 1024 * 1024)) { 2319 //message = 'Error - file size for ' + file.name + ' - max ' + yith_wapo.upload_max_file_size + ' MB allowed!'; 2320 message = wapoSprintf(yith_wapo.messages.maxFileSize, file.name, yith_wapo.upload_max_file_size); 2321 isError = true; 2322 } 2323 2324 if (isError) { 2325 alert(message); 2326 return false;
2327 } 2328 2329 } 2330 } 2331 2332 return true; 2333 }, 2334 2335 wapoSprintf = function (format, ...values) { 2336 return format.replace(/%([sd])/g, function (match, tipo) { 2337 if (tipo === 's') { 2338 return values.shift(); 2339 } else if (tipo === 'd') { 2340 const valor = values.shift(); 2341 return Number.isInteger(valor) ? valor.toString() : ''; 2342 } 2343 return match; 2344 }); 2345 }, 2346 2347 uploadFiles = function (fileList, uploaderElement) { 2348 2349 for (var count = 0; count < fileList.length; count++) { 2350 let uploadedFileContainer = uploaderElement.find('.yith-wapo-uploaded-file'), 2351 currentIndex = uploaderElement.find('.yith-wapo-uploaded-file-element').last().data('index') + 1; 2352 if (isNaN(currentIndex) || 'undefined' == typeof currentIndex) { 2353 currentIndex = 0; 2354 } 2355 appendNewUploadedFile(count, fileList, uploadedFileContainer, currentIndex); 2356 2357 if (count == fileList.length - 1) { 2358 uploadedFileContainer.show(); 2359 2360 uploadSingleFile(fileList, 0, uploaderElement); 2361 } 2362 } 2363 2364 }, 2365 appendNewUploadedFile = function ( count, fileList, uploadedFileContainer, currentIndex ) { 2366 let exactSize = calculateExactFileSize( fileList[count] ), 2367 fileName = fileList[count].name, 2368 optionId = $( uploadedFileContainer ).closest( '.yith-wapo-option' ).data( 'option-id' ); 2369 2370 var newElement = 2371 '<div class="yith-wapo-uploaded-file-element uploaded-file-'+currentIndex+'" data-index="' + currentIndex + '">' + 2372 '<div class="yith-wapo-uploaded-file-info">' + 2373 '<span class="info">' + 2374 '<label class="file-name"><span>' + fileName + '</span></label>' + 2375 '<span class="file-size">' + exactSize + '</span>' + 2376 '</span>' + 2377 '<i class="remove yith-plugin-fw__action-button__icon yith-icon yith-icon-trash" style="display:none"></i>' + 2378 '</div>' + 2379 '<div class="yith-wapo-loader-container" id="progressbar'+currentIndex+'">' + 2380 '<div class="yith-wapo-loader-label"></div>' + 2381 '<div class="yith-wapo-loader" role="progressbar"></div>' + 2382 '</div>' + 2383 '<input type="hidden" id="yith-wapo-' + optionId + '" class="option yith-wapo-option-value" name="yith_wapo[][' + optionId + '][]" >' + 2384 '</div>'; 2385 2386 uploadedFileContainer.append( newElement ); 2387 }, 2388 uploadSingleFile = function (fileList, fileCount, uploaderElement, isFinalUpload = false, currentIndex = 0) { 2389 2390 if (0 === parseInt(currentIndex) && $(uploaderElement).find('.yith-wapo-uploaded-file-element.completed').length) { 2391 currentIndex = $(uploaderElement).find('.yith-wapo-uploaded-file-element.completed').last().data('index') + 1; 2392 } 2393 2394 var fileLength = fileList.length - 1, 2395 currentFile = fileList[fileCount], 2396 data = new FormData(), 2397 option = $(uploaderElement).closest('.yith-wapo-option'), 2398 maxMultiple = option.data('max-multiple'), 2399 uploadedFileContainer = $(uploaderElement).find('.yith-wapo-uploaded-file'), 2400 uploaderContainer = $(uploaderElement).find('.yith-wapo-ajax-uploader-container'), 2401 uploadedFileElement = $(uploaderElement).find('.yith-wapo-uploaded-file-element[data-index="' + currentIndex + '"]'), 2402 removeIcon = $(uploaderElement).find('.yith-wapo-uploaded-file-info .remove'), 2403 progressLabel = $(uploadedFileElement).find('.yith-wapo-loader-container .yith-wapo-loader-label'), 2404 progressbar = $(uploadedFileElement).find('.yith-wapo-loader-container .yith-wapo-loader'); 2405 2406 data.append('action', 'yith_wapo_upload_file'); 2407 data.append( 'security', yith_wapo.addons_nonce); 2408 data.append('currentFile', currentFile); 2409 2410 $.ajax( 2411 { 2412 url: yith_wapo.ajaxurl, 2413 type: 'POST', 2414 contentType: false, 2415 processData: false, 2416 async: true, 2417 dataType: 'json', 2418 data: data, 2419 xhr: function () { 2420 var xhr = $.ajaxSettings.xhr(); 2421 if (xhr.upload) { 2422 xhr.upload.addEventListener('progress', function (event) { 2423 var currentPercent = 0; 2424 if (event.lengthComputable) { 2425 currentPercent = Math.ceil(event.loaded / event.total * 100); 2426 } 2427 progressbar.progressbar({ 2428 value: currentPercent, 2429 }); 2430 2431 }, false); 2432 xhr.addEventListener("progress", function (e) { 2433 if (fileList.length == 1 || isFinalUpload) { 2434 if (option.hasClass('allow-multiple')) {
2435 let countUploadedFiles = $(uploaderElement).find('.yith-wapo-uploaded-file-element').length; 2436 if ('undefined' === typeof maxMultiple || 'undefined' !== typeof maxMultiple && countUploadedFiles < maxMultiple) { 2437 uploaderContainer.fadeIn(); 2438 } 2439 } 2440 2441 removeIcon.fadeIn(); 2442 let parentInputHidden = $(uploaderElement).closest('.yith-wapo-option').find('input[type="hidden"].upload-parent'); 2443 parentInputHidden.val(1); 2444 2445 //Calculate prices after all the uploads. 2446 calculateTotalAddonsPrice(); 2447 2448 } 2449 }, false); 2450 return xhr; 2451 } 2452 return xhr; 2453 }, 2454 beforeSend: function () { 2455 uploaderContainer.hide(); 2456 uploadedFileContainer.show(); 2457 2458 progressbar.progressbar({ 2459 change: function () { 2460 progressLabel.text(progressbar.progressbar('value') + "%" + ' ' + yith_wapo.i18n.uploadPercentageDoneString); 2461 }, 2462 complete: function (e) { 2463 $(e.target).closest('.yith-wapo-loader-container').fadeOut(); 2464 } 2465 }); 2466 progressbar.show(); 2467 $('.cart').block({message: null, overlayCSS: {opacity: 0}}); 2468 2469 }, 2470 success: function (res, status) { 2471 if (status == 'success') { 2472 2473 2474 let hiddenElement = uploadedFileElement.find('input[type="hidden"]'), 2475 infoElement = uploadedFileElement.find('.yith-wapo-uploaded-file-info span.info'), 2476 resType = res.type; 2477 2478 2479 hiddenElement.val(res.url); // Add the value to the hidden input for $_POST['yith_wapo'] 2480 2481 if ('image/jpeg' === resType || 'image/jpg' === resType || 'image/png' === resType || 'image/gif' === resType || 'image/svg+xml' === resType) { 2482 infoElement.append('<img src="' + res.url + '" class="yith-wapo-img-uploaded" alt="Image uploaded from YITH Product Add-ons uploader">').fadeIn(); // Show the image preview. 2483 } 2484 2485 if (fileCount < fileLength) { 2486 if (fileCount == fileLength - 1) { 2487 isFinalUpload = true; 2488 } 2489 uploadSingleFile(fileList, fileCount + 1, uploaderElement, isFinalUpload, currentIndex + 1); 2490 } 2491 } 2492 }, 2493 complete: function () { 2494 $(uploadedFileElement).addClass('completed'); 2495 console.log('Single file upload completed!'); 2496 $('.cart').unblock({message: null}); 2497 }, 2498 error: function (xhr, textStatus, errorThrown) { 2499 let userMessage = 'Error uploading the file.'; 2500 2501 if (xhr.status === 400 && xhr.responseText.includes('Content-Length')) { 2502 userMessage = 'Error with the size of the file. '; 2503 } 2504 2505 $( '.yith-wapo-uploaded-file-element.uploaded-file-' + currentIndex ).find( 'i.remove' ).trigger( 'click' ); 2506 2507 console.error('AJAX error status:', textStatus); 2508 console.error('Error thrown:', errorThrown); 2509 console.error('Response text:', xhr.responseText); 2510 2511 alert(userMessage); 2512 }, 2513 2514 2515 }); 2516 2517 }, 2518 calculateExactFileSize = function( file ) { 2519 let exactSize = 0; 2520 let file_size = file.size; 2521 let file_types = ['Bytes', 'KB', 'MB', 'GB'], 2522 i = 0; 2523 while ( file_size > 900 ) { 2524 file_size /= 1024; 2525 i++; 2526 } 2527 exactSize = ( Math.round( file_size * 100 ) / 100 ) + ' ' + file_types[i]; 2528 2529 return exactSize; 2530 }, 2531 checkRequiredMinMax = function (action = '') { 2532 2533 let canProceed = true; 2534 2535 // Check force user selection for add-on type Select. 2536 if (!checkRequiredSelect(action)) { 2537 canProceed = false; 2538 } 2539 2540 // Required feature. 2541 if (action !== 'hide') { 2542 2543 let required = checkRequiredFields('highlight'); 2544 if (!required) { 2545 canProceed = false; 2546 } 2547 } 2548 var requiredOptions = 0; 2549 var checkMinMaxVal = ''; 2550 var apply_submit = action !== 'hide'; 2551 $('form.cart .yith-wapo-addon:not(.hidden)').each( 2552 function () { 2553 checkMinMaxVal = checkMinMax($(this), apply_submit); 2554 if (checkMinMaxVal > 0) { 2555 requiredOptions += checkMinMaxVal; 2556 } 2557 } 2558 ); 2559 if (action !== 'hide') { 2560 if (requiredOptions > 0) { 2561 canProceed = false;
2562 } 2563 } 2564 2565 return canProceed; 2566 }, 2567 /** 2568 * Check min/max for each option 2569 * @param addon 2570 * @param submit 2571 * @returns {number} 2572 */ 2573 checkMinMax = function(addon, submit = false) { 2574 2575 var addonType = addon.data('addon-type'); 2576 var minValue = addon.data('min'); 2577 var maxValue = addon.data('max'); 2578 var exaValue = addon.data('exa'); 2579 var errorMessage = addon.find('.min-error-message'), 2580 addonTitle = addon.find('.wapo-addon-title'), 2581 numberOfChecked = 0; 2582 2583 let toggle_addon_title = addon.find('.wapo-addon-title.toggle-closed'); 2584 addonTitle.removeClass('wapo-error'); 2585 2586 if ('select' === addonType || ('' === minValue && '' === exaValue && '' === maxValue)) { 2587 return; 2588 } 2589 2590 // Number / Text / TextArea 2591 if ('number' === addonType || 'text' === addonType || 'textarea' === addonType) { 2592 $(addon).find('.yith-wapo-option-value').each( 2593 function (index) { 2594 let numberValue = $(this).val(); 2595 if (numberValue.length) { 2596 numberOfChecked++; // Summing number of filled. 2597 } 2598 } 2599 ); 2600 2601 if (maxValue && numberOfChecked > maxValue) { 2602 let optionsElement = $(addon).find('.options-container'); 2603 if (!optionsElement.find('.max-selected-error').length) { 2604 optionsElement.append('<p class="max-selected-error">' + yith_wapo.i18n.maxOptionsSelectedMessage + '</p>'); 2605 addonTitle.addClass('wapo-error'); 2606 } 2607 return 1; 2608 } 2609 2610 } else if ( 'file' === addonType ) { 2611 numberOfChecked = $(addon).find( '.yith-wapo-uploaded-file' ).has('.yith-wapo-uploaded-file-element').length; 2612 } else { 2613 // Checkbox / Radio 2614 numberOfChecked = addon.find('input:checkbox:checked, input:radio:checked').length; // Sum of number of checked. 2615 } 2616 2617 // Exactly Values 2618 if (exaValue > 0) { 2619 2620 let optionsToSelect = 0; 2621 2622 if (exaValue == numberOfChecked) { 2623 addon.removeClass('required-min').find('.min-error').hide(); 2624 addon.find('input:checkbox').not(':checked'); 2625 } else { 2626 // If click on add to cart button. 2627 if (submit) { 2628 optionsToSelect = exaValue - numberOfChecked; 2629 addon.addClass('required-min'); 2630 addon.find('.min-error').show(); 2631 addonTitle.addClass('wapo-error'); 2632 2633 let errorMessageText = yith_wapo.i18n.selectOptions.replace('%d', exaValue) 2634 if (1 === exaValue) { 2635 errorMessageText = yith_wapo.i18n.selectAnOption; 2636 } 2637 2638 errorMessage.text(errorMessageText); 2639 2640 if (toggle_addon_title) { 2641 toggle_addon_title.click(); 2642 } 2643 } 2644 addon.find('.yith-wapo-option:not(.out-of-stock) input:checkbox').not(':checked').attr('disabled', false); 2645 } 2646 2647 return Math.abs( optionsToSelect ); 2648 2649 } else { 2650 2651 // At least values. 2652 if (minValue > 0) { 2653 let optionsToSelect = minValue - numberOfChecked; 2654 if (minValue <= numberOfChecked) { 2655 addon.removeClass('required-min').find('.min-error').hide(); 2656 } else { 2657 // If click on add to cart button. 2658 if (submit) { 2659 let minMessage = yith_wapo.i18n.selectAnOption; 2660 if (minValue > 1) { 2661 minMessage = yith_wapo.i18n.selectAtLeast.replace('%d', minValue) 2662 } 2663 2664 addon.addClass('required-min'); 2665 addon.find('.min-error').show(); 2666 addonTitle.addClass('wapo-error'); 2667 errorMessage.text(minMessage); 2668 2669 if (toggle_addon_title) { 2670 toggle_addon_title.click(); 2671 } 2672 } 2673 return optionsToSelect; 2674 } 2675 } 2676 2677 // Max values. 2678 if (!maxValue || maxValue >= numberOfChecked) { 2679 addon.removeClass('required-min').find('.max-selected-error').hide(); 2680 } else { 2681 // If click on add to cart button. 2682 if (submit) { 2683 addon.addClass('required-min'); 2684 let optionsElement = $(addon).find('.options-container'); 2685 if (!optionsElement.find('.max-selected-error').length) { 2686 optionsElement.append('<small class="max-selected-error">' + yith_wapo.i18n.maxOptionsSelectedMessage + '</small>'); 2687 addonTitle.addClass('wapo-error'); 2688 } 2689 } 2690 return 1; 2691 } 2692 2693 } 2694 }, 2695 /** Check force user select an option for add-on type Selector */ 2696 checkRequiredSelect = function (action = '') { 2697 2698 let value = true; 2699 2700 $('.yith-wapo-addon.yith-wapo-addon-type-select select').each( 2701 function () {
2702 let currentSelect = $(this); 2703 if (currentSelect.is(':required')) { 2704 let addon = currentSelect.closest('.yith-wapo-addon'); 2705 let errorMessage = addon.find('.min-error-message'), 2706 addonTitle = addon.find('.wapo-addon-title'); 2707 let selectValue = currentSelect.val(); 2708 errorMessage.text(''); 2709 addonTitle.removeClass('wapo-error'); 2710 addon.removeClass('required-min'); 2711 2712 if ('default' === selectValue && !addon.hasClass('hidden')) { 2713 value = false; 2714 if (!value && 'hide' !== action) { 2715 let error_el = addon.find('.min-error'); 2716 let toggle_addon = currentSelect.closest('div.yith-wapo-addon.wapo-toggle'); 2717 let toggle_addon_title = toggle_addon.find('.wapo-addon-title.toggle-closed'); 2718 addon.addClass('required-min'); 2719 2720 if (toggle_addon_title) { 2721 toggle_addon_title.click(); 2722 } 2723 addonTitle.addClass('wapo-error'); 2724 errorMessage.text(yith_wapo.i18n.selectAnOption.replace('%d', 1)); 2725 error_el.show(); 2726 } 2727 } 2728 } 2729 } 2730 ); 2731 2732 return value; 2733 }, 2734 checkTextInputLimit = function (input) { 2735 let valid = true, 2736 currentInput = $(input), 2737 currentValue = currentInput.val(), 2738 minLength = currentInput.attr('minlength'), 2739 maxLength = currentInput.attr('maxlength'), 2740 errorMessage = currentInput.closest( '.yith-wapo-option' ).find('.length-error-message'); 2741 if ((minLength !== '' && currentValue.length < minLength) || (maxLength !== '' && currentValue.length > maxLength)) { 2742 currentInput.addClass('length-error'); 2743 errorMessage.show(); 2744 valid = false; 2745 } else { 2746 errorMessage.hide(); 2747 currentInput.removeClass('length-error'); 2748 } 2749 return valid; 2750 }, 2751 numberOnChange = function() { 2752 var inputElementValue = $(this).val(), 2753 optionWrapper = $(this).closest('.yith-wapo-option'), 2754 resetImage = false; 2755 2756 if ('' == inputElementValue) { 2757 resetImage = true; 2758 } 2759 2760 replaceImageAction(optionWrapper, resetImage); 2761 checkMultipliedPrice($(this)); 2762 }, 2763 checkMultipliedPrice = function (addon) { 2764 let price = addon.attr('data-price'); 2765 let sale_price = addon.attr('data-price-sale'); 2766 let defaultPrice = addon.attr('data-default-price'); 2767 let priceType = addon.data('price-type'); 2768 let priceMethod = addon.data('price-method'); 2769 let default_attr = 'price'; 2770 let final_price = 0; 2771 let addon_value = addon.val(); 2772 2773 if (!defaultPrice > 0) { 2774 if (sale_price > 0 && ('number' !== addon.attr('type') && 'multiplied' === priceType)) { 2775 price = sale_price; 2776 default_attr = 'price-sale'; 2777 } 2778 defaultPrice = price; 2779 addon.data('default-price', defaultPrice); 2780 } 2781 if (priceMethod == 'value_x_product') { 2782 var productPrice = parseFloat($(wapoDOM.addonsContainer).attr('data-product-price')); 2783 final_price = addon_value * productPrice; 2784 } else if (priceType == 'multiplied') { 2785 final_price = addon_value * defaultPrice; 2786 } 2787 2788 if (final_price > 0 || priceMethod == 'decrease' || (final_price < 0 && priceType === 'multiplied')) { 2789 addon.attr( 'data-' + default_attr, final_price); 2790 } 2791 2792 }, 2793 // Multiply add-on price by length 2794 checkMultipliedLength = function (addon) { 2795 2796 let price = addon.data('price'); 2797 let defaultPrice = addon.data('default-price'); 2798 let priceType = addon.data('price-type'); 2799 2800 if (!defaultPrice > 0) { 2801 defaultPrice = price; 2802 addon.data('default-price', defaultPrice); 2803 } 2804 if ('characters' === priceType) { 2805 let remove_spaces = addon.data('remove-spaces'); 2806 let addonLength = addon.val().length; 2807 if (remove_spaces) { 2808 addonLength = addon.val().replace(/\s+/g, '').length; 2809 } 2810 addon.attr('data-price', addonLength * defaultPrice); 2811 } 2812 }, 2813 productQtyOnKeyUp = function () { 2814 var productID = $(this).data('product-id'); 2815 var productQTY = $(this).val(); 2816 var productURL = '?add-to-cart=' + productID + '&quantity=' + productQTY; 2817 $(this).parent().find('a').attr('href', productURL); 2818 }, 2819 2820 datepickerSaveButton = function(ev) { 2821 ev.preventDefault(); 2822 2823 var elementToClick = $('#ui-datepicker-div .ui-state-active'); 2824 2825 if (0 == elementToClick.length) { 2826 elementToClick = $('#ui-datepicker-div .ui-datepicker-today'); 2827 } 2828 2829 elementToClick.click(); 2830 $('.hasDatepicker').datepicker('hide'); 2831 }, 2832 datepickertimeOnChange = function(ev) { 2833 ev.preventDefault(); 2834 var option_id = $('#ui-datepicker-div').attr('wapo-option-id'); 2835 var tempTime = $('#' + option_id).closest('.date-container').find('.temp-time'); 2836 2837 tempTime.text($(this).val()); 2838 } 2839 2840 requestAQuoteButton = function (e) { 2841 e.preventDefault(); 2842 2843 if (typeof yith_wapo_general === 'undefined') { 2844 yith_wapo_general = {do_submit: true}; 2845 } 2846 if (!checkRequiredMinMax()) { 2847 yith_wapo_general.do_submit = false;
2848 } else { 2849 yith_wapo_general.do_submit = true; 2850 } 2851 }, 2852 2853 compositePriceUpdated = function (event, total) { 2854 let global_qty = parseFloat($('form.cart.ywcp > div.quantity input.qty').val()); 2855 let price = global_qty ? total / global_qty : total; 2856 let addonsContainer = $(wapoDOM.addonsContainer); 2857 2858 addonsContainer.attr('data-product-price', price); 2859 calculateTotalAddonsPrice(); 2860 }, 2861 setError = function ( option, message ) { 2862 option.find( '.yith-wapo-error' ).remove(); 2863 let label = option.find( '.yith-wapo-addon-label' ); 2864 2865 label.addClass( 'wapo-error' ); 2866 2867 option.append( '<small class="yith-wapo-error">' + message + '</small>' ); 2868 2869 }, 2870 cleanError = function( option ) { 2871 let label = option.find( '.yith-wapo-addon-label' ); 2872 2873 option.find( '.yith-wapo-error' ).remove(); 2874 label.removeClass( 'wapo-error' ); 2875 } 2876 2877 2878 2879 2880 2881 2882 // ***** EVENTS ****** 2883 2884 // Calculate Add-ons price triggers 2885 $(document).on( 2886 'ywgc-amount-changed', 2887 function (e, button_amount) { 2888 let price = button_amount.data('price'); 2889 let container = $(wapoDOM.addonsContainer); 2890 2891 container.attr('data-product-price', price); 2892 calculateTotalAddonsPrice(); 2893 } 2894 ); 2895 2896 /** 2897 * Since 4.0.0 - Allow external plugins the possibility to recalculate totals, after having changed the price. 2898 * 2899 * Price param is necessary. 2900 */ 2901 $(document).on( 2902 'yith-wapo-product-price-updated', 2903 function (e, price) { 2904 if (typeof price !== 'undefined') { 2905 $(wapoDOM.addonsContainer).attr('data-product-price', price); 2906 } 2907 calculateTotalAddonsPrice(); 2908 } 2909 ); 2910 2911 // YITH WC Gift Cards integration. 2912 $(document).on('change', '.gift-cards-list .ywgc-manual-amount-container input.ywgc-manual-amount', function (e) { 2913 let t = $(this), 2914 price = t.val(); 2915 2916 let container = $(wapoDOM.addonsContainer); 2917 2918 container.attr('data-product-price', price); 2919 calculateTotalAddonsPrice(); 2920 }); 2921 2922 // WC Product Bundles integration. 2923 $(document).on('woocommerce-product-bundle-updated-totals', function (e, bundle) { 2924 let bundlePrice = bundle.price_data.subtotals.price; 2925 let bundlePriceH = $(bundle.$bundle_price); 2926 let bundlePriceHtml = bundlePriceH.find('.amount').html(); 2927 2928 let container = $(wapoDOM.addonsContainer); 2929 2930 container.attr('data-product-price', bundlePrice); 2931 calculateTotalAddonsPrice(); 2932 2933 }); 2934 2935 // YITH Dynamic integration. 2936 $(document).on( 2937 'ywdpd_price_html_updated', 2938 function (e, html_price) { 2939 let price = $(html_price).children('.amount bdi').text(); 2940 price = wcPriceToFloat(price); 2941 2942 if (!isNaN(price)) { 2943 let container = $(wapoDOM.addonsContainer); 2944 2945 container.attr('data-product-price', price); 2946 calculateTotalAddonsPrice(); 2947 } 2948 } 2949 ); 2950 // YITH WC Product Bundles integration. 2951 $(document).on( 2952 'yith_wcpb_ajax_update_price_request', 2953 function (e, response) { 2954 2955 let price = response.price; 2956 2957 if (!isNaN(price)) { 2958 2959 $(wapoDOM.addonsContainer).attr('data-product-price', price); 2960 2961 $( document ).trigger( 'yith-wapo-reload-addons', price ); 2962 } 2963 } 2964 ); 2965 2966 $(document).on( 2967 'change', 2968 'form.cart div.yith-wapo-addon, form.cart .quantity input[type=number]', 2969 function (e) { 2970 if ($(e.delegateTarget.activeElement).hasClass('yith-wapo-edit-product-cart')) { 2971 return false; 2972 } 2973 2974 let addonOpt = $(e.target); 2975 maybeCalculateTotals(addonOpt) 2976 } 2977 ); 2978 $(document).on( 2979 'keyup', 2980 'form.cart .yith-wapo-addon-type-number input[type="number"], form.cart .yith-wapo-addon-type-text input[type="text"], form.cart .yith-wapo-addon-type-textarea textarea', 2981 function (e) { 2982 if ($(e.delegateTarget.activeElement).hasClass('yith-wapo-edit-product-cart')) { 2983 return false; 2984 } 2985 2986 let addonOpt = $(this); 2987 2988 maybeCalculateTotals(addonOpt) 2989 } 2990 ); 2991 $(document).on( 2992 'wapo-colorpicker-change wapo-colorpicker-clear', 2993 function (e, addonOpt) { 2994 if ($(e.delegateTarget.activeElement).hasClass('yith-wapo-edit-product-cart')) { 2995 return false; 2996 } 2997 maybeCalculateTotals($(addonOpt)) 2998 } 2999 ); 3000 3001 /** Product quantity change */ 3002 $( document ).on( 'change keyup', '.yith-wapo-option .wapo-product-qty', productQuantityChange ); 3003 $( document ).on( 'reset_data', resetAddons ); 3004 $( document ).on( 'found_variation', foundVariation ); 3005 3006 /** ajax reload addons **/ 3007 $( document ).on( 'yith-wapo-reload-addons', reloadAddons ); 3008 3009 /** Uploads */ 3010 $( document ).on( 'click', '.yith-wapo-uploaded-file .remove', removeUploadedFile ); 3011 3012 /** Checkboxes click */ 3013 $( 'form.cart' ).on('click', 'span.checkboxbutton', function() { 3014 if ( $( this ).find( 'input' ).is( ':checked' ) ) { 3015 $( this ).addClass( 'checked' ); 3016 } else { 3017 $( this ).removeClass( 'checked' ); 3018 } 3019 }); 3020 /** Checkboxes change */ 3021 $( document ).on( 'change', '.yith-wapo-addon-type-checkbox input', checkboxOnChange ); 3022 3023 /** Labels onChange */ 3024 $( document ).on('click', '.yith-wapo-addon-type-label .yith-wapo-option div.label', function() { 3025 $( this ).closest( '.yith-wapo-option' ).find( '.yith-proteo-standard-checkbox' ).click(); 3026 } ); 3027 $( document ).on( 'change', '.yith-wapo-addon-type-label input', labelsOnChange ); 3028 3029 /** Input text or textarea change */ 3030 $(document).on('change keyup', '.yith-wapo-addon-type-text input', inputOnChange); 3031 $(document).on('change keyup', '.yith-wapo-addon-type-textarea textarea', textareaOnChange); 3032 3033 /** Color click */ 3034 $( document ).on('click', '.yith-wapo-addon-type-color .yith-wapo-option div.label', function() { 3035 $( this ).closest( '.yith-wapo-option' ).find( '.yith-proteo-standard-checkbox' ).click(); 3036 } ); 3037 $( document ).on('change', '.yith-wapo-addon-type-color input', colorOnChange ); 3038 3039 /** Product click */ 3040 $( document ).on( 'click change', '.yith-wapo-addon-type-product .quantity input', function (e) { 3041 e.stopPropagation(); 3042 } 3043 ); 3044 $( document ).on('click', '.yith-wapo-addon-type-product .yith-wapo-option .product-container', 3045 function() { 3046 $( this ).closest( '.yith-wapo-option' ).find( '.yith-proteo-standard-checkbox' ).click(); 3047 } 3048 ); 3049 $( document ).on('change', '.yith-wapo-addon-type-product .yith-wapo-option input.yith-proteo-standard-checkbox', productOnChange ); 3050 3051 /** Add-on image click */ 3052 $( document ).on( 'click', '.yith-wapo-option .image-container', addonImageClicked ); 3053 3054 /** Radio click */ 3055 $('form.cart').on('click', 'span.radiobutton', function () { 3056 if ($(this).find('input').is(':checked')) { 3057 $(this).closest('.yith-wapo-addon.yith-wapo-addon-type-radio').find('span.radiobutton.checked').removeClass('checked'); 3058 $(this).addClass('checked'); 3059 } 3060 }); 3061 /** Radio change */ 3062 $( document ).on('click', '.yith-wapo-addon-type-radio input', radioOnChange); 3063 3064 /** Select click */ 3065 $( document ).on('change', '.yith-wapo-addon-type-select select', selectOnChange); 3066 3067 /** Input file */ 3068 $( document ).on('change', '.yith-wapo-addon-type-file input.file', fileOnChange); 3069 $( document ).on('click', '.yith-wapo-ajax-uploader .button, .yith-wapo-ajax-uploader .link', function() { 3070 $( this ).closest( '.yith-wapo-option').find( 'input.file' ).click(); 3071 }); 3072 /** Number change */ 3073 $(document).on('change keyup', '.yith-wapo-addon-type-number input', numberOnChange); 3074 3075 /** Datepicker change */ 3076 $( document ).on('click', '#wapo-datepicker-save button', datepickerSaveButton);
3077 $( document ).on('change', '#wapo-datepicker-time-select', datepickertimeOnChange); 3078 3079 /** Toggle feature */ 3080 $( document ).on('click', '.yith-wapo-addon.wapo-toggle .addon-header', toggleElement); 3081 3082 /** Product quantity on key up */ 3083 $( document ).on( 'keyup', '.wapo-product-qty', productQtyOnKeyUp ); 3084 3085 // WooCommerce Measurement Price Calculator (compatibility) 3086 $( 'form.cart' ).on('change', '#price_calculator.wc-measurement-price-calculator-price-table', yithWcMeasurementPC_Compatibility ); 3087 3088 // YITH WooCommerce Composite. Recalculate add-ons with the composite price. 3089 $( document ).on( 'yith_wcp_price_updated', function( ev, finalPrice ){ 3090 $( document ).trigger( 'yith-wapo-reload-addons', finalPrice ); 3091 }); 3092 3093 /** Ajax upload file */ 3094 // preventing page from redirecting. 3095 $('html').on('dragover, drop', function (e) { 3096 e.preventDefault(); 3097 e.stopPropagation(); 3098 }); 3099 // drag enter 3100 $(document).on('dragenter', '.yith-wapo-ajax-uploader', function (e) { 3101 e.stopPropagation(); 3102 e.preventDefault(); 3103 $(this).css('opacity', '0.5'); 3104 }); 3105 // drag over 3106 $(document).on('dragover', '.yith-wapo-ajax-uploader', function (e) { 3107 e.stopPropagation(); 3108 e.preventDefault(); 3109 }); 3110 3111 // drag leave 3112 $(document).on('dragleave', '.yith-wapo-ajax-uploader', function (e) { 3113 e.stopPropagation(); 3114 e.preventDefault(); 3115 if ($(e.target).hasClass('yith-wapo-ajax-uploader')) { 3116 $(this).css('opacity', '1'); 3117 } 3118 } 3119 ); 3120 3121 $( 'body' ).on('drop', '.yith-wapo-ajax-uploader', ajaxUploaderOnDrop); 3122 3123 /** Min max rules */ 3124 $(document).on('change', '.yith-wapo-addon-type-checkbox, .yith-wapo-addon-type-color, .yith-wapo-addon-type-label, .yith-wapo-addon-type-product', function () { 3125 checkMinMax($(this)); 3126 }); 3127 3128 // Check required fields before adding to cart (Required select and min/max values). 3129 $(document).on('click', 'form.cart button', function () { 3130 return checkAddonsRequirements(); 3131 }); 3132 3133 3134 /** YITH Composite */ 3135 $( document ).on( 'yith_wcp_price_updated', compositePriceUpdated ); 3136 3137 /** YITH RAQ */ 3138 $( document ).on('click', '.add-request-quote-button', requestAQuoteButton); 3139 3140 3141 /** Init JS */ 3142 initJS(); 3143 3144 } 3145);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.