PageSourceSearch

https://www.pressharbor.com/wp-content/themes/pressharbor-theme/assets/js/main.js?ver=1790910596

js pressharbor.com collected 2026-10-09 04:38:26 UTC 5,739 bytes, 173 lines download raw bytes

1/**
2 * PressHarbor — Main JS
3 * Sticky nav + FAQ accordion + dock URL animation
4 */
5
6// Sticky nav background on scroll
7( function () {
8	var nav = document.getElementById( 'nav' );
9	if ( ! nav ) return;
10	window.addEventListener( 'scroll', function () {
11		nav.classList.toggle( 'scrolled', window.scrollY > 60 );
12	} );
13} )();
14
15// Mobile nav toggle
16( function () {
17	var toggle = document.querySelector( '.nav-toggle' );
18	if ( ! toggle ) return;
19	toggle.addEventListener( 'click', function () {
20		var links = document.querySelector( '.nav-links' );
21		var open  = links.classList.toggle( 'open' );
22		toggle.setAttribute( 'aria-expanded', open ? 'true' : 'false' );
23	} );
24} )();
25
26// Capacity calculator (hero diagram)
27( function () {
28	var calc = document.getElementById( 'hd-calculator' );
29	if ( ! calc ) return;
30
31	// Bundled plans — workers and storage move together
32	var plans = typeof pressharborPlanData !== 'undefined' ? pressharborPlanData.plans : [];
33	if (!plans.length) return;
34
35	var pos = 0; // default: Core
36
37	var burstCheck = document.getElementById( 'hd-burst-check' );
38	var ctaEl      = document.getElementById( 'hd-price-cta' );
39	var contactUrl = 'https://manage.pressharbor.com/submitticket.php?step=2&deptid=1';
40
41	function fmt( n ) { return n.toFixed( 2 ) + '%'; }
42
43	function update() {
44		var plan  = plans[ pos ];
45		var burst = burstCheck && burstCheck.checked;
46
47		// Workers track
48		document.getElementById( 'hd-workers-display' ).textContent = plan.workers;
49		document.getElementById( 'hd-workers-fill' ).style.setProperty( '--fill', fmt( plan.pos ) );
50		calc.querySelectorAll( '[data-track="workers"]' ).forEach( function ( dot, i ) {
51			dot.classList.toggle( 'hd-dot--active', i === pos );
52		} );
53		document.getElementById( 'hd-workers-labels' ).querySelectorAll( 'span' ).forEach( function ( s, i ) {
54			s.classList.toggle( 'active', i === pos );
55		} );
56
57		// Storage track — mirrors workers
58		document.getElementById( 'hd-storage-display' ).textContent = plan.storage;
59		document.getElementById( 'hd-storage-fill' ).style.setProperty( '--fill', fmt( plan.pos ) );
60		calc.querySelectorAll( '[data-track="storage"]' ).forEach( function ( dot, i ) {
61			dot.classList.toggle( 'hd-dot--active', i === pos );
62		} );
63		document.getElementById( 'hd-storage-labels' ).querySelectorAll( 'span' ).forEach( function ( s, i ) {
64			s.classList.toggle( 'active', i === pos );
65		} );
66
67		// Price display
68		document.getElementById( 'hd-plan-name' ).textContent = plan.name + ( burst ? ' + Burst' : '' );
69
70		var footnote = document.getElementById( 'hd-price-footnote' );
71
72		if ( burst ) {
73			document.getElementById( 'hd-price-amount' ).textContent = 'Custom';
74			document.getElementById( 'hd-price-period' ).textContent = '';
75			if ( ctaEl ) { ctaEl.textContent = 'Let\u2019s Talk'; ctaEl.href = contactUrl; }
76			if ( footnote ) footnote.style.display = 'none';
77		} else {
78			document.getElementById( 'hd-price-amount' ).textContent = '$' + plan.price;
79			document.getElementById( 'hd-price-period' ).textContent = '/mo';
80			if ( ctaEl ) { ctaEl.textContent = plan.ctaLabel; ctaEl.href = plan.ctaUrl; }
81			if ( footnote ) footnote.style.display = '';
82		}
83	}
84
85	calc.addEventListener( 'click', function ( e ) {
86		var dot = e.target.closest( '[data-track]' );
87		if ( ! dot ) return;
88		pos = parseInt( dot.dataset.step, 10 );
89		update();
90	} );
91
92	if ( burstCheck ) {
93		burstCheck.addEventListener( 'change', update );
94	}
95} )();
96
97// Billing toggle (annual / monthly)
98( function () {
99	var toggle = document.getElementById( 'billing-toggle' );
100	if ( ! toggle ) return;
101	toggle.addEventListener( 'click', function ( e ) {
102		var btn = e.target.closest( '.billing-btn' );
103		if ( ! btn ) return;
104		var period = btn.dataset.period;
105		toggle.querySelectorAll( '.billing-btn' ).forEach( function ( b ) {
106			b.setAttribute( 'aria-pressed', b === btn ? 'true' : 'false' );
107		} );
108		document.querySelectorAll( '.plan-amount[data-annual]' ).forEach( function ( el ) {
109			el.textContent = el.dataset[ period ];
110		} );
111	} );
112} )();
113
114// FAQ accordion
115document.querySelectorAll( '.faq-question' ).forEach( function ( btn ) {
116	btn.addEventListener( 'click', function () {
117		var item   = btn.parentElement;
118		var isOpen = item.classList.contains( 'open' );
119		document.querySelectorAll( '.faq-item' ).forEach( function ( i ) {
120			i.classList.remove( 'open' );
121		} );
122		if ( ! isOpen ) item.classList.add( 'open' );
123	} );
124} );
125
126// Dock URL animation — cycles random IDs then settles
127( function () {
128	var chars   = 'abcdefghijklmnopqrstuvwxyz0123456789';
129	var dockId  = document.getElementById( 'dock-id' );
130	var spinner = document.querySelector( '.dock-spinner' );
131	if ( ! dockId ) return;
132	var started = false;
133
134	function randomId( len ) {
135		var s = '';
136		for ( var i = 0; i < len; i++ ) {
137			s += chars[ Math.floor( Math.random() * chars.length ) ];
138		}
139		return s;
140	}
141
142	function runAnimation() {
143		if ( started ) return;
144		started = true;
145		dockId.classList.add( 'cycling' );
146		var cycles = 0;
147		var total  = 18;
148		var interval = setInterval( function () {
149			dockId.textContent = randomId( 5 );
150			cycles++;
151			if ( cycles >= total ) {
152				clearInterval( interval );
153				dockId.textContent = randomId( 5 );
154				dockId.classList.remove( 'cycling' );
155				if ( spinner ) spinner.classList.add( 'done' );
156			}
157		}, 70 );
158	}
159
160	var evalSection = document.getElementById( 'dock' ) || document.getElementById( 'evaluation' );
161	if ( evalSection && 'IntersectionObserver' in window ) {
162		var obs = new IntersectionObserver( function ( entries ) {
163			if ( entries[0].isIntersecting ) {
164				setTimeout( runAnimation, 400 );
165				obs.disconnect();
166			}
167		}, { threshold: 0.3 } );
168		obs.observe( evalSection );
169	} else {
170		setTimeout( runAnimation, 2000 );
171	}
172} )();
173

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.