1var ppecHandler = function( data ) { 2 this.data = data; 3 this.actions = {}; 4 5 const parent = this; 6 7 // TODO: Need to convert this to vanilla js 8 this.processPayment = function( payment, action ) { 9 jQuery.post( ppecFrontVars.ajaxUrl, { 10 'action': action, 11 wp_ppdg_payment: payment, 12 data: parent.data, 13 nonce: parent.data.nonce 14 } ) 15 .done( function( data ) { 16 parent.completePayment( data ); 17 } ); 18 }; 19 20 this.completePayment = function( data ) { 21 let ret = true; 22 let dlgTitle = ppecFrontVars.str.paymentCompleted; 23 let dlgMsg = ppecFrontVars.str.redirectMsg; 24 let redirect_url; 25 if ( data.redirect_url ) { 26 redirect_url = data.redirect_url; 27 } else { 28 dlgTitle = ppecFrontVars.str.errorOccurred; 29 dlgMsg = data; 30 ret = false; 31 } 32 document.querySelector( '.wp-ppec-overlay[data-ppec-button-id="' + parent.data.id + '"]' ).style.display = 'none'; 33 34 const dialogMsgParagraph = document.createElement('p'); 35 dialogMsgParagraph.id = 'wp-ppdg-dialog-msg'; 36 dialogMsgParagraph.textContent = dlgMsg; 37 const dialog = document.createElement('div'); 38 dialog.id = 'wp-ppdg-dialog-message'; 39 dialog.title = dlgTitle; 40 dialog.appendChild(dialogMsgParagraph); 41 42 parent.scCont?.querySelector('.wp-ppec-button-container')?.insertAdjacentElement('afterend', dialog); 43 44 // Fade in dialog 45 dialog.style.display = 'none'; 46 dialog.style.opacity = 0; 47 dialog.style.transition = 'opacity 0.5s'; 48 dialog.style.display = 'block'; 49 setTimeout(() => { 50 dialog.style.opacity = 1; 51 }, 10); 52 53 if ( redirect_url ) { 54 location.href = redirect_url; 55 } 56 return ret; 57 }; 58 59 this.ValidatorTos = function( input ) { 60 return input.checked !== true ? ppecFrontVars.str.acceptTos : ''; 61 }; 62 63 this.ValidatorBilling = function( input ) { 64 let val; 65 if (input.type === 'checkbox'){ 66 val = input.checked; 67 } else { 68 val = Boolean(input.value.trim()) 69 } 70 71 return parent.isElementVisible(input) && !val ? ppecFrontVars.str.required : null; 72 }; 73 74 this.isValidTotal = function() { 75 parent.calcTotal(); 76 return !!parent.data.total; 77 }; 78 79 this.ValidatorQuantity = function( input ) { 80 const val_orig = input.value; 81 const val = parseInt( val_orig ); 82 let error = false; 83 if ( isNaN( val ) || ( val_orig % 1 !== 0 ) || val <= 0 ) { 84 error = ppecFrontVars.str.enterQuantity; 85 } else if ( parent.data.stock_enabled && val > parent.data.stock_items ) { 86 error = ppecFrontVars.str.stockErr.replace( '%d', parent.data.stock_items ); 87 } else { 88 parent.data.quantity = val; 89 } 90 return error; 91 }; 92 93 this.ValidatorAmount = function( input ) { 94 const val_orig = input.value; 95 const val = parseFloat( val_orig ); 96 const min_val = input.getAttribute( 'min' ); 97 let error = false; 98 const errMsg = ppecFrontVars.str.enterAmount; 99 if ( !isNaN( val ) && min_val <= val ) { 100 parent.data.orig_price = val; 101 parent.data.price = parent.applyVariations( val ); 102 } else { 103 error = errMsg; 104 } 105 return error; 106 }; 107 108 // if ( this.data.btnStyle.layout === 'horizontal' ) { 109 // this.data.btnStyle.tagline = false; 110 // } 111 // 112 // this.clientVars = {}; 113 // 114 // this.clientVars[ this.data.env ] = this.data.client_id; 115 116 this.inputs = []; 117 118 this.validateInput = function( input, validator ) { 119 if ( !input ) { 120 return false; 121 } 122 123 this.inputs.push( [ input, validator ] ); 124 125 input.addEventListener('change', function(e) { 126 parent.displayInputError( this, validator ); 127 parent.validateOrder(); 128 }) 129 }; 130 131 this.validateOrder = function() { 132 let isValid = true; 133 134 document.querySelectorAll('#wpec_billing_' + parent.data.id + ', #place-order-' + parent.data.id ).forEach(el => { 135 parent.toggleVisibility(el, 'block', !parent.isValidTotal()); 136 }) 137 138 parent.toggleVisibility( 139 document.getElementById( parent.data.id ), 140 'block', 141 !parent.isElementVisible(document.getElementById( 'place-order-' + parent.data.id )) 142 ); 143 144 parent.inputs.forEach( function( inputArr ) { 145 const input = inputArr[ 0 ]; 146 const validator = inputArr[ 1 ]; 147 if ( validator( input ) ) { 148 isValid = false; 149 } 150 } ); 151 152 if ( !parent.isValidTotal() ) { 153 isValid = false; 154 } 155 156 // TODO: this may not needed any more. 157 // if ( isValid ) { 158 // parent.actions?.enable(); 159 // } else { 160 // parent.actions?.disable(); 161 // } 162
163 parent.updateAllAmounts(); 164 165 // Dispatch a custom event for addons to listen on 'validateOrder' function run. 166 document.dispatchEvent(new CustomEvent('wpec_validate_order', { 167 detail: { 168 ppecHandler: parent, 169 isValid: isValid, 170 } 171 })) 172 jQuery( document ).trigger( 'wpec_validate_order', [ parent ] ); // TODO: Need to remove this jquery version after all addon code is updated to the vanilla version. 173 174 return isValid; 175 }; 176 177 this.displayInputError = function( input, validator ) { 178 const error = validator( input ); 179 const errMsgCont = input.type === 'checkbox' || input.type === 'number' ? input.parentNode.parentNode.querySelector( '.wp-ppec-form-error-msg' ) : input.parentNode.querySelector( '.wp-ppec-form-error-msg' ); 180 181 if (error && error.length){ 182 input.classList.add('hasError'); 183 errMsgCont.style.display = 'block'; 184 errMsgCont.innerText = error; 185 }else { 186 input.classList.remove('hasError'); 187 errMsgCont.style.display = 'none'; 188 errMsgCont.innerText = ''; 189 } 190 }; 191 192 this.displayErrors = function() { 193 parent.inputs.forEach( function( input ) { 194 parent.displayInputError( input[ 0 ], input[ 1 ] ); 195 } ); 196 }; 197 198 /** 199 * @deprecated Use `getScCont()` instead. 200 */ 201 this.scCont = document.querySelector( '.wp-ppec-shortcode-container[data-ppec-button-id="' + parent.data.id + '"]' ); 202 203 this.getScCont = function(){ 204 return document.querySelector( '.wp-ppec-shortcode-container[data-ppec-button-id="' + parent.data.id + '"]' ); 205 } 206 207 // TODO: Need to convert this to vanilla js 208 this.formatMoney = function( n ) { 209 var decimalPlaces = isNaN(decimalPlaces = Math.abs(parent.data.dec_num)) ? 2 : parent.data.dec_num; 210 var decimalSeparator = parent.data.dec_sep || "."; 211 var thousandSeparator = parent.data.thousand_sep || ","; 212 var sign = n < 0 ? "-" : ""; 213 var integerPart = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(decimalPlaces))); 214 var integerPartLength = integerPart.length; 215 var remainderLength = integerPartLength > 3 ? integerPartLength % 3 : 0; 216 217 var formattedIntegerPart = sign + 218 (remainderLength ? integerPart.substr(0, remainderLength) + thousandSeparator : "") + 219 integerPart.substr(remainderLength).replace(/(\d{3})(?=\d)/g, "$1" + thousandSeparator) + 220 (decimalPlaces ? decimalSeparator + Math.abs(n - integerPart).toFixed(decimalPlaces).slice(2) : ""); 221 222 var formats = { 223 left: '{symbol}{price}', 224 left_space: '{symbol} {price}', 225 right: '{price}{symbol}', 226 right_space: '{price} {symbol}' 227 }; 228 229 var formattedPrice = formats[parent.data.curr_pos] 230 .replace('{symbol}', parent.data.currency_symbol) 231 .replace('{price}', formattedIntegerPart); 232 233 return formattedPrice; 234 }; 235 236 this.getPriceContainer = function() { 237 // Here, we're using 'querySelectorAll' instead of 'querySelector', because there is more than one element. 238 return document.querySelectorAll( '.' + parent.scCont.getAttribute( 'data-price-class' ) ); 239 }; 240 241 this.getProductItemContainer = function() { 242 // Get the product item container using the price container, so that we don't select other product item container when more than one same product shortcode in a page. 243 const priceContainer = document.querySelector( '.' + parent.scCont.getAttribute( 'data-price-class' ) ); 244 245 // Selector of the container may vary across different templates. Select the one it can find. 246 247 // For template 1,3 248 let productItemContainer = parent.closestParent(priceContainer, '.wpec-product-item-' + parent.data.product_id ); 249 250 // For template 2 251 if (!productItemContainer){ 252 productItemContainer = parent.closestParent(priceContainer, '.wpec-post-item-' + parent.data.product_id ); 253 } 254 255 // For url payment form 256 if (!productItemContainer){ 257 productItemContainer = parent.closestParent(priceContainer, '.wpec-modal-product-' + parent.data.product_id ); 258 } 259 260 return productItemContainer; 261 }; 262 263 this.updateAllAmounts = function() { 264 parent.calcTotal(); 265 266 const productItemContainer = parent.getProductItemContainer(); 267 268 if ( productItemContainer ){ 269 productItemContainer.querySelectorAll( '.wpec-price-amount' ).forEach(el => el.textContent = parent.formatMoney( parent.data.price )) 270 271 if ( parent.data.quantity > 1 ) { 272 productItemContainer.querySelectorAll( '.wpec-quantity' ).forEach(el => el.style.display = 'inline') 273 productItemContainer.querySelectorAll( '.wpec-quantity-val' ).forEach(el => el.textContent = parent.data.quantity) 274 } else { 275 productItemContainer.querySelectorAll( '.wpec-quantity' ).forEach(el => el.style.display = 'none'); 276 } 277 278 const total = productItemContainer.querySelectorAll( '.wpec_tot_current_price' ); 279 const tot_new = productItemContainer.querySelectorAll( '.wpec_tot_new_price' ); 280 const price_new = productItemContainer.querySelectorAll( '.wpec-new-price-amount' ); 281 282 if ( typeof parent.data.discountAmount !== "undefined" ) {
283 price_new.forEach(el => el.textContent = parent.formatMoney( parent.data.newPrice ) ); 284 tot_new.forEach(el => el.textContent = parent.formatMoney( parent.data.total ) ); 285 total.forEach(el => el.textContent = parent.formatMoney( parent.data.subtotal )); 286 } else if ( total.length > 0 ) { 287 total.forEach(el => el.textContent = parent.formatMoney( parent.data.total )); 288 } 289 290 // Update the total shipping cost. 291 const shipping_new = productItemContainer.querySelectorAll( '.wpec_price_shipping_amount' ); 292 shipping_new.forEach(el => el.textContent = parent.formatMoney(parent.getTotalShippingCost())); 293 294 // Update the total tax cost. 295 const tax_val = productItemContainer.querySelectorAll( '.wpec-tax-val' ); 296 tax_val.forEach(el => el.textContent = parent.formatMoney( parent.data.tax_amount * parent.data.quantity ) ); 297 } 298 299 // TODO: Need to convert this to vanilla js 300 jQuery( document ).trigger( 'wpec_after_update_all_amounts', [ parent ] ); 301 }; 302 303 this.calcTotal = function() { 304 let itemSubt = parseFloat( parent.data.price ); 305 let quantity = parseInt( parent.data.quantity ); 306 let tAmount = itemSubt * quantity; 307 //We need to round to 2 decimal places to make sure that the API call will not fail. 308 let roundedTotal = tAmount.toFixed(2);//round to 2 decimal places 309 let roundedTotalAsNumber = parseFloat(roundedTotal);//convert to number 310 tAmount = roundedTotalAsNumber; 311 let subtotal = tAmount; 312 313 parent.data.newPrice = itemSubt; 314 315 if ( typeof parent.data.discount !== "undefined" ) { 316 let discountAmount = 0; 317 if ( parent.data.discountType === 'perc' ) { 318 discountAmount = parent.PHP_round( tAmount * parent.data.discount / 100, parent.data.dec_num ); 319 } else { 320 discountAmount = data.discount; 321 } 322 if ( discountAmount > tAmount ) { 323 discountAmount = tAmount; 324 } 325 tAmount = tAmount - discountAmount; 326 parent.data.discountAmount = parent.PHP_round( discountAmount, parent.data.dec_num ); 327 parent.data.newPrice = parent.PHP_round( itemSubt - discountAmount / quantity, parent.data.dec_num ); 328 } 329 330 if ( parent.data.tax ) { 331 let tax = parent.PHP_round( tAmount / quantity * parent.data.tax / 100, parent.data.dec_num ); 332 parent.data.tax_amount = tax; 333 tAmount += tax * parent.data.quantity; 334 subtotal += parent.PHP_round( subtotal / quantity * parent.data.tax / 100, parent.data.dec_num ) * parent.data.quantity; 335 } 336 337 const totalShippingCost = parent.getTotalShippingCost(); 338 if ( totalShippingCost ) { 339 tAmount += parseFloat( totalShippingCost ); 340 subtotal += parseFloat( totalShippingCost ); 341 } 342 parent.data.total = parent.PHP_round( tAmount, parent.data.dec_num ); 343 parent.data.subtotal = parent.PHP_round( subtotal, parent.data.dec_num ); 344 }; 345 346 this.PHP_round = function( num, dec ) { 347 return Math.round( num * Math.pow( 10, dec ) ) / Math.pow( 10, dec ); 348 }; 349 350 this.applyVariations = function( amount ) { 351 let grpId; 352 if ( parent.data.variations.applied ) { 353 for ( grpId = 0; grpId < parent.data.variations.applied.length; ++grpId ) { 354 let variation = parent.data.variations[ grpId ]; 355 amount = amount + parseFloat( variation.prices[ parent.data.variations.applied[ grpId ] ] ); 356 } 357 } 358 return parent.PHP_round( amount, parent.data.dec_num ); 359 }; 360 361 /** 362 * Calculates the total shipping cost. 363 * It calculates the per quantity shipping cost along with the base shipping cost of that product. 364 * 365 * @returns {float} The total shipping cost in float of two decimal places. 366 */ 367 this.getTotalShippingCost = function() { 368 let total = 0; 369 const quantity = parent.data.quantity ? parseInt(parent.data.quantity) : 1; 370 const baseShipping = parent.data.shipping ? parseFloat( parent.data.shipping ) : 0; 371 const shippingPerQuantity = parent.data.shipping_per_quantity ? parseFloat( parent.data.shipping_per_quantity ) : 0; 372 373 total = baseShipping + (shippingPerQuantity * quantity); 374 // Round to 2 decimal places. 375 total = parseFloat(total.toFixed(2)) 376 return total; 377 } 378 379 this.toggleVisibility = function (element, displayVisibilityType = 'block', condition = null) { 380 if (!element){ 381 return; 382 } 383 384 // if no condition provided, then the condition will be based on whether the element is visible or not. 385 if (condition === null) { 386 // No specific condition provided. 387 // The content will only visible/invisible if its currently invisible/visible respectively. 388 condition = !parent.isElementVisible(element); 389 } 390 391 // Show / hide the element based on condition. 392 if (condition === true) { 393 element.style.display = displayVisibilityType; 394 } else { 395 element.style.display = 'none'; 396 } 397 } 398 399 this.isElementVisible = function (element) { 400 // Check if element exists in the DOM and is an Element node 401 if (!(element instanceof Element)) { 402 return false;
403 } 404 405 // Check if the element has a non-zero bounding box (displayed and has size) 406 const style = getComputedStyle(element); 407 const hasVisibleDisplay = style.display !== 'none'; 408 const hasVisibleVisibility = style.visibility !== 'hidden'; 409 const hasNonZeroSize = element.offsetWidth > 0 && element.offsetHeight > 0; 410 411 return hasVisibleDisplay && hasVisibleVisibility && hasNonZeroSize; 412 } 413 414 this.closestParent = function (element, selector) { 415 // Traverse up the DOM tree 416 while (element) { 417 // Check if the current element matches the selector 418 if (element.matches(selector)) { 419 return element; 420 } 421 // Move to the parent element 422 element = element.parentElement; 423 } 424 // Return null if no matching parent is found 425 return null; 426 } 427 428 this.construct = function(){ 429 parent.validateInput( document.getElementById( 'wpec-tos-' + parent.data.id ), parent.ValidatorTos ); 430 parent.validateInput( document.querySelector( '#wp-ppec-custom-quantity[data-ppec-button-id="' + parent.data.id + '"]' ), parent.ValidatorQuantity ); 431 parent.validateInput( document.querySelector( '#wp-ppec-custom-amount[data-ppec-button-id="' + parent.data.id + '"]' ), parent.ValidatorAmount ); 432 document.querySelectorAll( '#wpec_billing_' + parent.data.id + ' .wpec_required' ).forEach( function(element) { 433 parent.validateInput( element, parent.ValidatorBilling ); 434 } ); 435 parent.data.orig_price = parseFloat( parent.data.price ); 436 parent.scCont.querySelectorAll( 'select.wpec-product-variations-select, input.wpec-product-variations-select-radio' )?.forEach(function (item){ 437 item.addEventListener('change', function (e){ 438 const grpId = e.currentTarget.getAttribute( 'data-wpec-variations-group-id' ); 439 const varId = e.currentTarget.value; 440 if ( Object.getOwnPropertyNames( parent.data.variations ).length !== 0 ) { 441 if ( !parent.data.variations.applied ) { 442 parent.data.variations.applied = []; 443 } 444 parent.data.variations.applied[ grpId ] = varId; 445 parent.data.price = parent.applyVariations( parent.data.orig_price ); 446 parent.validateOrder(); 447 } 448 }) 449 }); 450 parent.scCont.querySelectorAll( 'select.wpec-product-variations-select, input.wpec-product-variations-select-radio:checked' )?.forEach(el => { 451 // Dispatch change event to trigger any 'change' event listeners on this element 452 el.dispatchEvent( new Event('change') ); 453 }) 454 455 parent.scCont.querySelector( '.wpec_product_shipping_enable' )?.addEventListener( 'change', function() { 456 parent.toggleVisibility(parent.scCont.querySelector( '.wpec_shipping_address_container' ), 'inherit'); 457 parent.scCont.querySelector( '.wpec_address_wrap' )?.classList.toggle('shipping_enabled'); 458 } ); 459 460 document.getElementById('place-order-' + parent.data.id)?.addEventListener('click', () => { 461 const hasInputErrors = this.checkAndShowInputErrors(); 462 if (hasInputErrors){ 463 return; 464 } 465 466 if ( !parent.data.total ) { 467 const same_shipping_billing_address_enabled = parent.scCont.querySelector( '.wpec_product_shipping_enable' )?.checked; 468 const billing_address = { 469 address_line_1: document.getElementById( 'wpec_billing_address-' + parent.data.id )?.value, 470 admin_area_1: document.getElementById( 'wpec_billing_state-' + parent.data.id )?.value, 471 admin_area_2: document.getElementById( 'wpec_billing_city-' + parent.data.id )?.value, 472 postal_code: document.getElementById( 'wpec_billing_postal_code-' + parent.data.id )?.value, 473 country_code: document.getElementById( 'wpec_billing_country-' + parent.data.id )?.value, 474 }; 475 const shipping_address = same_shipping_billing_address_enabled ? billing_address : { 476 address_line_1: document.getElementById( 'wpec_shipping_address-' + parent.data.id )?.value, 477 admin_area_1: document.getElementById( 'wpec_shipping_state-' + parent.data.id )?.value, 478 admin_area_2: document.getElementById( 'wpec_shipping_city-' + parent.data.id )?.value, 479 postal_code: document.getElementById( 'wpec_shipping_postal_code-' + parent.data.id )?.value, 480 country_code: document.getElementById( 'wpec_shipping_country-' + parent.data.id )?.value, 481 }; 482 483 const paymentData = { 484 payer: { 485 name: { 486 given_name: document.getElementById( 'wpec_billing_first_name-' + parent.data.id )?.value, 487 surname: document.getElementById( 'wpec_billing_last_name-' + parent.data.id )?.value, 488 }, 489 email_address: document.getElementById( 'wpec_billing_email-' + parent.data.id )?.value, 490 address: billing_address, 491 shipping_address: shipping_address, 492 } 493 } 494 495 document.dispatchEvent(new CustomEvent('wpec_process_free_payment', { 496 detail: { 497 paymentData, 498 data: parent.data, 499 } 500 })); 501 502 parent.processPayment(paymentData, 'wpec_process_empty_payment' ); 503 } 504 }); 505 506 document.getElementById( 'wpec-redeem-coupon-btn-' + parent.data.id )?.addEventListener('click', async function (e) { 507 e.preventDefault(); 508 const wpecCouponBtn = e.currentTarget; 509 const wpecCouponInputField = document.getElementById('wpec-coupon-field-' + parent.data.id) 510 const couponCode = wpecCouponInputField?.value; 511 if (couponCode.trim() === '') { 512 return false;
513 } 514 const wpecCouponSpinner = wpecCouponBtn.querySelector('svg'); 515 wpecCouponBtn.setAttribute('disabled', true); 516 wpecCouponSpinner.style.display = 'inline'; 517 518 const ajaxData = new URLSearchParams({ 519 'action': 'wpec_check_coupon', 520 'product_id': parent.data.product_id, 521 'coupon_code': couponCode, 522 'curr': parent.data.currency, 523 /*'amount': parent.data.item_price, 524 'tax': parent.data.tax, 525 'shipping': parent.data.shipping*/ 526 }); 527 try { 528 const response = await fetch(ppecFrontVars.ajaxUrl, { 529 method: "post", 530 headers: { 531 'Content-Type': 'application/x-www-form-urlencoded' 532 }, 533 body: ajaxData 534 }); 535 536 const response_data = await response.json(); 537 538 if (response_data.success) { 539 parent.data.discount = response_data.discount; 540 parent.data.discountType = response_data.discountType; 541 parent.data.couponCode = response_data.code; 542 543 document.getElementById('wpec-coupon-info-' + parent.data.id).innerHTML = '<span class="wpec_coupon_code">' + response_data.discountStr + '</span><button class="wpec_coupon_apply_btn" id="wpec-remove-coupon-' + parent.data.id + '" title="' + ppecFrontVars.str.strRemoveCoupon + '">' + ppecFrontVars.str.strRemove + '</button>' 544 document.getElementById('wpec-redeem-coupon-btn-' + parent.data.id).style.display = 'none'; 545 document.getElementById('wpec-coupon-field-' + parent.data.id).style.display = 'none'; 546 547 const totalContainers = parent.getPriceContainer(); 548 // Note: there is more than one price container in a single product shortcode. 549 totalContainers.forEach(function (totalCont){ 550 let totCurr; 551 let totNew; 552 if (totalCont.querySelector('.wpec_price_full_total')) { 553 totCurr = totalCont.querySelector('span.wpec_tot_current_price') 554 totCurr?.classList.add('wpec_line_through'); 555 totNew = totalCont.querySelector('span.wpec_tot_new_price'); 556 } else { 557 totCurr = totalCont.querySelector('span.wpec_price_amount') 558 totCurr?.classList.add('wpec_line_through'); 559 totNew = totalCont.querySelector('span.wpec_new_price_amount'); 560 } 561 562 const priceCurr = totalCont.querySelector('span.wpec-price-amount') 563 priceCurr?.classList.add('wpec_line_through'); 564 565 const priceNew = totalCont.querySelector('span.wpec-new-price-amount'); 566 parent.validateOrder(); 567 568 document.getElementById('wpec-remove-coupon-' + parent.data.id)?.addEventListener('click', function (e) { 569 e.preventDefault(); 570 document.getElementById('wpec-coupon-info-' + parent.data.id).innerHTML = ''; 571 document.getElementById('wpec-coupon-field-' + parent.data.id).value = ''; 572 document.getElementById('wpec-coupon-field-' + parent.data.id).style.display = 'block'; 573 document.getElementById('wpec-redeem-coupon-btn-' + parent.data.id).style.display = 'block'; 574 totCurr?.classList.remove('wpec_line_through'); 575 priceCurr?.classList.remove('wpec_line_through'); 576 if (totNew) { 577 totNew.innerHTML = ''; 578 } 579 if (priceNew) { 580 priceNew.innerHTML = ''; 581 } 582 delete parent.data.discount; 583 delete parent.data.discountType; 584 delete parent.data.couponCode; 585 delete parent.data.discountAmount; 586 delete parent.data.newPrice; 587 parent.validateOrder(); 588 }); 589 }) 590 } else { 591 document.getElementById('wpec-coupon-info-' + parent.data.id).innerHTML = response_data.msg 592 } 593 wpecCouponSpinner.style.display = 'none'; 594 wpecCouponBtn.removeAttribute('disabled'); 595 596 } catch (err) { 597 alert(err.message); 598 } 599 }) 600 601 const couponField = document.getElementById( 'wpec-coupon-field-' + parent.data.id ); 602 couponField?.addEventListener( 'keydown', function( keyboardEvent ) { 603 if ( keyboardEvent.code === 13 ) { 604 keyboardEvent.preventDefault(); 605 document.getElementById( 'wpec-redeem-coupon-btn-' + parent.data.id ).click(); 606 return false; 607 } 608 } ); 609 610 parent.validateOrder(); 611 612 if ( parent.data.errors ) { 613 const errorEl = document.createElement('div'); 614 errorEl.className = 'wp-ppec-form-error-msg'; 615 errorEl.innerHTML = parent.data.errors; 616 617 parent.getScCont().appendChild( errorEl ); 618 errorEl.style.display = 'block'; 619 } 620 621 document.querySelectorAll( '.wpec-button-placeholder' ).forEach( function (el) { 622 el.remove() 623 }) 624 625 } 626 this.construct(); // TODO: Need to move it to class constructor. 627 628 629 this.checkAndShowInputErrors = () => { 630 let result = false;
631 632 const wpecPlaceOrderButtonSection = document.getElementById("place-order-" + parent.data.id); 633 if (wpecPlaceOrderButtonSection) { 634 wpecPlaceOrderButtonSection.querySelector("button>svg").style.display = 'inline'; 635 wpecPlaceOrderButtonSection.querySelector("button").setAttribute("disabled", true); 636 } 637 parent.displayErrors(); 638 // Get first error input if there is any. 639 const errInput = parent.getScCont().querySelector('.hasError'); 640 if (errInput) { 641 errInput.focus(); 642 errInput.dispatchEvent(new Event('change')); 643 644 if (wpecPlaceOrderButtonSection) { 645 wpecPlaceOrderButtonSection.querySelector("button>svg").style.display = 'none'; 646 wpecPlaceOrderButtonSection.querySelector("button").removeAttribute("disabled"); 647 } 648 649 result = true; 650 } 651 652 return result; 653 } 654}; 655 656const wpecModal = function( $ ) { 657 let openModalId = false; 658 659 document.querySelectorAll( '.wpec-modal-open, .wpec-modal-overlay, .wpec-modal-close' ).forEach(function (el){ 660 el.addEventListener( 'click', function( e ) { 661 e.preventDefault(); 662 toggleModal( this.getAttribute( 'data-wpec-modal' ) ); 663 } ) 664 }); 665 666 document.onkeydown = function( evt ) { 667 evt = evt || window.event; 668 var isEscape = false;
669 if ( "key" in evt ) { 670 isEscape = ( evt.key === "Escape" || evt.key === "Esc" ); 671 } else { 672 isEscape = ( evt.keyCode === 27 ); 673 } 674 if ( isEscape && openModalId ) { 675 toggleModal(); 676 } 677 }; 678 679 function toggleModal( modalId ) { 680 modalId = modalId ? modalId : openModalId; 681 openModalId = modalId; 682 var modal = document.getElementById( modalId ); 683 modal.classList.toggle( 'wpec-opacity-0' ); 684 modal.classList.toggle( 'wpec-pointer-events-none' ); 685 } 686 687 document.querySelectorAll('.wpec-custom-number-input button').forEach(button => { 688 button.addEventListener('click', function() { 689 const increment = this.getAttribute('data-action') === 'increment' ? 1 : -1; 690 const customQuantityInput = this.parentElement.querySelector('input'); 691 const step = customQuantityInput.getAttribute('step') ? Number(customQuantityInput.getAttribute('step')) : 1; 692 customQuantityInput.value = Number(customQuantityInput.value) + increment * step; 693 customQuantityInput.dispatchEvent(new Event('change')); 694 }); 695 }); 696 697 // TODO: Need to convert this to vanilla js 698 //shortcode_wpec_show_all_products 699 $("#wpec-sort-by").change(function(){ 700 $("#wpec-sort-by-form").submit(); 701 }); 702}; 703 704jQuery( wpecModal );
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.