PageSourceSearch

https://smartadvisormatch.com/public/dist/AB57B22AF9F56A823411C970A3E2A8CD.js

js smartadvisormatch.com collected 2026-10-01 12:28:41 UTC 14,627 bytes, 433 lines download raw bytes

1var smartasset = smartasset || {};
2
3smartasset.home = smartasset.home || {};
4
5/**
6 * Sends an event for the form in the hero component's submit event
7 * only if the form input is valid.
8 *
9 * @param {element} form - the form element that is being submitted
10 *
11 * @return {boolean} true if the form is valid, false otherwise.
12 */
13smartasset.home.submitHeroFormEvent = function(form)
14{
15    var $input = $(form).children('input');
16    var zip = $input.val();
17
18    if (zip && zip.match($input.attr('pattern')))
19    {
20        smartasset.analytics.eventTrack('home-getstarted-success');
21        return true;
22    }
23
24    return false;
25};
26
27/**
28 * Restricts the input from a user's keyboard from entering non numeric characters
29 * in a form field, the form field is also limited to a fixed number of characters.
30 *
31 * This uses KeyboardEvent.key preferably since KeyboardEvent.keyCode is deprecated
32 * However it does fallback to KeyboardEvent.keyCode if KeyboardEvent.key is not available.
33 *
34 * The logic here accounts for both use cases.
35 *
36 * @param {number} max - maximum number of characters allowed. Defaults to 5 characters.
37 * @param {element} $input - jQuery input element whose keydown events we'll be handling.
38 */
39smartasset.home.restrictNumberInput = function ($input, max) {
40    var selectAll = false;
41
42    var maxCharacters = max || 5;
43    // Allowed keyCodes and key values for modifier keys: backspace, delete, tab, escape and enter.
44    var allowedModifierKeysAndKeycodes =
45        [46, 8, 9, 27, 13, 'Escape', 'Esc', 'Backspace', 'Delete', 'Del', 'Tab', 'Enter'];
46    // Allowed key values for directionals, omitting keyCodes because we need to check for a range.
47    var allowedDirectionalKeys = ['Left', 'ArrowLeft', 'Right', 'ArrowRight', 'Home', 'End'];
48    // Copy & Cut key values and keyCodes
49    var copyOrCutKeysAndKeycodes = ['c', 68, 'x', 88];
50    // 'a' character key value & keyCode
51    var selectAllKeyAndKeycode = ['a', 65];
52
53    $input.on('keydown', function (e) {
54        var inputValue = $(this).val();
55        // Prefer KeyboardEvent.key but fallback to KeyboardEvent.keyCode if needed.
56        var key = e.key || e.keyCode;
57
58        // Allow: left, right, home, end keys
59        if ($.inArray(key, allowedModifierKeysAndKeycodes) !== -1 ||
60            // Allow: Ctrl+c || Cmd+c || Ctrl+x || Cmd+x
61            ($.inArray(key, copyOrCutKeysAndKeycodes) && (e.ctrlKey || e.metaKey)) ||
62            // Allow: home || end || left || right
63            (($.inArray(key, allowedDirectionalKeys) !== -1) || (key >= 35 && key <= 39)))
64        {
65            // let the keypress happen.
66        }
67        else if ($.inArray(key, selectAllKeyAndKeycode) && (e.ctrlKey || e.metaKey))
68        {
69            // Ctrl+A || Cmd+A: Set flag
70            selectAll = true;
71        }
72        else
73        {
74            // Ensure that it is a number and stop the keypress if it is not.
75            // (regex: match only for strings with a single digit or Num and a single digit)
76            if ((key.match(/^\d\b|Num\d$/) === null) ||
77               ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) ||
78               ((inputValue.length > maxCharacters - 1) && !selectAll))
79            {
80                e.preventDefault();
81            }
82            selectAll = false;
83        }
84    });
85};
86
87
88$(function()
89{
90    // handling keyboard input for hero form.
91    var $input = $('.home-hero-form').find('input');
92    smartasset.home.restrictNumberInput($input, 5);
93
94    // handling event tracking for the state and city links via JS
95    $('.js-us-region-links-list').on('click', 'a', function()
96    {
97        smartasset.analytics.eventTrack('home-statedirectory-click', {state: $(this).text()});
98    });
99
100    $('.js-top-cities-list').on('click', 'a', function()
101    {
102        smartasset.analytics.eventTrack('home-citydirectory-click', {city: $(this).data('cityName')});
103    });
104});
105/**
106 * Port of SA's mobile navigation, this is scaled down as there is only one
107 * level of nesting required for the meganav.
108 */
109
110$(function()
111{
112    // Initialize the navigation menu.
113    (function initializeMenu() {
114
115        var $headerButtons = $('#header .buttons'),
116            $navCategories = $headerButtons.find('.nav-categories'),
117            $body = $(document.body),
118            isAnimating = false,
119            currentYPosition = null;
120
121        // Get all the main category links after the mobile sections have been appended.
122        var $navCategoryLinks = $navCategories.find('> ul > li.has-sub:not(.nav-more) > a:not(.js-static-link)');
123
124        // Mobile: Append mobile category titles to the DOM, using the regular link titles.
125        $navCategoryLinks.each(function() {
126            var linkText = $(this).text();
127            if (linkText)
128            {
129                $(this).parent().prepend("<h4 class=\"mobile-menu-category-title js-hidden\">" + linkText + "</h4>");
130            }
131        });
132
133        // Mobile: Navigate to a sub-category on clicking a main category link.
134        $navCategoryLinks.click(function(e) {
135
136            // Since these links can be clicked on desktop, check if on tablet size and below.
137            // NOTE: There is no screen width check in the other click event listeners, because those elements should only appear on mobile/tablet.
138            if ($(document).width() <= 1120)
139            {
140                // Do not not go to the default link URL.
141                e.preventDefault();
142
143                if (isAnimating) return;
144                isAnimating = true;
145
146                var $this = $(this);
147
148                $navCategories.removeClass('overflowed');
149                $navCategories.children('ul').toggleClass('offscreen');
150
151                $this.siblings('.nav-sub-container').addClass('mobile-category-active overflowed');
152                $this.siblings('.mobile-menu-category-title').removeClass('js-hidden');
153
154                setTimeout(function () {
155                    isAnimating = false;
156                }, 500);
157            }
158        });
159
160        // Mobile: Navigate back to main categories on clicking the top titles in the sub menus.
161        $navCategories.find('.mobile-menu-category-title').click(function() {
162            if (!isAnimating)
163            {
164                var $this = $(this);
165
166                isAnimating = true;
167
168                $navCategories.children('ul').removeClass('offscreen');
169                $navCategories.addClass('overflowed');
170
171                setTimeout (function() {
172                    $this.siblings('.nav-sub-container').removeClass('mobile-category-active');
173                    $this.addClass('js-hidden');
174                    isAnimating = false;
175                }, 500);
176            }
177        });
178
179        // Mobile: Toggle the mobile menu on clicking the hamburger button, the mobile close button, or the mobile overlay (area outside the menu).
180        $('.header-mobile-menu-button, .mobile-menu-close-button, #mobile-menu-overlay').on('click', function () {
181
182            // Don't do anything if animating.
183            if (isAnimating) return;
184
185            var menuIsActive = $headerButtons.hasClass('mobile-menu-active');
186
187            $('#mobile-menu-overlay').toggle();
188            isAnimating = true;
189
190            // If menu is open, close it.
191            if (menuIsActive)
192            {
193                $headerButtons.removeClass('mobile-menu-active');
194
195                // Let the body scroll.
196                window.requestAnimationFrame(function()
197                {
198                    // Add a 'mobile-menu-open' class to the body to make it non-scrollable.
199                    $body.removeClass('mobile-menu-open')
200                        .css({
201                            top: ''
202                        });
203
204                    window.scrollTo({
205                        top: currentYPosition,
206                        behavior: 'instant'
207                    });
208                });
209
210                setTimeout(function() {
211                    // Reset elements after the menu has finished closing.
212                    $headerButtons.removeClass('mobile-menu-visible');
213                    $navCategories.children('ul').removeClass('offscreen');
214                    $navCategories.find('.nav-sub-container').removeClass('mobile-category-active');
215                }, 500);
216            }
217            else
218            {
219                // Toggle the visibility of the mobile menu, then scroll it into view.
220                $headerButtons.addClass('mobile-menu-visible');
221                setTimeout(function() {
222                    $headerButtons.addClass('mobile-menu-active');
223                }, 10);
224
225                window.requestAnimationFrame(function()
226                {
227                    // recording the current window position
228                    currentYPosition = window.scrollY;
229                    // Add a 'mobile-menu-open' class to the body to make it non-scrollable.
230                    $body.addClass('mobile-menu-open')
231                        .css({
232                            top: currentYPosition * -1 + 'px'
233                        });
234                });
235
236                // Making the menu scrollable since this class was possibly removed.
237                $navCategories.addClass('overflowed');
238            }
239
240            setTimeout(function() {
241                isAnimating = false;
242            }, 500);
243        });
244    })();
245});
246// All tracking related functionality is contained here
247// mostly ported from SA. tweaked here and there
248var smartasset = smartasset || {};
249
250smartasset.analytics = smartasset.analytics || {};
251
252/**
253 * Initialize mixpanel link tracking
254 */
255smartasset.analytics.initializeTracking = function()
256{
257    // track the page view
258    smartasset.analytics.trackPageView(true);
259
260    // track the page view in facebook
261    smartasset.analytics.facebookTrack('ViewContent');
262};
263
264/**
265 * Track page view on MixPanel and GA
266 * @param noReferer -- boolean. Set to TRUE if you do not want to override the referer
267 */
268smartasset.analytics.trackPageView = function(noReferer)
269{
270    if ($.type(noReferer) !== "boolean")
271    {
272        noReferer = false;
273    }
274
275    if (IS_DEV)
276    {
277        console.log("Tracking page-view // noReferer: " + noReferer);
278    }
279
280    // send Google "pageview", a custom Google event
281    if (typeof ga === 'function')
282    {
283        if (!noReferer)
284        {
285            var url = prefixUrl(smartasset.page.originalUrl);
286            ga('set', 'referrer', url);
287        }
288
289        // "pageview", "page", and "title" are Google Analytics keywords
290        ga('send', 'pageview', {
291            'page': window.location.pathname,
292            'title': document.title
293        });
294
295        if (IS_DEV)
296        {
297            console.log('PageView Track: ', window.location.pathname);
298        }
299
300        // Broadcast our "Page Loaded" event to all services
301        smartasset.analytics.track('Page Loaded');
302    }
303};
304
305/**
306 * Facebook tracking
307 * @param label: custom id to send to FB for tracking
308 * @param customData: a custom object sent with specifications about the event
309 */
310
311smartasset.analytics.facebookTrack = function(label, customData)
312{
313    // First check if the facebook tracker is instantiated
314    if (typeof fbq === 'function' && $.type(label) === 'string')
315    {
316        // Set the event type
317        var eventType;
318        if (label === 'ViewContent')
319        {
320            eventType = 'track';
321        }
322        else
323        {
324            eventType = 'trackCustom';
325        }
326
327        // Check if there is custom data
328        if ($.type(customData) !== 'object')
329        {
330            customData = {};
331        }
332
333        // Include UTM Campaign params
334        var queryParams = $.queryParams();
335
336        customData.utmsource = queryParams.utm_source ? queryParams.utm_source : undefined;
337        customData.utmcampaign = queryParams.utm_campaign ? queryParams.utm_campaign : undefined;
338        customData.utmmedium = queryParams.utm_medium ? queryParams.utm_medium : undefined;
339        customData.utmcontent = queryParams.utm_content ? queryParams.utm_content : undefined;
340        customData.utmterm = queryParams.utm_term ? queryParams.utm_term : undefined;
341
342        // Track the event
343        fbq(eventType, label, customData);
344
345        if (IS_DEV) {
346            console.log('Facebook Track: ' + label, customData);
347        }
348    }
349};
350
351/**
352 * Event Tracker
353 * @param mpEvent: name of the MP event
354 * @param mpParams: additional optional items to include in the MP event
355 */
356smartasset.analytics.eventTrack = function(mpEvent, mpParams)
357{
358    if ($.type(mpEvent) === 'string')
359    {
360        // Track in Google Analytics (GA) and Mixpanel (MP)
361        if ($.type(mpParams) !== 'object')
362        {
363            mpParams = {};
364        }
365
366        smartasset.analytics.track(mpEvent, mpParams);
367    }
368};
369
370/**
371 * PLEASE READ NOTES IN THE CODE ON HOW TO USE THIS!
372 * track("eventName", { value: 4, color: "Blue"})
373 *
374 * @param {string} eventKey
375 * @param {Object=} data optional object with parameters for the event.
376 * @param {boolean} skipMixpanel if true, skips sending data to Mixpanel
377 */
378smartasset.analytics.track = function(eventKey, data, skipMixpanel)
379{
380    var skipGoogleAnalytics = false,
381        eventsToSkipInGA = ['Page Loaded'];
382
383    if (eventsToSkipInGA.indexOf(eventKey) !== -1)
384    {
385        skipGoogleAnalytics = true;
386    }
387
388    // Omit events from MP
389    if (typeof skipMixpanel !== 'boolean')
390    {
391        skipMixpanel = false;
392    }
393
394    if (IS_DEV)
395    {
396        console.log('Tracking Event: ', eventKey, data);
397    }
398
399    // instantiate a copy of the data parameter that we can mutate
400    var properties = (typeof data === 'object') ? data : {};
401
402    // avoid logging an invalid key
403    if (!eventKey || typeof eventKey !== 'string')
404    {
405        console.error('Error - key is invalid');
406        return;
407    }
408
409    properties.page = window.location.pathname;
410
411    // Raise an event within the window. This is useful for detecting
412    // events in other code.
413    // NOTE: replace "createEvent" with CustomEvent when we deprecate IE8
414    if (document.createEvent && document.dispatchEvent)
415    {
416        var evt = document.createEvent('Event');
417        evt.initEvent(eventKey, true, true);
418        document.dispatchEvent(evt);
419    }
420
421    // Send event to Mixpanel
422    if (!skipMixpanel && typeof mixpanel === "object" && typeof mixpanel.track === 'function')
423    {
424        mixpanel.track(eventKey, properties);
425    }
426
427    // Send event to Google Analytics
428    if (!skipGoogleAnalytics && typeof ga === 'function')
429    {
430        ga('send', 'event', eventKey, eventKey, properties);
431    }
432};
433

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.