PageSourceSearch

https://www.twik.io/wp-content/themes/twik_website_v2/js/page-pricing/v2/common.js?v=1.0.0

js twik.io collected 2026-09-25 02:41:12 UTC 18,827 bytes, 554 lines download raw bytes

1const wpadminbar = document.getElementById('wpadminbar');
2const masthead = document.getElementById('masthead');
3const planSwitcher = document.querySelector('.plans-changer--bottom');
4
5const getOffsetHeight = () => {
6    let height = masthead.getBoundingClientRect().height;
7
8    if (wpadminbar) {
9        height = height + wpadminbar.getBoundingClientRect().height;
10    }
11
12    return height;
13};
14
15const getTop = (el) => el.offsetTop + (el.offsetParent && getTop(el.offsetParent));
16
17/**
18 * Plan sticky switcher
19 */
20(() => {
21    const planSwitcherDetector = document.querySelector('.plan-switcher-detector');
22
23    if (!planSwitcherDetector || !planSwitcher) {
24        return;
25    }
26
27    const callback = (entries) => {
28        planSwitcher.classList.toggle('is-sticky', !entries[0].isIntersecting);
29    }
30
31    let options = {
32        rootMargin: `-${getOffsetHeight()}px 0px 0px 0px`,
33        threshold: 0,
34    };
35
36    let observer = new IntersectionObserver(callback, options);
37
38    observer.observe(planSwitcherDetector);
39
40    window.addEventListener('resize', () => {
41        observer.disconnect();
42
43        options = {
44            rootMargin: `-${getOffsetHeight()}px 0px 0px 0px`,
45            threshold: 0,
46        };
47
48        observer = new IntersectionObserver(callback, options);
49
50        observer.observe(planSwitcherDetector);
51    });
52
53    function setUpPlanSwitcherHeight() {
54        document.body.style.setProperty('--plan-switcher-height', `${planSwitcher.getBoundingClientRect().height}px`);
55    }
56
57    setUpPlanSwitcherHeight();
58
59    document.fonts.ready.then(setUpPlanSwitcherHeight);
60
61    window.addEventListener('resize', setUpPlanSwitcherHeight);
62})();
63
64/**
65 * PC Mobile Observer
66 */
67(() => {
68    const stickyDetector = document.querySelector('.pc-mobile-observer');
69
70    if (!stickyDetector) {
71        return;
72    }
73
74    const callback = (entries) => {
75        stickyDetector.classList.toggle('is-sticky', !entries[0].isIntersecting);
76        document.body.classList.toggle('pricing-panel-is-sticky', !entries[0].isIntersecting);
77    }
78
79    let options = {
80        rootMargin: `-${getOffsetHeight()}px 0px 0px 0px`,
81        threshold: 0,
82    };
83
84    let observer = new IntersectionObserver(callback, options);
85
86    observer.observe(stickyDetector);
87
88    window.addEventListener('resize', () => {
89        observer.disconnect();
90
91        options = {
92            rootMargin: `-${getOffsetHeight()}px 0px 0px 0px`,
93            threshold: 0,
94        };
95
96        observer = new IntersectionObserver(callback, options);
97
98        observer.observe(stickyDetector);
99    });
100})();
101
102
103/**
104 * Plan sticky panels
105 */
106(() => {
107    const stickyCardPanels = document.querySelectorAll('.pri-card-data-sticky');
108
109    if (!stickyCardPanels.length) {
110        return;
111    }
112
113    const plansCards = document.querySelector('.pri-card-plans');
114    const detector = stickyCardPanels[0].querySelector('._pri_card_data_sticky_observer');
115
116    function getPanelsOffsetHeight() {
117        return getOffsetHeight() + planSwitcher.getBoundingClientRect().height;
118    };
119
120    function setUpPanelSizes() {
121        let rect = stickyCardPanels[0].parentNode.querySelector('.pri-card-features').getBoundingClientRect();
122        let height = rect.height + stickyCardPanels[0].querySelector('.pri-card-data-sticky__content').getBoundingClientRect().height;
123
124        stickyCardPanels.forEach((panel) => {
125            panel.style.height = `${height + 1}px`;
126        });
127    }
128
129    setUpPanelSizes();
130
131    document.fonts.ready.then(setUpPanelSizes);
132
133    document.addEventListener('resize', setUpPanelSizes);
134
135    let options = {
136        rootMargin: `-${getPanelsOffsetHeight()}px 0px 0px 0px`,
137        threshold: 0,
138    };
139
140    let callback = (entries) => {
141        plansCards.classList.toggle('is-plan-cards-sticky', !entries[0].isIntersecting);
142    };
143
144    let observer = new IntersectionObserver(callback, options);
145
146    observer.observe(detector);
147
148    window.addEventListener('resize', () => {
149        observer.disconnect();
150
151        options = {
152            rootMargin: `-${getPanelsOffsetHeight()}px 0px 0px 0px`,
153            threshold: 0,
154        };
155
156        observer = new IntersectionObserver(callback, options);
157
158        observer.observe(detector);
159    });
160})();
161
162/**
163 * Backgrounds
164 */
165(() => {
166    const heroCoverDown = document.querySelector('.pri-hero-cover--down');
167    const videoTestimonials = document.querySelector('.video-testimonials');
168
169    if (heroCoverDown) {
170        const hero = heroCoverDown.parentNode;
171
172        function setUpCoverBottom() {
173            let heroTop = getTop(hero) + hero.getBoundingClientRect().height;
174            let videoTop = getTop(videoTestimonials);
175
176            heroCoverDown.style.insetBlockEnd = `-${videoTop - heroTop}px`;
177        }
178
179        setUpCoverBottom();
180
181        document.fonts.ready.then(setUpCoverBottom);
182
183        window.addEventListener('resize', setUpCoverBottom);
184    }
185})();
186
187/**
188 * Plan switcher
189 */
190jQuery(document).ready(($) => {
191    const $planSwitchers = $('.plan-switcher-list');
192    const $planSwitchCtrl = $('.plan-switch-ctrl');
193    const $cardPlans = $('.pri-card-plans');
194    const $cardPriceOld = $cardPlans.find('.pri-card__price-old');
195
196    function setUpSwitchFloater() {
197        $planSwitchers.each((i, switcher) => {
198            let $active = $(switcher).find('.plan-switch-ctrl--active');
199            let $floater = $(switcher).find('.plan-switch-ctrl-floater');
200
201            $floater.css({
202                width: $active.outerWidth(),
203                transform: `translateX(${$active.position().left}px)`,
204            });
205        });
206    }
207
208    const $labels = $('.plans-changer-desktop .plan-switcher-label, .plans-changer--top .plan-switcher-label');
209
210    if ($labels.length > 0) {
211        function setUpSwitchFloaterLabels() {
212            $labels.each((i, label) => {
213                let $parent = $(label).closest('.plan-switcher-list');
214
215                $parent.css({
216                    'margin-inline-end': `${$(label).outerWidth() - 24}px`,
217                });
218            });
219        }
220
221        setUpSwitchFloaterLabels();
222
223        document.fonts.ready.then(() => {
224            setUpSwitchFloaterLabels();
225        });
226
227        window.addEventListener('resize', setUpSwitchFloaterLabels);
228    }
229
230    if ($planSwitchers.length > 0) {
231        setUpSwitchFloater();
232
233        document.fonts.ready.then(() => {
234            setUpSwitchFloater();
235
236            $planSwitchers.addClass('inited');
237        });
238
239        window.addEventListener('resize', setUpSwitchFloater);
240
241        $planSwitchCtrl.on('click', function(event) {
242            event.preventDefault();
243
244            const $btn = $(this);
245            const type = $btn.data('type');
246
247            $planSwitchers.each((i, switcher) => {
248                let $active = $(switcher).find(`[data-type="${type}"]`);
249
250                $active
251                    .addClass('plan-switch-ctrl--active')
252                    .siblings()
253                    .removeClass('plan-switch-ctrl--active');
254
255                setUpSwitchFloater();
256            });
257
258            $cardPlans
259                .removeClass('pri-card-plans--monthly pri-card-plans--yearly')
260                .addClass(`pri-card-plans--${type}`);
261
262            if ('yearly' === type) {
263                $cardPriceOld.stop().animate({
264                    opacity: 1,
265                    height: 'toggle',
266                });
267            } else {
268                $cardPriceOld.stop().animate({
269                    opacity: 'toggle',
270                    height: 'toggle',
271                });
272            }
273        });
274
275        /**
276         * Plans range picker
277         */
278        const $rangeSliders = $('.pri-card-stepper-ctrl');
279
280        if ($rangeSliders.length > 0) {
281            const $priCards = $('.pri-card');
282            const plansOptions = JSON.parse(document.getElementById('pricing_plans_options').innerHTML);
283
284            function markRangesGridsAsActive(value) {
285                $('.irs-grid-text').each((i, item) => {
286                    item.classList.toggle('is-active', item.textContent === value);
287                });
288            }
289
290            function updateIrsBarSingle($bar, percent) {
291                $bar.css({
292                    opacity: percent * 0.01,
293                });
294            }
295
296            function updateDataByRangeValue(value) {
297                plansOptions.forEach((plan, index) => {
298                    let visit = plan.plan_visits.filter((item) => item.visit === value)[0];
299                    let $card = $priCards.eq(index);
300
301                    if (visit.enabled) {
302                        $card.removeClass('pri-card--disabled');
303
304                        let $features = $priCards.eq(index).find('.pri-card-features');
305
306                        let $featuresHTML = visit['plan:features'];
307
308                        $featuresHTML = $featuresHTML.replaceAll('[x]', '<span class="feat-enabled feat-enabled--yes"></span>');
309                        $featuresHTML = $featuresHTML.replaceAll('[ ]', '<span class="feat-enabled feat-enabled--no"></span>');
310                        $featuresHTML = $featuresHTML.split('\n').map((item) => `<li class="pri-card-feature">${item}</li>`).join('');
311
312                        $features.html($featuresHTML);
313
314                        const formatter = new Intl.NumberFormat('en-US');
315
316                        let priceMo = visit['price:mo'];
317                        let priceYr = visit['price:yr'];
318
319                        if (!isNaN(priceMo)) {
320                            priceMo = formatter.format(priceMo);
321                        }
322
323                        if (!isNaN(priceYr)) {
324                            priceYr = formatter.format(priceYr);
325                        }
326
327                        $card.find('._price_mo').html(priceMo);
328                        $card.find('._price_yr').html(priceYr);
329
330                        $card.find('.twik-btn').attr('data-color_mode', visit['btn_color']);
331                    } else {
332                        $card.addClass('pri-card--disabled');
333
334                        $card.find('._price_mo').html('--');
335                        $card.find('._price_yr').html('--');
336                    }
337                });
338            }
339
340            $rangeSliders.each((i, slider) => {
341                let $bar = null;
342                let visits_values = PT_VISITS.split(',');
343
344                $(slider).ionRangeSlider({
345                    skin: 'big',
346                    grid: true,
347                    values: visits_values,
348                    onStart: function (data) {
349                        $bar = $(data.slider).find('.irs-bar.irs-bar--single');
350
351                        markRangesGridsAsActive(data.from_value);
352                    },
353                    onUpdate: function(data) {
354                        $bar = $(data.slider).find('.irs-bar.irs-bar--single');
355
356                        updateIrsBarSingle($bar, data.from_percent);
357                        markRangesGridsAsActive(data.from_value);
358                        updateDataByRangeValue(data.from_value);
359                    },
360                    onChange: function(data) {
361                        let value = $(data.input).prop('value');
362
363                        updateIrsBarSingle($bar, data.from_percent);
364                        markRangesGridsAsActive(value);
365                        updateDataByRangeValue(value);
366                    },
367                });
368
369                let instance = $(slider).data('ionRangeSlider');
370
371                $(document).on('click', '.irs-grid-text', function() {
372                    instance.update({
373                        from: visits_values.indexOf($(this).text()),
374                    });
375                });
376            });
377        }
378    }
379});
380
381
382jQuery(document).ready(function ($) {
383    if (document.body.classList.contains("page-template-page-pricing-v2")) {
384        // js switch slider
385
386        const parentSwitch = document.querySelectorAll(".js-slider-control");
387
388        if (parentSwitch !== null) {
389            parentSwitch.forEach((parent) => {
390                const switchButton = parent.querySelectorAll(".controls-tab__control");
391
392                const switchBlock = parent.querySelectorAll(".card-slider");
393
394                let backFloater = parent.querySelector(".controls-tab__back");
395                let activeTab = [...switchButton].filter((ctrl) => ctrl.classList.contains("active"))[0];
396
397                window.setUpActiveTabState = function(modal = false) {
398                    if (modal && modal.openedWindow) {
399                        backFloater = modal.openedWindow.querySelector(".controls-tab__back");
400                        activeTab = [...modal.openedWindow.querySelectorAll(".controls-tab__control")].filter((ctrl) => ctrl.classList.contains("active"))[0];;
401                    }
402
403                    backFloater.style.width = activeTab.clientWidth + "px";
404                    backFloater.style.transform = `translate(${activeTab.offsetLeft}px, -50%)`;
405                    backFloater.style.backgroundColor = activeTab.dataset.bgcolor;
406                }
407
408                setUpActiveTabState();
409
410                window.addEventListener("resize", setUpActiveTabState);
411
412                document.fonts.ready.then(setUpActiveTabState);
413
414                switchButton.forEach((el) => {
415                    const id = el.getAttribute("data-id");
416
417                    activeTab = el;
418
419                    el.addEventListener("click", () => {
420                        activeTab = el;
421
422                        if (el.classList.contains("js-slider-second")) {
423                            parent.classList.add("dark");
424                        } else {
425                            parent.classList.remove("dark");
426                        }
427
428                        switchButton.forEach((siblings) => {
429                            siblings.classList.remove("active");
430                        });
431
432                        switchBlock.forEach((siblings) => {
433                            siblings.classList.remove("active");
434                        });
435
436                        if (parent.querySelector('.card-slider[data-id="' + id + '"]') !== null) {
437                            parent.querySelector('.card-slider[data-id="' + id + '"]').classList.add("active");
438                        }
439
440                        setUpActiveTabState();
441
442                        el.classList.add("active");
443                    });
444                });
445            });
446        }
447
448        const svgControls = document.querySelector(".js-svg-controls");
449        const svgControl = svgControls.querySelectorAll(".js-svg-control");
450        const tabsWrap = svgControls.querySelector(".svg-tabs");
451        const svgParent = document.querySelector(".product-second");
452        const iceButton = svgParent.querySelector(".js-move-iceberg");
453        const iceButtonBack = svgParent.querySelector(".js-moveBack-iceberg");
454        const iceberg = svgParent.querySelector(".iceberg");
455        const rcontainer = svgParent.querySelector(".rconainer");
456        const palms = rcontainer.querySelectorAll(".right-elem");
457        const clouds = svgParent.querySelectorAll(".cloud");
458        const bg = svgParent.querySelector(".product-second__bg");
459        const text = svgParent.querySelector(".wth-text");
460
461        if (svgControls !== null && svgControl !== null) {
462            iceButton.addEventListener("click", () => {
463                iceberg.classList.add("move");
464
465                iceButton.classList.add("active");
466
467                iceButtonBack.classList.remove("active");
468
469                iceButton.closest(".controls-tab__body").classList.add("green");
470
471                clouds.forEach((palm) => {
472                    palm.classList.add("move");
473                });
474
475                tabsWrap.classList.add("hide1");
476
477                rcontainer.classList.add("show");
478
479                palms.forEach((palm) => {
480                    palm.classList.add("show");
481                });
482
483                setTimeout(() => {
484                    rcontainer.classList.add("finish");
485
486                    bg.classList.add("finish");
487
488                    text.classList.add("show");
489                }, 200);
490
491                setTimeout(() => {
492                    iceberg.classList.add("hide1");
493                }, 200);
494            });
495
496            iceButtonBack.addEventListener("click", () => {
497                iceberg.classList.remove("hide1");
498                iceberg.classList.remove("move");
499
500                iceButtonBack.classList.add("active");
501
502                tabsWrap.classList.remove("hide1");
503
504                iceButton.classList.remove("active");
505                iceButton.closest(".controls-tab__body").classList.remove("green");
506
507                palms.forEach((palm) => {
508                    palm.classList.remove("show");
509                });
510
511                clouds.forEach((palm) => {
512                    palm.classList.remove("move");
513                });
514
515                rcontainer.classList.remove("show");
516                rcontainer.classList.remove("finish");
517
518                bg.classList.remove("finish");
519
520                text.classList.remove("show");
521            });
522
523            svgControl.forEach((control) => {
524                let id = control.getAttribute("data-id");
525
526                control.addEventListener("click", () => {
527                    svgControls.querySelectorAll(".js-control-tab").forEach((tab) => {
528                        tab.classList.remove("active");
529                    });
530
531                    svgControl.forEach((el) => {
532                        el.classList.remove("active");
533                    });
534
535                    control.classList.add("active");
536
537                    document.querySelector('.button--tab[data-id="' + id + '"]').classList.add("active");
538
539                    svgControls.querySelector('.js-control-tab[data-id="' + id + '"]').classList.add("active");
540
541                    iceberg.querySelectorAll(".js-svg-control").forEach((elem) => {
542                        if (elem.getAttribute("data-id") === id) {
543                            elem.classList.add("active");
544                        }
545                    });
546
547                    document.body.classList.remove("oh");
548                });
549            });
550        }
551    }
552});
553
554

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.