PageSourceSearch

https://pintofscience.ie/_resources/themes/pos18/js/pages/event-checkout.js?v=1788607614&m=1778050177

js pintofscience.ie collected 2026-09-26 06:32:55 UTC 29,085 bytes, 855 lines download raw bytes

1// Global variables
2var inputMaskCurrency = {
3    radixPoint: ".",
4    groupSeparator: ",",
5    digits: 2,
6    autoGroup: true,
7    prefix: appCfg.currency_symbol,
8    rightAlign: false
9};
10
11// On document loaded
12$(document).ready(function() {
13    // elements
14    var elements = {
15        total: $('.cart-total'),
16        cart: $('.cart-wrapper'),
17        form: $("#payment-form"),
18        intentForm: $('#Form_PaymentIntentForm'),
19        cardArea: $('.client-info-card'),
20        donationTotal: $('.donation-total'),
21        miniCart: $('.mini-cart-total'),
22        clientInfo: $('.client-info')
23    };
24
25    // update quantity
26    updateQuantity(elements);
27
28    // update donation amount
29    updateDonationAmount(elements);
30
31    // remove cart item
32    removeCartItem(elements);
33
34    // offered product
35    initOfferedProduct();
36
37    // customize select drop-down
38    jcf.replace('select');
39
40    // question list
41    initQuestionList();
42    
43    // switch payment method
44    var paymentSwitcher = $('[name="payment"]');
45
46    //--- un-check on page loads
47    paymentSwitcher.prop('checked', false);
48
49    //--- payment method changing
50    paymentSwitcher.on('change', function(){
51        //
52        elements.clientInfo.show();
53
54        // 
55        var val = $(this).val();
56
57        if(val == 'Stripe'){
58            elements.cardArea.find('[data-parsley-required]').attr('data-parsley-required', true);
59            elements.cardArea.show();
60            elements.intentForm.show();
61        }
62        else{
63            elements.cardArea.find('[data-parsley-required]').attr('data-parsley-required', false);
64            elements.cardArea.hide();
65            elements.intentForm.hide();
66        }
67
68        // rebind parsley
69        elements.form.parsley().destroy();
70        elements.form.parsley();
71    });
72
73    // turn on Stripe payment
74    const pmc = document.getElementById('payment_method_card');
75
76    if (pmc) {
77        pmc.checked = true;
78        pmc.dispatchEvent(new Event('change'));
79    }
80
81    // initialize Stripe
82    stripeMethods.init(elements);
83
84    // submit form
85    elements.form.submit(function(event) {
86        // send FB pixel data
87        sendFbPixelData.checkout(elements);
88
89        // Stripe payment
90        if( $('input[name=payment]:checked').val() == 'Stripe' && elements.form.parsley().validate() ) {
91            stripeMethods.makePayment(elements);
92            return false;
93        }
94    });
95
96    // send FB pixel data on checkout success
97    sendFbPixelData.success(elements);
98
99    // monitor cart total and do page refresh if needed
100    const cartTotalEle = elements.total.get(0);
101    const cartTotalInit = getCartTotal(elements);
102
103    const cartTotalObserver = new MutationObserver((mutationsList) => {
104        for (const mutation of mutationsList) {
105            if (mutation.type === "characterData" || mutation.type === "childList") {
106                const cartTotalUpdated = getCartTotal(elements);
107
108                if ((cartTotalInit === 0 || cartTotalUpdated === 0) && cartTotalInit !== cartTotalUpdated) {
109                    elements.cart.LoadingOverlay("show");
110                    window.location.reload();
111                }
112
113                break
114            }
115        }
116    });
117
118    if (cartTotalEle) {
119        cartTotalObserver.observe(cartTotalEle, {
120            characterData: true,
121            childList: true,
122            subtree: false
123        });
124    }
125});
126
127// check nested defined
128function isDefined(obj, key) {
129    if (typeof obj === 'undefined') return false;
130
131    return key.split(".").every(function(x) {
132        if(typeof obj != "object" || obj === null || ! x in obj) {
133            return false;
134        }
135
136        obj = obj[x];
137
138        return true;
139    });
140}
141
142// Update quantity
143function updateQuantity(elements){
144    $('.item-quantity').on('change', function(){
145        var t         = $(this),
146            name      = t.attr('name'),
147            remaining = parseInt( t.data('tickets') ) || 0,
148            quantity  = parseInt( t.data('quantity') ) || 0,
149            total     = remaining + quantity,
150            val       = parseInt( t.val() ) || 1,
151            type      = t.data('type') || 'event';
152
153        var newQuantity = val;
154        
155        if(val > total){
156            t.val(total);
157            newQuantity = total;
158        }
159        else if(val < 1){
160            t.val(1);
161            newQuantity = 1;
162        }
163
164        if(newQuantity > 0){
165            var eid  = parseInt( t.data('event') ) || 0,
166                sec  = t.data('sec') || '';
167
168            var url = appCfg.site_url+ 'cart/'+ (name == 't_quantity' ? 'settQuantity' : 'setquantity') +'/' + eid
169
170            var data = {
171                SecurityID: sec,
172                mergedCart: true
173            };
174
175            if(name == 't_quantity'){
176                data['Tquantity'] = newQuantity;
177            }
178            else{
179                data['quantity'] = newQuantity;
180            }
181
182            if(type === 'product'){
183                data['productSize'] = ( t.data('product-size') || '' );
184
185                url = appCfg.site_url+ 'shop-checkout/setquantity/' + eid;
186            }
187
188            $.ajax({
189                url: url,
190                method: "GET",
191                data: data,
192                beforeSend: function(){
193                    elements.cart.LoadingOverlay("show");
194                },
195                complete: function(){
196                    elements.cart.LoadingOverlay("hide");
197                }
198            })
199            .done(function(data) {
200                elements.total.text(data);
201                
202                // update mini cart
203                elements.miniCart.trigger('updateQuantity');
204            })
205            .fail(function(jqXHR, textStatus){
206                bootbox.alert(textStatus);
207            });
208        }
209    });
210}
211
212// Remove Cart Item
213function removeCartItem(elements){
214    $(document).on('click', '.remove-item', function(){
215        var link = this.href;
216        var message = $(this).data('message') || 'Are you sure you would like to remove this item?';
217        var labels  = {
218            yes: $(this).data('label-yes') || 'Remove',
219            no: $(this).data('label-no') || 'Don\'t remove'
220        };
221
222        bootbox.confirm({
223            className: 'confirm-box',
224            message: message,
225            buttons: {
226                cancel: {
227                    label: labels.no,
228                    className: 'btn-secondary'
229                },
230                confirm: {
231                    label: labels.yes,
232                    className: 'btn-outline-secondary'
233                }
234            },
235            callback: function(result) {
236                if(result){
237                    $.LoadingOverlay("show");
238                    window.location.href = link;
239                }
240            }
241        });
242
243        return false;
244    });
245}
246
247// Update donation amount
248function updateDonationAmount(elements){
249    $(document).on('click', '.change-donation', function(){
250        var self       = $(this),
251            title      = self.attr('title'),
252            amount     = self.data('amount') || 0.00,
253            eid        = self.data('event'),
254            itemId     = self.data('item'),
255            sec        = self.data('sec'),
256            amountText = self.prev('.item-donation-amount');
257
258        var modal = bootbox.prompt({ 
259            className: 'confirm-box',
260            title: title,
261            callback: function(result){
262                if(result){
263                    var newAmount = result.length > 1 ? result.substr(appCfg.currency_symbol.length) : 0;
264
265                    // update amount
266                    $.ajax({
267                        url:  appCfg.site_url+ 'cart/setDonationAmount/' + eid,
268                        method: "GET",
269                        dataType: "json",
270                        data: {
271                            SecurityID: sec,
272                            item_id: itemId,
273                            donation_amount: newAmount,
274                            mergedCart: true
275                        },
276                        beforeSend: function(){
277                            elements.cart.LoadingOverlay("show");
278                        },
279                        complete: function(){
280                            elements.cart.LoadingOverlay("hide");
281                        }
282                    })
283                    .done(function(res) {
284                        if( res.amount && res.amount_total && res.cart_total ){
285                            // update amount data
286                            self.data('amount', res.amount.substr(1) );
287
288                            // update amount text
289                            amountText.text(res.amount);
290
291                            // update donation total
292                            elements.donationTotal.text(res.amount_total);
293
294                            // update cart total
295                            elements.total.text(res.cart_total);
296                        }
297                    })
298                    .fail(function(jqXHR, textStatus){
299                        bootbox.alert(textStatus);
300                    });
301                }
302            }
303        });
304
305        // inputMask
306        modal.on('shown.bs.modal', function (e) {
307            $(e.currentTarget).find('input').val(amount).inputmask("numeric", inputMaskCurrency);
308        })
309        .on('hide.bs.modal', function (e) {
310            $(e.currentTarget).find('input').remove();
311        });
312    });
313}
314
315// Offered product form
316function initOfferedProduct() {
317    // product options
318    var qf = $('[name="product_quantity"]'),
319        pf = $('.product-quantity'),
320        ps = $('#product-size'),
321        op = $('#offered-product');
322
323    ps.on('change', function(){
324        var v       = parseInt( $(this).val() ) || 0,
325            o       = $('option:selected', this),
326            s       = o.attr('data-size') || '',
327            inStock = parseInt(o.attr('data-stock')) || 0;
328
329        qf.find('> option').each(function(){
330            if( $(this).val() <= inStock ){
331                $(this).show();
332            }
333            else{
334                $(this).hide();
335            }
336        });
337
338        if( inStock > 0 ){
339            pf.show();
340        }
341        else{
342            pf.hide();
343        }
344    });
345
346    // show offered product if no product in the cart
347    if( $('.type-ShopOrderItem').length <= 0 ) {
348        op.show();
349    }
350
351    // buy offered product
352    var btnAdd2Card = op.find('.btn-add2cart');
353
354    btnAdd2Card.on('click', function(){
355        var id  = $(this).data('pro-id') || 0,
356            sec = $(this).data('sec-id') || '';
357        
358        window.location.href = appCfg.site_locale_url + 'shop-checkout/add/' + ps.val() + '?SecurityID=' + sec + '&quantity=' + qf.val();
359
360        return false;
361    });
362
363    op.find('.btn-ok-add').on('click', function(){
364        if (op.find('.product-quantity').is(':visible')) {
365            btnAdd2Card.trigger('click');
366            return false;
367        }
368
369        var error = $(this).data('error-variation') || 'Please select product size and/or variation';
370
371        bootbox.alert(error); return false;
372    });
373
374    op.find('.btn-no-add').on('click', function(){
375        op.remove(); return false;
376    });
377}
378
379// Get cart total
380function getCartTotal(elements) {
381    var t = elements.total.first().text().replace(/[^0-9\.]+/g,"");
382
383    return t ? parseFloat(t) : 0;
384}
385
386// Reset recaptcha
387function resetRecaptcha() {
388    if (typeof window.grecaptcha !== "undefined") {
389        window.grecaptcha.reset();
390    }
391}
392
393// Fire FB pixel event
394var sendFbPixelData = {
395    // checkout page
396    checkout: function(elements){
397        // vars
398        var select, items = [];
399
400        // collect item
401        $('.cart-item').each(function(){
402            select = $(this).find('[name="quantity"]')
403    
404            if(select.length){
405                var item     = $(this),
406                    id       = select.data('event'),
407                    qty      = parseInt( select.val() ),
408                    price    = item.find('.item-price').first().text().replace(/[^0-9\.]+/g,""),
409                    donation = item.find('.item-donation-amount').text().replace(/[^0-9\.]+/g,"");
410    
411                items.push({
412                    'id': id,
413                    'quantity': qty,
414                    'item_price': price
415                });
416            }
417        });
418
419        // send data
420        if(items.length && $.inArray(appCfg.fb_method, ["client", "both"]) !== -1){
421            var fbData = {
422                contents: items,
423                content_type: 'product',
424                value: elements.total.first().text().replace(/[^0-9\.]+/g,""),
425                currency: appCfg.currency_code 
426            };
427
428            fbq('track', 'InitiateCheckout', fbData);
429            fbq('track', 'AddPaymentInfo', fbData);
430        }
431    },
432    // success page
433    success: function(elements){
434        // validate
435        if( !document.URL.match(/cart\/success/gi) ){
436            return;
437        }
438
439        // vars
440        var items = [];
441        var gtm_items = [];
442        var order_id = document.querySelector('.cart-summary')?.dataset?.orderId || '';
443
444        // collect item
445        $('.cart-item-product').each(function(){
446            var item     = $(this),
447                id       = item.data('id'),
448                qty      = item.find('.item-quantity-text').first().text().replace(/[^0-9\.]+/g,""),
449                price    = item.find('.item-price').first().text().replace(/[^0-9\.]+/g,""),
450                title    = item.find('.item-title')?.first()?.text() || '',
451                category = item.find('.item-category')?.first()?.text() || '';
452                
453            items.push({
454                'id': id,
455                'quantity': qty,
456                'item_price': price
457            });
458
459            gtm_items.push({
460                id:          item.data('id'),
461                name:        title,
462                price:       price,
463                quantity:    qty,
464                category:    category,
465            });
466        });
467
468        // send data
469        if(items.length && $.inArray(appCfg.fb_method, ["client", "both"]) !== -1){
470            var fbData = {
471                contents: items,
472                content_type: 'product',
473                value: getCartTotal(elements),
474                currency: appCfg.currency_code 
475            };
476
477            fbq('track', 'Purchase', fbData);
478        }
479
480        // send data to GTM and RedditPixel
481        window.Tracking.trackPurchase({
482            id:          "#" + order_id,
483            value:       getCartTotal(elements),
484            currency:    appCfg.currency_code,
485            items: gtm_items,
486        });
487    }
488};
489
490// Do payment
491var stripeMethods = {
492    stripe: null,
493    clientSecret: null,
494    cardElement: null,
495    stripeElements: null,
496    canSubmit: false,
497    cardHolder: $('#stripe-card-element'),
498    errorHolder: $('#stripe-card-errors'),
499    //
500    init: function(elements) {
501        var amount = getCartTotal(elements);
502
503        if (appCfg.stripe_pk && this.cardHolder.length && amount > 0) {
504            var self         = this;
505            var locale       = document.documentElement.lang || 'en';
506                locale       = locale.split('-')[0];
507
508            this.stripe = Stripe(appCfg.stripe_pk, {
509                locale: locale
510            });
511
512            this.stripeElements = this.stripe.elements({
513                mode: 'payment',
514                currency: window.appCfg.currency_code.toLowerCase(),
515                amount: Math.ceil(amount * 100),
516            });
517
518            this.cardElement = this.stripeElements.create('payment');
519
520            // mount card element
521            this.cardElement.mount('#stripe-card-element');
522
523            // validate card
524            this.cardElement.addEventListener('change', function(event) {
525                if (event.error) {
526                    self.errorHolder.html('<div class="parsley-errors-list filled" style="position: static;">'+ event.error.message +'</div>');
527                    self.canSubmit = false;
528                }
529                else {
530                    self.errorHolder.html('');
531                    self.canSubmit = true;
532                }
533            });        
534        }
535    },
536    //
537    makePayment: async function(elements) {
538        var self = this;
539        var amount = getCartTotal(elements);
540
541        if (self.stripe && self.canSubmit && self.cardHolder.length && amount > 0) {
542            // display loading indicator
543            $.LoadingOverlay("show");
544
545            // Update Stripe Elements amount
546            this.stripeElements.update({amount: Math.ceil(amount * 100)});
547
548            // Trigger form validation and wallet collection
549            const {error: submitError} = await self.stripeElements.submit();
550
551            if (submitError) {
552                // show errors
553                bootbox.alert(submitError);
554
555                // hide loading overlay
556                $.LoadingOverlay("hide");
557
558                //
559                return;
560            }
561
562            // create payment intent
563            var paymentForm = $('#payment-form');
564            var intentForm = $('#Form_PaymentIntentForm');
565            var ifData     = intentForm.serializeArray();
566
567            paymentForm.serializeArray().forEach((item) => {
568                if (item.name !== 'SecurityID') {
569                    ifData.push(item);
570                }
571            })
572            
573            $.ajax({
574                url: intentForm.attr("action"),
575                method: "POST",
576                data: ifData,
577                complete: function(){}
578            })
579            .done(function(data) {
580                if (typeof data === "string") {
581                    if (data.includes("message validation")) {
582                        bootbox.alert($(data).find('.message.validation').text());
583                    }
584
585                    $.LoadingOverlay("hide");
586                }
587                else if (data.status == 'success') {
588                    clientSecret  = data.client_secret;
589                    paymentIntent = data.id;
590                    
591                    // Submit payment form and complete order
592                    self.stripe.confirmPayment({
593                        elements: self.stripeElements,
594                        clientSecret: clientSecret,
595                        confirmParams: {
596                            return_url: data.return_url,
597                        },
598                        redirect: 'if_required',
599                    })
600                    .then(function(result) {
601                        if (result.error) {
602                            resetRecaptcha();
603                            bootbox.alert(result.error.message);
604                            $.LoadingOverlay("hide");
605                        }
606                        else if (isDefined(result, 'paymentIntent.status') && result.paymentIntent.status == 'succeeded') {
607                            paymentForm.append(
608                                '<input type="hidden" name="payment_intent" value="'+ result.paymentIntent.id +'" />' +
609                                '<input type="hidden" name="client_secret" value="'+ result.paymentIntent.client_secret +'" />'
610                            );
611                            
612                            paymentForm.get(0).submit();
613                        }
614                        else if (isDefined(result, 'paymentIntent.status') && result.paymentIntent.status === 'processing') {
615                            window.location.href = appCfg.site_url+ 'cart/processing/';
616                        }
617                        else {
618                            resetRecaptcha();
619                            $.LoadingOverlay("hide");
620
621                            if (isDefined(result, 'paymentIntent.status')) {
622                                bootbox.alert("Can't complete your payment. Status: " + result.paymentIntent.status);
623                            }
624                            else {
625                                console.log(result);
626                            }
627                        }
628                    });
629
630                    /*
631                    self.stripe.handleCardPayment(clientSecret, self.cardElement).then(function (result) {
632                        if (result.error) {
633                            resetRecaptcha();
634                            bootbox.alert(result.error.message);
635                            $.LoadingOverlay("hide");
636                        } 
637                        else if (isDefined(result.paymentIntent, 'status') && result.paymentIntent.status == 'succeeded') {
638                            var form = $('#payment-form');
639
640                            form.append(
641                                '<input type="hidden" name="payment_intent" value="'+ paymentIntent +'" />' +
642                                '<input type="hidden" name="client_secret" value="'+ clientSecret +'" />'
643                            );
644                            
645                            form.get(0).submit();
646                        }
647                    });
648                    */
649                }
650                else {
651                    resetRecaptcha();
652                    bootbox.alert(data.message);
653                    $.LoadingOverlay("hide");
654                }
655            })
656            .fail(function(jqXHR, textStatus){
657                resetRecaptcha();
658                bootbox.alert(textStatus);
659                $.LoadingOverlay("hide");
660            });
661        }
662        else {
663            bootbox.alert('Please complete credit card details.');
664        }
665
666        /*
667        data = {
668            number: $('.card-number').val(),
669            cvc: $('.card-cvc').val(),
670            exp_month: $('.card-exp_month').val(),
671            exp_year: $('.card-exp_year').val(),
672            name: $('.card-name').val(),
673        };
674    
675        Stripe.card.createToken(data, stripeMethods.responseHandler);
676        */
677    },
678    //
679    responseHandler: function(status, response) {
680        var form = $('#payment-form');
681
682        if (response.error) {
683            stripeMethods.triggerError(response.error.param, response.error.code, response.error.message);
684        } 
685        else {
686            token = response.id;
687            form.append($('<input type="hidden" name="token" />').val(token));
688            form.get(0).submit();
689        }
690    },
691    //
692    triggerError: function(param, code, message) {
693        if( $('.card-'+param).length !== 0 ) {
694            parsleyInstance = $('.card-'+param).parsley();
695            window.ParsleyUI.removeError(parsleyInstance, code);
696            window.ParsleyUI.addError(parsleyInstance, code, message);
697        } 
698        else {
699            bootbox.dialog({
700                title: 'Error!!!',
701                message: '<div class="alert alert-danger mb-0" role="alert">'+ response.error.message +'</div>'
702            });
703        }
704    }
705};
706
707/**
708 * Question list
709 */
710function initQuestionList()
711{
712    var qdrClass = '.question-display-rules';
713    var questions = {};
714    var form;
715
716    document.querySelectorAll(qdrClass).forEach(function(ele) {
717        if (ele.value) {
718            var qid   = ele.dataset.qid;
719            var rules = JSON.parse(ele.value);
720
721            if (rules) {
722                var holder = document.getElementById('question-container-'+ qid +'_Holder');
723                var input  = holder.querySelector('[name="Questions_'+ qid +'"]');
724                var conditions = {};
725
726                if (rules.Rules && rules.Rules.length) {
727                    rules.Rules.forEach(function(_rule) {
728                        conditions[_rule.ConditionQuestion] = {
729                            'option': _rule.ConditionOption,
730                            'value': _rule.ConditionValue,
731                        }
732                    });
733                }
734
735                // store question data
736                questions[qid] = {
737                    'holder': holder,
738                    'input': input,
739                    'conditions': conditions,
740                    'DisplayOnLoad': rules.DisplayOnLoad,
741                    'DisplayRulesConjunction': rules.DisplayRulesConjunction,
742                };
743
744                // hide the question holder if not to be displayed on load
745                if (rules.DisplayOnLoad !== 1) {
746                    holder.classList.add('d-none');
747                    toggleIgnoreValidation(holder, 'on');
748                }
749
750                // get the form
751                if (!form) {
752                    form = holder.closest('form');
753                }
754            }
755        }
756    });
757
758    if (form) {
759        toggleQuestions(questions);
760
761        form.addEventListener("change", function(e) {
762            var regex = /^Question/;
763            
764            if (e.target.name.match(regex)) {
765                toggleQuestions(questions);
766            }
767        });
768    }
769
770    // method to toggle the visibility of questions
771    function toggleQuestions(questions) {
772        if (Object.keys(questions).length > 0) {
773            for (var qid in questions) {
774                var question = questions[qid];
775                var conditions_size = Object.keys(question.conditions).length;
776    
777                if (question.conditions && conditions_size > 0) {
778                    var displays = [];
779    
780                    for (var cid in question.conditions) {                      
781                        var condition = question.conditions[cid];
782                        var conditionCurrVal = questions[cid] && questions[cid].input ? questions[cid].input.value : "";
783
784                        if (questions[cid] && questions[cid].input && questions[cid].input.type === "checkbox" && !questions[cid].input.checked) {
785                            conditionCurrVal = "";
786                        }
787    
788                        var conditionNumbers = {
789                            left: parseFloat(conditionCurrVal),
790                            right: parseFloat(condition.value)
791                        };
792    
793                        if (condition.option === "IsBlank" && conditionCurrVal === "") {
794                            displays.push(1);
795                        }
796                        else if (condition.option === "IsNotBlank" && conditionCurrVal !== "") {
797                            displays.push(1);
798                        }
799                        else if (condition.option === "HasValue" && conditionCurrVal === condition.value) {
800                            displays.push(1);
801                        }
802                        else if (condition.option === "ValueNot" && conditionCurrVal !== condition.value) {
803                            displays.push(1);
804                        }
805                        else if (condition.option === "ValueLessThan" && conditionNumbers.left < conditionNumbers.right) {
806                            displays.push(1);
807                        }
808                        else if (condition.option === "ValueLessThanEqual" && conditionNumbers.left <= conditionNumbers.right) {
809                            displays.push(1);
810                        }
811                        else if (condition.option === "ValueGreaterThan" && conditionNumbers.left > conditionNumbers.right) {
812                            displays.push(1);
813                        }
814                        else if (condition.option === "ValueGreaterThanEqual" && conditionNumbers.left >= conditionNumbers.right) {
815                            displays.push(1);
816                        }
817                    }
818    
819                    if (question.DisplayRulesConjunction === "Or") {
820                        if (displays.length) {
821                            question.holder.classList.remove('d-none');
822                            toggleIgnoreValidation(question.holder, 'off');
823                        }
824                        else {
825                            question.holder.classList.add('d-none');
826                            toggleIgnoreValidation(question.holder, 'on');
827                        }
828                    }
829                    else if (question.DisplayRulesConjunction === "And") {
830                        if (displays.length === conditions_size) {
831                            question.holder.classList.remove('d-none');
832                            toggleIgnoreValidation(question.holder, 'off');
833                        }
834                        else {
835                            question.holder.classList.add('d-none');
836                            toggleIgnoreValidation(question.holder, 'on');
837                        }
838                    }
839                }
840            }
841        }
842    }
843
844    // method to toogle .ignore-validation class on input
845    function toggleIgnoreValidation(holder, action) {
846        holder.querySelectorAll('input').forEach(function(ele){
847            if (action === 'on') {
848                ele.classList.add('ignore-validation');
849            }
850            else {
851                ele.classList.remove('ignore-validation');
852            }
853        });
854    }
855}

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.