PageSourceSearch

https://artsg.com/wp-content/themes/artsg-2027/js/main.js?ver=1789370573

js artsg.com collected 2026-10-02 05:00:00 UTC 9,561 bytes, 254 lines download raw bytes

1jQuery.noConflict();
2
3(function($) {
4  $(function() {
5    // GF phone fields: drop the prefilled placeholder
6    $('.ginput_container_phone input').removeAttr('placeholder');
7
8    // login form error text
9    if ($('.login-form .wppb-error, .portal__login .wppb-error').length > 0) {
10      $('.login-form .wppb-error, .portal__login .wppb-error').html('<strong>ERROR</strong>: Invalid Code.');
11    }
12
13    // GravityView edit mode: pull the page's own form widget
14    const params = new URLSearchParams(window.location.search);
15    if (params.get('gvid') && params.get('edit')) {
16      $('.portal .elementor-widget-wp-widget-gform_widget .gform_wrapper').remove();
17    }
18
19    // hide an empty GravityView table
20    const $gvTable = $(".gv-table-view");
21    if ($gvTable.length > 0 && $gvTable.find('.gv-no-results').length > 0) {
22      $gvTable.hide();
23    }
24
25    // portal sidebar: drawer below 768px, sub-menus toggle in place
26    const $portalNav = $('[data-portal-nav]');
27    if ($portalNav.length) {
28      const $pToggle = $portalNav.find('.portal-nav__toggle');
29      const $pClose  = $portalNav.find('.portal-nav__close');
30
31      function setPortalNav(open) {
32        $portalNav.toggleClass('portal-nav--open', open);
33        $('body').toggleClass('portal-nav-open', open);
34        $pToggle.attr('aria-expanded', open ? 'true' : 'false');
35        (open ? $pClose : $pToggle).trigger('focus');
36      }
37
38      $pToggle.on('click', function() { setPortalNav(true); });
39      $pClose.add($portalNav.find('.portal-nav__scrim')).on('click', function() { setPortalNav(false); });
40      $(document).on('keydown', function(e) {
41        if (e.key === 'Escape' && $portalNav.hasClass('portal-nav--open')) {
42          setPortalNav(false);
43        }
44      });
45
46      // current page's branch opens on load, the rest start closed
47      $portalNav.find('.portal-nav__menu .menu-item-has-children').each(function() {
48        const $li  = $(this);
49        const open = $li.is('.current-menu-item, .current-menu-parent, .current-menu-ancestor, .current_page_ancestor');
50        $li.toggleClass('is-open', open);
51        $('<button type="button" class="portal-nav__submenu-toggle" aria-label="Show sub-pages"></button>')
52          .attr('aria-expanded', open ? 'true' : 'false')
53          .insertAfter($li.children('a').first());
54      });
55
56      $portalNav.on('click', '.portal-nav__submenu-toggle', function() {
57        const $li  = $(this).closest('li');
58        const open = !$li.hasClass('is-open');
59        $li.toggleClass('is-open', open);
60        $(this).attr('aria-expanded', open ? 'true' : 'false');
61      });
62    }
63
64    // required field legend
65    const requiredLegend = `
66      <div class="gform_require_sign">
67        <p class="gform_required_legend">
68          "<span class="gfield_required gfield_required_asterisk">*</span>" indicates required fields
69        </p>
70      </div>
71    `;
72    $(requiredLegend).insertAfter("#gform_wrapper_67 .gf_page_steps, #gform_wrapper_247 .gf_page_steps");
73    
74    const $navToggle = $('.site-header__toggle');
75    const $navClose  = $('.primary-navbar__close');
76
77    // aria-expanded tracks whether #primary-navbar is open
78    function setNavOpen(open) {
79      $('body').toggleClass('nav-open-overlay', open);
80      $navToggle.attr('aria-expanded', open ? 'true' : 'false');
81      // focus follows the panel or the next Tab lands at the top of the page
82      (open ? $navClose : $navToggle).trigger('focus');
83    }
84
85    $navToggle.on('click', function() {
86      setNavOpen(true);
87    });
88
89    // bound to the button not the inner span, keyboard clicks do not reach descendants
90    $navClose.on('click', function() {
91      setNavOpen(false);
92    });
93
94    $(document).on('keydown', function(e) {
95      if (e.key === 'Escape' && $('body').hasClass('nav-open-overlay')) {
96        setNavOpen(false);
97      }
98    });
99    
100    $(document).on("click", ".primary-navbar__list li.menu-item-has-children > a", function (e) {
101      const link = $(this);
102      const submenu = link.next(".sub-menu");
103
104      const linkWidth = link.outerWidth();
105      const iconZoneWidth = 30; // clickable area width (px)
106      const clickX = e.offsetX;
107
108      // clicks in the icon zone at the right edge toggle the submenu
109      if (clickX > linkWidth - iconZoneWidth) {
110        e.preventDefault();
111
112        if (submenu.is(":visible")) {
113          link.removeClass("open");
114          submenu.slideUp(200);
115        } else {
116          $(".primary-navbar__list li.menu-item-has-children > a").removeClass("open");
117          $(".primary-navbar__list .sub-menu").slideUp(200);
118
119          link.addClass("open");
120          submenu.slideDown(200);
121        }
122      }
123    });  
124    
125    // feature list reveal
126
127    const $features = $('.feature-list');
128
129    if ($features.length && 'IntersectionObserver' in window) {
130        $features.addClass('js-reveal');
131
132        const reveal = new IntersectionObserver(function (entries) {
133            entries.forEach(function (entry) {
134                if (entry.isIntersecting) {
135                    entry.target.classList.add('is-inview');
136                    reveal.unobserve(entry.target);
137                }
138            });
139        }, { threshold: 0.2, rootMargin: '0px 0px -10% 0px' });
140
141        $features.find('.feature-list__item').each(function () {
142            reveal.observe(this);
143        });
144    }
145
146    // archive filter: a real <select> that submits on change
147
148    $(document).on('change', 'select[data-autosubmit]', function () {
149        this.form.submit();
150    });
151// empty selects stay out of the URL
152$(document).on('submit', 'form:has(select[data-autosubmit])', function () {
153    $(this).find('select').each(function () { if (this.value === '') { this.disabled = true; } });
154});
155
156    // sticky header
157
158    const $header = $('.site-header');
159
160    // two thresholds so the colour swap does not flicker around one line
161    let sticky = false;
162    function checkSticky() {
163        const y = $(window).scrollTop();
164        if (!sticky && y > 24) { sticky = true; }
165        else if (sticky && y < 4) { sticky = false; }
166        $header.toggleClass('is-sticky', sticky);
167        // also on body, the fly-out panel is a sibling and reads its vars from there
168        $('body').toggleClass('is-header-sticky', sticky);
169    }
170
171    checkSticky();
172    $(window).on('scroll load resize', checkSticky);
173
174  });
175})(jQuery);
176// section nav: current tab scrolled into view, the edge with more tabs fades
177document.querySelectorAll('[data-section-nav]').forEach(function (scroller) {
178	var current = scroller.querySelector('.is-current');
179	var update  = function () {
180		scroller.classList.toggle('is-overflowing', scroller.scrollWidth > scroller.clientWidth + 1);
181		scroller.classList.toggle('is-end', scroller.scrollLeft + scroller.clientWidth >= scroller.scrollWidth - 2);
182	};
183	if (current && scroller.scrollWidth > scroller.clientWidth) {
184		scroller.scrollLeft = current.offsetLeft - (scroller.clientWidth - current.offsetWidth) / 2;
185	}
186	update();
187	scroller.addEventListener('scroll', update, { passive: true });
188	window.addEventListener('resize', update);
189});
190
191// carousel: the track is a scroll-snap strip, buttons scroll it one slide
192document.querySelectorAll('[data-carousel]').forEach(function (carousel) {
193	var track   = carousel.querySelector('.carousel__track');
194	var current = carousel.querySelector('[data-carousel-current]');
195	var prev    = carousel.querySelector('[data-carousel-prev]');
196	var next    = carousel.querySelector('[data-carousel-next]');
197	if (!track) { return; }
198
199	var slides = Array.prototype.slice.call(track.children);
200	var index  = function () {
201		var x = track.scrollLeft, best = 0;
202		slides.forEach(function (el, i) { if (Math.abs(el.offsetLeft - x) < Math.abs(slides[best].offsetLeft - x)) { best = i; } });
203		return best;
204	};
205	var go = function (i) {
206		i = Math.max(0, Math.min(slides.length - 1, i));
207		track.scrollTo({ left: slides[i].offsetLeft, behavior: 'smooth' });
208	};
209
210	if (prev) { prev.addEventListener('click', function () { go(index() - 1); }); }
211	if (next) { next.addEventListener('click', function () { go(index() + 1); }); }
212	track.addEventListener('scroll', function () {
213		if (current) { current.textContent = index() + 1; }
214	}, { passive: true });
215});
216
217// elementor testimonial carousel: the theme shows the overflow, so loop mode needs more clones than the one-per-view default
218jQuery(window).on('elementor/frontend/init', function () {
219	elementorFrontend.hooks.addAction('frontend/element_ready/testimonial-carousel.default', function ($el) {
220		var container = $el.find('.elementor-main-swiper')[0];
221		var tries     = 0;
222		if (!container) { return; }
223
224		(function wait() {
225			var swiper = container.swiper;
226			if (!swiper) { if (tries++ < 100) { setTimeout(wait, 50); } return; }
227			if (!swiper.params.loop) { return; }
228
229			swiper.params.loopAdditionalSlides = 3;
230			swiper.loopDestroy();
231			swiper.loopCreate();
232			swiper.update();
233			swiper.slideToLoop(0, 0);
234		})();
235	});
236});
237
238// portal login and content handlers
239jQuery(function ($) {
240  // single-password login, the username doubles as the password
241  $('.page-template-portal-login .login-username input, .page-template-vip-portal-alt .login-username input').on('change', function () {
242    $(this).closest('form').find('.login-password input').val($(this).val());
243  });
244
245  var cb = $('form.user-already-submitted #input_234_13_1');
246  if (cb.length && cb.val() === '1') {
247    cb.prop('checked', true);
248  }
249
250  $('#generate-email').on('click', function () {
251    $('#vip-invite-email-content').toggle(500);
252  });
253});
254

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.