1/* global revenue_campaign Revenue jQuery */ 2// the below line ignores revenue_campaign not camel case warning 3/* eslint-disable camelcase */ 4jQuery( function ( $ ) { 5 if ( typeof revenue_campaign === 'undefined' ) { 6 console.error( 'Revenue campaign script not loaded.' ); 7 return; 8 } 9 10 function setSaveCapsuleText( amount ) { 11 const $amountPriceContianer = $( this ).find( 12 '.woocommerce-Price-amount' 13 ); 14 15 if ( $amountPriceContianer.length ) { 16 $amountPriceContianer.text( Revenue.formatPrice( amount ) ); 17 } 18 } 19 function setPriceText( price ) { 20 const $priceContainer = $( this ).find( '.woocommerce-Price-amount' ); 21 $priceContainer.text( Revenue.formatPrice( price ) ); 22 } 23 // to handle case when there is default attributes or variation of a product 24 // trigger change event on initial load. 25 $( function () { 26 // for all variation products 27 $( '[data-variations] .revx-product-Attr-wrapper' ).trigger( 'change' ); 28 // for volume discount variation products 29 $( 30 '[data-campaign-type="volume_discount"] .revx-product-Attr-wrapper' 31 ).trigger( 'change' ); 32 } ); 33 // for all other campaigns other than volume discount with variation product 34 $( document ).on( 35 'change', 36 '[data-variations] .revx-product-Attr-wrapper', 37 function () { 38 const $container = $( this ).closest( '[data-variations]' ); 39 const variationMap = JSON.parse( 40 $container.attr( 'data-variations' ) || '[]' 41 ); 42 43 // Collect selected attributes 44 const selectedAttrs = {}; 45 $container.find( '.revx-product-Attr-wrapper' ).each( function () { 46 selectedAttrs[ $( this ).attr( 'name' ) ] = $( this ).val(); 47 } ); 48 49 // Try to find a matching variation (compare with variation.attributes) 50 const matchedVariation = variationMap.find( ( variation ) => { 51 if ( ! variation.attributes ) return false; 52 return Object.entries( selectedAttrs ).every( 53 ( [ key, val ] ) => 54 ! val || 55 ! variation.attributes[ 56 key.replace( /attribute_/, '' ) 57 ] || 58 variation.attributes[ 59 key.replace( /attribute_/, '' ) 60 ] === val 61 ); 62 } ); 63 64 // Update price if we found a match 65 if ( matchedVariation ) { 66 const $offerItemContainer = $container; 67 const $saveAmountContainer = $offerItemContainer.find( 68 '[data-smart-tag="saveBadgeWrapper"]' 69 ); 70 const $regularPriceContainer = $offerItemContainer.find( 71 '.revx-product-regular-price' 72 ); 73 const $oldPriceContainer = $offerItemContainer.find( 74 '.revx-product-old-price' 75 ); 76 const $offeredPriceContainer = $offerItemContainer.find( 77 '.revx-product-offered-price' 78 ); 79 80 setSaveCapsuleText.call( 81 $saveAmountContainer, 82 matchedVariation.saved_amount 83 ); 84 setPriceText.call( 85 $regularPriceContainer, 86 matchedVariation.regular_price 87 ); 88 setPriceText.call( 89 $oldPriceContainer, 90 matchedVariation.regular_price 91 ); 92 $container.attr( 93 'data-regular-price', 94 matchedVariation?.regular_price 95 ); 96 // Trigger products and buy-x-get-y products use their regular price. 97 const isRequired = 98 $container.data( 'is-x-product' ) === 'yes' || 99 $container.data( 'is-trigger' ) === 'yes'; 100 // if the product is trigger product or x product, it will get regular price 101 setPriceText.call( 102 $offeredPriceContainer, 103 matchedVariation?.offered_price || 104 ( isRequired ? matchedVariation?.regular_price : 0 ) 105 ); 106 $container.attr( 107 'data-offered-price', 108 matchedVariation?.offered_price || 109 ( isRequired ? matchedVariation?.regular_price : 0 ) 110 ); 111 // since both have some common classes, this can be done with same function 112 // make seperate funciton if situation changes, and use else if ladder or if. 113 if ( 114 $container.attr( 'campaign_type' ) === 'bundle_discount' || 115 $container.attr( 'campaign_type' ) === 'buy_x_get_y' 116 ) { 117 const $bundleTopContainer = 118 $container.closest( '.revx-template' ); 119 $bundleTopContainer.trigger( 'revx-variation-changed' ); 120 // updateBundleTotal( $bundleTopContainer ); 121 } 122 $( document ).trigger( 123 'revx-campaign-variation-updated', 124 [ $container, matchedVariation, isRequired ] 125 ); 126 } 127 128 // === Filter other attributes === 129 $container.find( '.revx-product-Attr-wrapper' ).each( function () { 130 const $select = $( this ); 131 const attrName = $select.attr( 'name' ); 132 const attrNameFiltered = attrName.replace( /attribute_/, '' ); 133 134 // Build list of allowed options for this attribute given others 135 const allowedValues = new Set(); // use Set to avoid duplicates 136 allowedValues.add( '' ); 137 for ( const variation of variationMap ) { 138 const fits = Object.entries( selectedAttrs ).every( 139 ( [ key, val ] ) => 140 key === attrName || 141 ! val || 142 ! variation.attributes[ 143 key.replace( /attribute_/, '' ) 144 ] || 145 variation.attributes[ 146 key.replace( /^attribute_/, '' ) 147 ] === val 148 ); 149 if ( fits ) { 150 if ( variation.attributes[ attrNameFiltered ] ) { 151 allowedValues.add( 152 variation.attributes[ attrNameFiltered ] 153 ); 154 } else { 155 const options = JSON.parse( 156 $( this ).attr( 'data-options' ) || '[]' 157 ); 158 options?.forEach( ( option ) => 159 allowedValues.add( option ) 160 ); 161 } 162 } 163 } 164 165 // Update select options (enable/disable) 166 $select.find( 'option' ).each( function () { 167 const $opt = $( this ); 168 169 // keep "choose" option
170 if ( allowedValues.has( $opt.val() ) ) { 171 $opt.prop( 'disabled', false ).show(); 172 } else { 173 $opt.prop( 'disabled', true ).hide(); 174 } 175 } ); 176 } ); 177 178 // Store matched variation + selected attributes 179 $container.attr( 180 'data-selected-value', 181 JSON.stringify( selectedAttrs || {} ) 182 ); 183 $container.attr( 184 'data-matched-variation', 185 JSON.stringify( matchedVariation || {} ) 186 ); 187 // setting variation id for payload usage 188 $container.attr( 189 'data-variation-id', 190 JSON.stringify( matchedVariation?.id ?? 0 ) 191 ); 192 // update image source 193 if ( matchedVariation?.image_url ) { 194 $container 195 .find( 'img' ) 196 .attr( 'src', matchedVariation?.image_url ); 197 } 198 } 199 ); 200 201 // for volume discount only 202 $( '[data-campaign-type="volume_discount"]' ).on( 203 'change', 204 '.revx-product-Attr-wrapper', 205 function () { 206 const $container = $( this ).closest( '[data-product-id]' ); 207 const variationMap = JSON.parse( 208 $container.attr( 'data-variation-map' ) || '[]' 209 ); 210 211 // Collect selected attributes 212 const selectedAttrs = {}; 213 $container.find( '.revx-product-Attr-wrapper' ).each( function () { 214 selectedAttrs[ $( this ).attr( 'name' ) ] = $( this ).val(); 215 } ); 216 217 // Try to find a matching variation (compare with variation.attributes) 218 const matchedVariation = variationMap.find( ( variation ) => { 219 if ( ! variation.attributes ) return false; 220 return Object.entries( selectedAttrs ).every( 221 ( [ key, val ] ) => 222 ! val || 223 ! variation.attributes[ key ] || 224 variation.attributes[ key ] === val 225 ); 226 } ); 227 228 // Store matched variation + selected attributes 229 $container.attr( 230 'data-selected-value', 231 JSON.stringify( selectedAttrs || {} ) 232 ); 233 $container.attr( 234 'data-matched-variation', 235 JSON.stringify( matchedVariation || {} ) 236 ); 237 238 // Update price if we found a match 239 if ( matchedVariation ) { 240 const $offerItemContainer = 241 $container.closest( '[data-offer-item]' ); 242 const $saveAmountContainer = $offerItemContainer.find( 243 '[data-smart-tag="saveBadgeWrapper"]' 244 ); 245 const $oldPriceContainer = $offerItemContainer.find( 246 '.revx-product-old-price' 247 ); 248 const $offeredPriceContainer = $offerItemContainer.find( 249 '.revx-product-offered-price' 250 ); 251 const $radioButton = $container 252 .closest( '[data-campaign-type="volume_discount"]' ) 253 .find( '[data-saved-amount]' ); 254 255 // Check if multiple variation selection is enabled 256 const $volumeContainer = $container.closest( 257 '.revx-volume-discount-item' 258 ); 259 const $allVariationWrappers = $volumeContainer.find( 260 '.revx-volume-attributes [data-variation-map]' 261 ); 262 263 if ( $allVariationWrappers.length > 1 ) { 264 // Multiple variations - calculate total price 265 let totalRegularPrice = 0; 266 let totalOfferedPrice = 0; 267 let totalSavedAmount = 0; 268 269 $allVariationWrappers.each( function () { 270 const currMatchedVariation = JSON.parse( 271 $( this ).attr( 'data-matched-variation' ) ?? '{}' 272 ); 273 274 if ( currMatchedVariation ) { 275 totalRegularPrice += parseFloat( 276 currMatchedVariation.regular_price || 0 277 ); 278 totalOfferedPrice += parseFloat( 279 currMatchedVariation.offered_price || 0 280 ); 281 totalSavedAmount += parseFloat( 282 currMatchedVariation.saved_amount || 0 283 ); 284 } 285 } ); 286 287 $radioButton.data( 'saved-amount', totalSavedAmount ); 288 setSaveCapsuleText.call( 289 $saveAmountContainer, 290 totalSavedAmount 291 ); 292 setPriceText.call( $oldPriceContainer, totalRegularPrice ); 293 setPriceText.call( 294 $offeredPriceContainer, 295 totalOfferedPrice 296 ); 297 } else { 298 // Single variation - use matched variation price 299 $radioButton.data( 300 'saved-amount', 301 matchedVariation.saved_amount 302 ); 303 setSaveCapsuleText.call( 304 $saveAmountContainer, 305 matchedVariation.saved_amount 306 ); 307 setPriceText.call( 308 $oldPriceContainer, 309 matchedVariation.regular_price 310 ); 311 setPriceText.call( 312 $offeredPriceContainer, 313 matchedVariation.offered_price 314 ); 315 } 316 317 // trigger the `handleRadioClick` function of `add-to-cart.js` file with the correct container to update the atc button text . 318 $radioButton.trigger( '.revx-volume-discount-item' ); 319 } 320 321 // === Filter other attributes === 322 $container.find( '.revx-product-Attr-wrapper' ).each( function () { 323 const $select = $( this ); 324 const attrName = $select.attr( 'name' ); 325 326 // Build list of allowed options for this attribute given others 327 const allowedValues = new Set(); // use Set to avoid duplicates 328 allowedValues.add( '' ); 329 for ( const variation of variationMap ) { 330 const fits = Object.entries( selectedAttrs ).every( 331 ( [ key, val ] ) => 332 key === attrName || 333 ! val || 334 ! variation.attributes[ key ] || 335 variation.attributes[ key ] === val 336 ); 337 if ( fits ) { 338 if ( variation.attributes[ attrName ] ) { 339 allowedValues.add( 340 variation.attributes[ attrName ] 341 ); 342 } else { 343 const options = JSON.parse( 344 $( this ).attr( 'data-options' ) || '[]' 345 ); 346 options?.forEach( ( option ) => 347 allowedValues.add( option ) 348 ); 349 } 350 } 351 } 352 353 // Update select options (enable/disable) 354 $select.find( 'option' ).each( function () { 355 const $opt = $( this ); 356 357 // keep "choose" option
358 if ( allowedValues.has( $opt.val() ) ) { 359 $opt.prop( 'disabled', false ).show(); 360 } else { 361 $opt.prop( 'disabled', true ).hide(); 362 } 363 } ); 364 } ); 365 } 366 ); 367 368 // TODO: remove this funciton, handled in campaign-total.js now 369 // can handle bundle and buy x get y 370 function updateBundleTotal( $container ) { 371 let totalRegular = 0; 372 let totalOffer = 0; 373 // data-product-id is common in buy x get y and bungle. 374 $container.find( '[data-product-id]' ).each( function () { 375 const qty = parseInt( $( this ).attr( 'data-product-qty' ) ) || 1; // if quantity is not set, then its atleast 1 376 const regularPrice = 377 parseFloat( $( this ).attr( 'data-regular-price' ) ) || 0; // should always get regular price, still a fallback is set. 378 const offeredPrice = parseFloat( 379 $( this ).attr( 'data-offered-price' ) ?? regularPrice // need to decide if user will get the sale price or the regular price 380 ); 381 382 totalRegular += regularPrice * qty; 383 totalOffer += offeredPrice * qty; 384 } ); 385 const $totalContainer = $container 386 .find( '[data-smart-tag="totalText"]' ) 387 .parent(); 388 const $oldPriceContainer = $totalContainer.find( 389 '.revx-product-old-price' 390 ); 391 const $offeredPriceContainer = $totalContainer.find( 392 '.revx-product-offered-price' 393 ); 394 setPriceText.call( $oldPriceContainer, totalRegular ); 395 setPriceText.call( $offeredPriceContainer, totalOffer ); 396 } 397} ); 398 399/** 400 * Volume discount (template2): highlight the selected option row. 401 * Moved out of inline <script> so campaign markup can be escaped. 402 */ 403jQuery( function ( $ ) { 404 $( document ).on( 'click', '.revx-volume-discount-item', function () { 405 const $radio = $( this ).find( '.revx-radio-wrapper' ); 406 const $attribute = $( this ).find( '.revx-volume-attributes' ); 407 408 // Deactivate every other option. 409 $( '.revx-radio-wrapper, .revx-volume-attributes' ) 410 .removeClass( 'revx-active' ) 411 .addClass( 'revx-inactive' ); 412 413 // Activate the clicked one. 414 $radio 415 .add( $attribute ) 416 .addClass( 'revx-active' ) 417 .removeClass( 'revx-inactive' ); 418 } ); 419} );
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.