PageSourceSearch

https://ipservices.com/wp-content/plugins/wp-easycart/design/theme…/ec-account-subscriptions.js?ver=6_0_1

js ipservices.com collected 2026-09-26 05:41:08 UTC 9,547 bytes, 237 lines download raw bytes

1/**
2 * WP EasyCart: account subscription pages ( 6.0.0 ).
3 *
4 * Loaded after ec-store.js as its own file so stores with a custom copy of ec-store.js still get it.
5 * Replaces ec_show_update_subscription_payment() / ec_show_update_subscription_details() with versions that
6 * keep the legacy toggles for older template copies and, on the 6.0.0 card layout ( .ec_account_subscription_v2 ),
7 * open one panel at a time, scroll it into view below any fixed or sticky site header and focus its heading.
8 * The account page can be loaded by AJAX, so every control uses delegated events.
9 */
10( function( $, window, document ) {
11	'use strict';
12
13	if ( ! $ ) {
14		return;
15	}
16
17	var PANELS = {
18		payment: { panel: '.ec_account_subscription_details_payment_form', trigger: '.ec_account_subscription_details_card_change' },
19		plan: { panel: '.ec_account_subscription_upgrade_row', trigger: '.ec_account_subscription_details_plan_change' }
20	};
21
22	function prefersReducedMotion() {
23		return !! ( window.matchMedia && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches );
24	}
25
26	function isPinned( el ) {
27		var position;
28		try {
29			position = window.getComputedStyle( el ).position;
30		} catch ( e ) {
31			return false;
32		}
33		return 'fixed' === position || 'sticky' === position || '-webkit-sticky' === position;
34	}
35
36	/**
37	 * Height covered at the top of the viewport by fixed / sticky elements ( site headers, WP admin bar ).
38	 * Samples points across the top edge and walks up from each hit to a pinned ancestor, then repeats
39	 * below what it found so stacked bars ( announcement bar + nav ) add up.
40	 */
41	function topOffset() {
42		var offset = 0;
43		var viewportWidth = window.innerWidth || document.documentElement.clientWidth;
44		var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
45		var adminBar = document.getElementById( 'wpadminbar' );
46		if ( adminBar && isPinned( adminBar ) ) {
47			var barRect = adminBar.getBoundingClientRect();
48			if ( barRect.bottom > 0 && barRect.height > 0 ) {
49				offset = Math.max( offset, barRect.bottom );
50			}
51		}
52		if ( ! document.elementFromPoint ) {
53			return offset;
54		}
55		var xs = [ 12, Math.round( viewportWidth / 2 ), viewportWidth - 12 ];
56		for ( var pass = 0; pass < 4; pass++ ) {
57			var found = offset;
58			var y = Math.min( offset + 1, viewportHeight - 1 );
59			for ( var i = 0; i < xs.length; i++ ) {
60				var el = document.elementFromPoint( xs[ i ], y );
61				while ( el && el !== document.body && el !== document.documentElement ) {
62					if ( isPinned( el ) ) {
63						var rect = el.getBoundingClientRect();
64						/* Ignore full-screen overlays and anything not actually touching the top area. */
65						if ( rect.height > 0 && rect.height < viewportHeight * 0.5 && rect.top <= y + 1 && rect.bottom > found ) {
66							found = rect.bottom;
67						}
68						break;
69					}
70					el = el.parentElement;
71				}
72			}
73			if ( found <= offset ) {
74				break;
75			}
76			offset = found;
77		}
78		return Math.max( 0, Math.round( offset ) );
79	}
80
81	/** Scroll only when the panel is not already comfortably on screen; then focus its heading. */
82	function revealPanel( panel ) {
83		if ( ! panel || ! panel.getBoundingClientRect ) {
84			return;
85		}
86		var gap = 16;
87		var offset = topOffset();
88		var rect = panel.getBoundingClientRect();
89		var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
90		var available = viewportHeight - offset;
91		var fullyVisible = rect.top >= offset && rect.bottom <= viewportHeight;
92		var tallButTopVisible = rect.height > available && rect.top >= offset && rect.top <= offset + available / 3;
93		if ( ! fullyVisible && ! tallButTopVisible ) {
94			var target = Math.max( 0, ( window.pageYOffset || document.documentElement.scrollTop ) + rect.top - offset - gap );
95			try {
96				window.scrollTo( { top: target, behavior: prefersReducedMotion() ? 'auto' : 'smooth' } );
97			} catch ( e ) {
98				window.scrollTo( 0, target );
99			}
100		}
101		var heading = panel.querySelector( '.ec_account_subscription_v2_panel_title' );
102		if ( heading ) {
103			try {
104				heading.focus( { preventScroll: true } );
105			} catch ( e ) {
106				heading.focus();
107			}
108		}
109	}
110
111	function openPanel( key ) {
112		var other = 'payment' === key ? 'plan' : 'payment';
113		var $panel = $( PANELS[ key ].panel );
114		if ( ! $panel.closest( '.ec_account_subscription_v2' ).length ) {
115			/* Older template copies: the toggles as they always were. */
116			$panel.show();
117			$( PANELS[ key ].trigger ).hide();
118			$( PANELS[ other ].panel ).hide();
119			$( PANELS[ other ].trigger ).show();
120			return false;
121		}
122		$( PANELS[ other ].panel ).hide();
123		$( PANELS[ other ].trigger ).attr( 'aria-expanded', 'false' ).removeClass( 'is-open' );
124		$panel.show();
125		$( PANELS[ key ].trigger ).attr( 'aria-expanded', 'true' ).addClass( 'is-open' );
126		if ( 'plan' === key ) {
127			$panel.each( function() {
128				planState( this );
129			} );
130		}
131		revealPanel( $panel.get( 0 ) );
132		return false;
133	}
134
135	function closePanel( key ) {
136		var $panel = $( PANELS[ key ].panel );
137		$panel.hide();
138		var $trigger = $( PANELS[ key ].trigger ).attr( 'aria-expanded', 'false' ).removeClass( 'is-open' ).show();
139		if ( $trigger.length ) {
140			try {
141				$trigger.get( 0 ).focus( { preventScroll: true } );
142			} catch ( e ) {
143				$trigger.get( 0 ).focus();
144			}
145		}
146		return false;
147	}
148
149	/** Enable Save only when the plan or quantity changed and the quantity is valid. */
150	function planState( panel ) {
151		var $panel = $( panel );
152		var currentPlan = String( $panel.attr( 'data-current-plan' ) || '' );
153		var currentQuantity = parseInt( $panel.attr( 'data-current-quantity' ), 10 ) || 1;
154		var plan = String( $panel.find( 'input[name="ec_selected_plan"]' ).val() || currentPlan );
155		var $quantity = $panel.find( 'input.ec_account_subscription_v2_qty' );
156		var quantity = currentQuantity;
157		var valid = true;
158		if ( $quantity.length ) {
159			var raw = $.trim( String( $quantity.val() ) );
160			quantity = parseInt( raw, 10 );
161			valid = /^\d+$/.test( raw ) && quantity >= 1 && quantity <= 1000000;
162			$panel.find( '.ec_account_subscription_v2_qty_error' ).prop( 'hidden', valid );
163			$quantity.attr( 'aria-invalid', valid ? 'false' : 'true' );
164			$panel.find( '[data-ec-sub-step="-1"]' ).prop( 'disabled', valid && quantity <= 1 );
165		}
166		var changed = plan !== currentPlan || ( valid && quantity !== currentQuantity );
167		$panel.find( '[data-ec-sub-save="plan"]' ).prop( 'disabled', ! ( changed && valid ) );
168		return changed && valid;
169	}
170
171	window.ec_show_update_subscription_payment = function() {
172		return openPanel( 'payment' );
173	};
174
175	window.ec_show_update_subscription_details = function() {
176		return openPanel( 'plan' );
177	};
178
179	window.ec_account_subscription_close_panel = function( key ) {
180		return closePanel( 'plan' === key ? 'plan' : 'payment' );
181	};
182
183	/** Plan Save: validate, then hand over to ec_update_subscription_info() ( ec-store.js ). */
184	window.ec_account_subscription_save_plan = function( button, subscription_id, nonce ) {
185		var panel = $( button ).closest( '.ec_account_subscription_upgrade_row' ).get( 0 );
186		if ( panel && ! planState( panel ) ) {
187			return false;
188		}
189		$( button ).prop( 'disabled', true );
190		return window.ec_update_subscription_info( subscription_id, nonce );
191	};
192
193	$( document ).on( 'change', '.ec_account_subscription_v2_plans input[type="radio"]', function() {
194		var $panel = $( this ).closest( '.ec_account_subscription_upgrade_row' );
195		$panel.find( 'input[name="ec_selected_plan"]' ).val( this.value );
196		$panel.find( '.ec_account_subscription_v2_plan' ).removeClass( 'is-selected' );
197		$( this ).closest( '.ec_account_subscription_v2_plan' ).addClass( 'is-selected' );
198		planState( $panel.get( 0 ) );
199	} );
200
201	$( document ).on( 'click', '[data-ec-sub-step]', function( event ) {
202		event.preventDefault();
203		var $panel = $( this ).closest( '.ec_account_subscription_upgrade_row' );
204		var $quantity = $panel.find( 'input.ec_account_subscription_v2_qty' );
205		var value = parseInt( $quantity.val(), 10 );
206		value = ( isNaN( value ) ? 1 : value ) + parseInt( $( this ).attr( 'data-ec-sub-step' ), 10 );
207		value = Math.min( 1000000, Math.max( 1, value ) );
208		$quantity.val( value );
209		planState( $panel.get( 0 ) );
210	} );
211
212	$( document ).on( 'input change', '.ec_account_subscription_v2 input.ec_account_subscription_v2_qty', function() {
213		planState( $( this ).closest( '.ec_account_subscription_upgrade_row' ).get( 0 ) );
214	} );
215
216	/* Enter in the plan panel would submit the shared form into the card update; run Save instead. */
217	$( document ).on( 'keydown', '.ec_account_subscription_v2 .ec_account_subscription_upgrade_row input', function( event ) {
218		if ( 13 === event.which ) {
219			event.preventDefault();
220			var $save = $( this ).closest( '.ec_account_subscription_upgrade_row' ).find( '[data-ec-sub-save="plan"]' );
221			if ( $save.length && ! $save.prop( 'disabled' ) ) {
222				$save.trigger( 'click' );
223			}
224		}
225	} );
226
227	$( document ).on( 'click', '[data-ec-sub-close]', function( event ) {
228		event.preventDefault();
229		closePanel( 'plan' === $( this ).attr( 'data-ec-sub-close' ) ? 'plan' : 'payment' );
230	} );
231
232	$( document ).on( 'keydown', '.ec_account_subscription_v2 .ec_account_subscription_v2_panel', function( event ) {
233		if ( 27 === event.which ) {
234			closePanel( $( this ).is( PANELS.plan.panel ) ? 'plan' : 'payment' );
235		}
236	} );
237} )( window.jQuery, window, document );

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.