1/* 2 * JavaScript for front-end discount code display 3 * 4 */ 5jQuery(document).ready(function($) { 6 7 // Cache the value of the last checked discount code 8 var last_checked_discount_code; 9 10 /* 11 * Show / Hide discount code field if a free plan is selected 12 * 13 */ 14 toggle_discount_box( $('input[name=subscription_plans][type=radio]').length > 0 ? $('input[name=subscription_plans][type=radio]:checked') : $('input[name=subscription_plans][type=hidden]') ) 15 16 /** 17 * Trigger automatically "Apply" discount button when the user already entered a discount code and selects another subscription plan, or checks the "Automatically renew subscription" checkbox. 18 * This will update the discount message shown below the field. 19 * 20 */ 21 $(document).on('click', '.pms-subscription-plan input[type="radio"][name="subscription_plans"]', function(){ 22 23 // If subscription is not free and discount code field is not empty 24 if (($(this).attr("data-price") > 0) && ($('#pms_subscription_plans_discount_code').length > 0)) { 25 26 $('#pms-apply-discount').trigger('click') 27 28 } else { 29 30 $('#pms-subscription-plans-discount-messages-wrapper').hide() 31 $('#pms-subscription-plans-discount-messages').hide() 32 33 } 34 35 toggle_discount_box( $(this) ) 36 37 }) 38 39 $(document).on('click', '.pms-subscription-plan-auto-renew input[type="checkbox"][name="pms_recurring"]', function () { 40 41 // If discount code field is not empty 42 if ( $('#pms_subscription_plans_discount_code').length > 0 ){ 43 44 $('#pms-apply-discount').trigger('click') 45 46 } else { 47 48 $('#pms-subscription-plans-discount-messages-wrapper').hide() 49 $('#pms-subscription-plans-discount-messages').hide() 50 51 } 52 53 toggle_discount_box( $($pms_checked_subscription) ) 54 55 }) 56 57 /** 58 * Toggle discount code box when the subscription plans field is shown from the 59 * Profile Builder Conditional Logic functionality 60 */ 61 $(document).on("wppbAddRequiredAttributeEvent", function (e) { 62 if ($(e.target).is('#pms_subscription_plans_discount_code')) 63 toggle_discount_box($('input[name=subscription_plans][type=radio]').length > 0 ? $('input[name=subscription_plans][type=radio]:checked') : $('input[name=subscription_plans][type=hidden]')) 64 }) 65 66 /** 67 * Handles discount code validation when the user clicks the "Apply" discount button 68 * 69 */ 70 $(document).on('click', '#pms-apply-discount', function(e){ 71 72 e.preventDefault(); 73 74 // If undefined, cache the parent form 75 if( typeof $pms_form == 'undefined' ) 76 $pms_form = $(this).closest('form'); 77 78 var $subscription_plan = ''; 79 80 $('.pms-subscription-plan input[type="radio"]').each(function(){ 81 if($(this).is(':checked')){ 82 $subscription_plan = $(this); 83 } 84 }); 85 86 if( $subscription_plan == '' ) { 87 $subscription_plan = $('input[type=hidden][name=subscription_plans]'); 88 } 89 90 if( $('#pms_subscription_plans_discount_code').val() == '' ) { 91 $('#pms-subscription-plans-discount-messages-wrapper').fadeOut( 350 ); 92 93 // empty the message HTML alongside the fadeOut so the previous success/error text does not linger in the DOM after a clear 94 $('#pms-subscription-plans-discount-messages').fadeOut( 350 ).empty().removeClass( 'pms-discount-success pms-discount-error' ) 95 96 reset_discounted_plan_data( $subscription_plan ) 97 jQuery(document).trigger( 'pms_discount_error', [ null, $subscription_plan, $pms_form ] ) 98 99 return false; 100 } 101 102 // Cache the discount code 103 last_checked_discount_code = $('#pms_subscription_plans_discount_code').val(); 104 105 pwyw_price = ''; 106 107 if ( $('input[name="subscription_price_'+$subscription_plan.val()+'"]').length != 0 ) 108 pwyw_price = $('input[name="subscription_price_'+$subscription_plan.val()+'"]').val(); 109 110 var data = { 111 'action' : 'pms_discount_code', 112 'code' : $.trim( $('#pms_subscription_plans_discount_code').val()), 113 'subscription' : $subscription_plan.val(), 114 'recurring' : $('input[name="pms_recurring"]:checked').val(), 115 'pwyw_price' : pwyw_price, 116 'pmstkn_original' : $pms_form.find('input[name="pmstkn"]').val(), 117 'pms_current_subscription': $pms_form.find('input[name="pms_current_subscription"]').val(), 118 'form_action' : $pms_form.find('input[name="form_action"]').val(), 119 }; 120 121 // addons that extend checkout with additional purchasable items can hook this filter to inject fields into the AJAX request (e.g. Order Bumps appends pms_order_bumps[] plus form-submit field name mirrors so the server-side bundle pipeline reads the same composition) 122 var augmentedData = $(document).triggerHandler( 'pms_filter_discount_ajax_payload', [ data, $pms_form ] ); 123 124 if( augmentedData && typeof augmentedData === 'object' ) 125 data = augmentedData; 126 127 var currency = new URLSearchParams(window.location.search).get('pms_mc_currency'); 128 129 if ( currency ) 130 data['pms_mc_currency'] = currency; 131 132 if( data.pmstkn_original === undefined && jQuery( '.wppb-register-user' ).length > 0 ) 133 data.pmstkn_original = 'pb_form' 134 135 // Make sure it's not an empty discount 136 if ( data['code'] !== '' ) { 137 138 $('#pms-subscription-plans-discount-messages').hide() 139 $('#pms-subscription-plans-discount-messages-wrapper').show() 140 $('#pms-subscription-plans-discount-messages-loading').fadeIn(350) 141 142 // let add-ons attach extra context to the request before it is sent 143 // - the data object is passed by reference, so handlers mutate it in place (e.g. the Group add-on adds the selected plan i
143d and seat count so per-seat plans are priced server-side) 144 jQuery( document ).trigger( 'pms_discount_code_request_data', [ data ] ) 145 146 // We can also pass the url value separately from ajaxurl for front end AJAX implementations 147 jQuery.post(pms_discount_object.ajax_url, data, function (response) { 148 149 if (response.success != undefined) { 150 151 // Add success message 152 $('#pms-subscription-plans-discount-messages').removeClass('pms-discount-error') 153 $('#pms-subscription-plans-discount-messages').addClass('pms-discount-success') 154 155 $('#pms-subscription-plans-discount-messages-loading').fadeOut(350, function () { 156 $('#pms-subscription-plans-discount-messages').html(response.success.message).fadeIn(350) 157 }) 158 159 // Hide payment fields 160 if (response.is_full_discount) 161 $.pms_hide_payment_fields( $pms_form ) 162 else 163 $.pms_show_payment_fields( $pms_form ) 164 165 // Cache the initial values only once so they can be restored after the discount is removed 166 if ( !$subscription_plan.data('discounted-price') ) 167 $subscription_plan.data( 'price-original', $subscription_plan.data('price') ) 168 169 // Cache the initial MC amount only once so it can be restored after the discount is removed 170 if ( !$subscription_plan.data('discounted-price') && typeof $subscription_plan.data('mc_price') != 'undefined' ) 171 $subscription_plan.data( 'mc-price-original', $subscription_plan.data('mc_price') ) 172 173 // Keep the MC amount in sync so Tax and gateway JS use the discounted value too 174 if ( typeof $subscription_plan.data('mc_price') != 'undefined' ) 175 $subscription_plan.data( 'mc_price', response.discounted_price ) 176 177 $subscription_plan.data( 'price', response.discounted_price ) 178 $subscription_plan.data( 'discounted-price', true ) 179 $subscription_plan.data( 'discounted-price-value', response.original_discounted_price ) 180 181 if( response.is_full_discount == true ){ 182 183 if( response.recurring_payments == 1 ){ 184 $( 'input[name="pms_recurring"]', $pms_auto_renew_field ).prop( 'checked', true ) 185 $pms_auto_renew_field.hide() 186 } 187 188 $subscription_plan.data( 'is-full-discount', true ) 189 190 } else 191 $subscription_plan.data( 'is-full-discount', false ) 192 193 $subscription_plan.data( 'discount-recurring-payments', response.recurring_payments ) 194 195 // listeners receive the response, the primary input and the form so addons that augment the AJAX response (e.g. Order Bumps' bundle_items per-item breakdown) can apply their own data-attribute updates before downstream consumers (order summary, gateways) react 196 jQuery(document).trigger( 'pms_discount_success', [ response, $subscription_plan, $pms_form ] ) 197 198 } 199 200 if (response.error != undefined) { 201 202 // Add error message 203 $('#pms-subscription-plans-discount-messages').removeClass('pms-discount-success') 204 $('#pms-subscription-plans-discount-messages').addClass('pms-discount-error') 205 206 $('#pms-subscription-plans-discount-messages-loading').fadeOut(350, function () { 207 $('#pms-subscription-plans-discount-messages').html(response.error.message).fadeIn(350) 208 }) 209 210 // Show payment fields 211 $.pms_show_payment_fields( $pms_form ) 212 213 reset_discounted_plan_data( $subscription_plan ) 214 215 jQuery(document).trigger( 'pms_discount_error', [ response, $subscription_plan, $pms_form ] ) 216 217 } 218 219 }); 220 } else { 221 222 reset_discounted_plan_data( $subscription_plan ) 223 224 jQuery(document).trigger( 'pms_discount_error' ) 225 226 } 227 228 }) 229 230 /** 231 * If there is a discount code value already set on document ready 232 * apply it 233 * 234 */ 235 if( $('input[name=discount_code]').val() != '' ) 236 $('#pms-apply-discount').trigger('click') 237 238 /** 239 * When losing focus of the discount code field, directly apply the discount 240 * 241 */ 242 $('input[name=discount_code]').on( 'blur', function() { 243 244 if( last_checked_discount_code != $('input[name=discount_code]').val() ) 245 $('#pms-apply-discount').trigger('click'); 246 247 if ( $('input[name=discount_code]').val() == '' ) 248 $.pms_show_payment_fields( $pms_form ); 249 }) 250 251 /* 252 * Show / Hide discount code field if a free plan is selected 253 * 254 */ 255 function toggle_discount_box( $element ) { 256 257 if( !$element ) 258 return 259 260 var selector = '#pms-subscription-plans-discount'; 261 262 if( !subscription_has_discount( $element.val() ) ) 263 $(selector).hide() 264 else { 265 if ( $element.attr('data-price') == '0' ) { 266 267 if ( $.isFunction( $.pms_plan_is_prorated ) && $.pms_plan_is_prorated( $element ) ) { 268 269 if ( $('input[name="pms_recurring"]', $('.pms-subscription-plan-auto-renew') ).prop('checked') || $element.data('recurring') == 2 ){ 270 $(selector).show() 271 return 272 } 273 274 } 275 276 if ( $.isFunction( $.pms_plan_has_signup_fee ) && $.pms_plan_has_signup_fee( $element ) ) 277 $(selector).show() 278 279 $(selector).hide() 280 281 } else { 282 $(selector).show() 283 } 284 } 285 286 } 287 288 function subscription_has_discount( subscription_id ){ 289 290 // show the box if we don't have data available (old default) 291 if( typeof pms_discount_object == 'undefined' || typeof pms_discount_object.discounted_subscriptions == 'undefined' ) 292 return true 293 294 let return_value = false 295 let subscriptions = JSON.parse( pms_discount_object.discounted_subscriptions ) 296 297 for ( var subscription in subscriptions ){ 298 if( subscription_id == subscriptions[subscription] ) 299 return_value = true 300 } 301 302 // bundle-aware addons (e.g. Order Bumps) can override the decision so the discount box stays visible when a selected bump has a targeting discount even if the primary plan does not 303 let filtered = $( document ).triggerHandler( 'pms_filter_subscription_has_discount', [ return_value, subscription_id ] ) 304 305 return typeof filtered === 'boolean' ? filtered : return_value 306 307 } 308 309 310 // addons can request a fresh visibility check when their state changes (e.g. Order Bumps toggling a bump that has a targeting discount); re-run toggle_discount_box against the currently selected primary plan 311 $( document ).on( 'pms_recheck_discount_box_visibility', function() { 312 toggle_discount_box( $('input[name=subscription_plans][type=radio]').length > 0 ? $('input[name=subscription_plans][type=radio]:checked') : $('input[name=subscription_plans][type=hidden]') ) 313 } ) 314 315 /** 316 * Restore the initial plan values after a discount is removed or becomes invalid 317 * - used by the error and reset paths after the discount code is applied 318 */ 319 function reset_discounted_plan_data( $subscription_plan ) { 320 321 if ( typeof $subscription_plan.data('price-original') != 'undefined' ) 322 $subscription_plan.data( 'price', $subscription_plan.data('price-original') ) 323 324 if ( typeof $subscription_plan.data('mc-price-original') != 'undefined' ) 325 $subscription_plan.data( 'mc_price', $subscription_plan.data('mc-price-original') ) 326 327 $subscription_plan.data( 'discounted-price', false ) 328 $subscription_plan.data( 'discounted-price-value', 0 ) 329 $subscription_plan.data( 'discount-recurring-payments', 0 ) 330 $subscription_plan.data( 'is-full-discount', false ) 331 332 } 333 334});
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.