PageSourceSearch

https://richardson-realty.com/Widgets/BaseHelper.js?v=jRaTagZYps2Bkq5LlvbRMZiiRFq1XftrPi5vH5WCyUI

js richardson-realty.com collected 2026-09-27 13:01:36 UTC 59,761 bytes, 1,781 lines download raw bytes

1var BaseHelper = {
2    uuid: 0,
3    setIfNotNull: (item, field, value) => {
4        if (item == null || value == null || item[field] == null) {
5            return;
6        }
7        item[field] = value;
8    },
9
10    isNullOrUndefined(value) {
11        return value === null || value === undefined;
12    },
13
14    // Earth's radius in kilometers and miles
15    EARTH_RADIUS_KM: 6371,
16    EARTH_RADIUS_MI: 3959,
17    extractNumbers: (str) => {
18        let result = '';
19        let hasDecimal = false;
20
21        for (let i = 0; i < str.length; i++) {
22            const char = str[i];
23
24            if (char >= '0' && char <= '9') {
25                result += char;
26            } else if (char === '.' && !hasDecimal) {
27                result += char;
28                hasDecimal = true;
29            }
30        }
31
32        return result;
33    },
34    calculateGeoDistance: function (point1, point2, radius) {
35        // Convert latitude and longitude from degrees to radians
36        var lat1 = point1.lat * Math.PI / 180;
37        var lat2 = point2.lat * Math.PI / 180;
38        var deltaLat = (point2.lat - point1.lat) * Math.PI / 180;
39        var deltaLng = (point2.lng - point1.lng) * Math.PI / 180;
40
41        // Apply the Haversine formula
42        var a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
43            Math.cos(lat1) * Math.cos(lat2) *
44            Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
45        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
46
47        // Distance in the unit corresponding to the given radius
48        var distance = radius * c;
49
50        return distance;
51    },
52
53    insertOrGetLookupArr: function (lookup, key) {
54        let arr = lookup[key];
55        if (arr == null) {
56            arr = lookup[key] = [];
57        }
58        return arr;
59    },
60
61    getTags: function (data) {
62        data.tag = BaseHelper.DeserializeDataTableToObjects(data.tagRaw);
63        let listingTagKeyValueList = data.tag;
64        let tagKeyList = data.tagKey;
65        let tagKeyValueList = data.tagKeyValue;
66        let tagValueList = data.tagValue;
67
68        let tagList = [];
69        let tagValueLookupTagGroup = {};
70        let keywordLookupTagGroup = {}
71        let idLookupTagKey = {};
72        let idLookupTagValue = {};
73        let idLookupTagKeyValue = {};
74        let idLookupTag = {};
75
76        let listingLookupTag = {};
77        let tagLookupListing = {};
78
79        tagKeyList.forEach((tagKey) => {
80            idLookupTagKey[tagKey.TagKeyId] = tagKey;
81        });
82
83        tagValueList.forEach((tagValue) => {
84            idLookupTagValue[tagValue.TagValueId] = tagValue;
85        });
86
87        tagKeyValueList.forEach((tagKeyValue) => {
88            idLookupTagKeyValue[tagKeyValue.TagKeyValueId] = tagKeyValue;
89        });
90
91        listingTagKeyValueList.forEach((item) => {
92            let listingId = item.ListingID;
93            let tagKeyValueId = item.TagKeyValueID;
94            let tag = idLookupTag[tagKeyValueId];
95            if (tag == null) {
96                let tkv = idLookupTagKeyValue[tagKeyValueId];
97                if (tkv != null) {
98                    let tk = idLookupTagKey[tkv.TagKeyId];
99                    let tv = idLookupTagValue[tkv.TagValueId];
100                    if (tk != null && tv != null) {
101                        tag = {
102                            TagKeyValueID: tagKeyValueId,
103                            TagKeyID: tk.TagKeyId,
104                            TagValueID: tv.TagValueId,
105                            TagKey: tk.Name,
106                            TagValue: tv.Name,
107                            Keyword: tv.Name
108                        };
109                        idLookupTag[tagKeyValueId] = tag;
110                        tagList.push(tag);
111                        //--------------------------------------------------------------------------------
112                        let tagGroup = BaseHelper.insertOrGetLookupArr(tagValueLookupTagGroup, tv.Name);
113                        tagGroup.push(tag);
114                        tagGroup = BaseHelper.insertOrGetLookupArr(keywordLookupTagGroup, tv.Name);
115                        tagGroup.push(tag);
116
117                        //---------------------------------------------------------------------------------
118                        //handle features
119                        let tvLower = tv.Name.toLowerCase();
120                        let noneMatch = tvLower.match("none");
121                        let tkLower = tk.Name.toLowerCase();
122                        let featureMatch = tkLower.match("features");
123                        if (
124                            noneMatch == null
125                            && featureMatch != null
126                            && featureMatch.index != null
127                            && featureMatch.index > 0
128                        ) {
129                            let keyword = tk.Name.substr(0, featureMatch.index);
130                            tagGroup = BaseHelper.insertOrGetLookupArr(keywordLookupTagGroup, keyword);
131                            let featureTag = structuredClone(tag);
132                            featureTag.Keyword = keyword
133                            tagGroup.push(featureTag);
134                        }
135
136                    }
137                }
138            }
139            //------------------------------------------------------------------------
140            listingLookupTag[listingId] = tag;
141            let listingList = tagLookupListing[tagKeyValueId];
142            if (listingList == null) {
143                listingList = [];
144                tagLookupListing[tagKeyValueId] = listingList;
145            }
146            listingList.push(listingId);
147        });
148
149        let result = {
150            tagList: tagList,
151            tagValueLookupTagGroup: tagValueLookupTagGroup,
152            idLookupTagKey: idLookupTagKey,
153            idLookupTagKeyValue: idLookupTagKeyValue,
154            idLookupTagValue: idLookupTagValue,
155            listingLookupTag: listingLookupTag,
156            tagLookupListing: tagLookupListing,
157            keywordLookupTagGroup: keywordLookupTagGroup
158        };
159        return result;
160
161    },
162
163
164    setBackgroundImageWithFallback: function (containerElement, mainImagePath, loadingImagePath, errorImagePath) {
165        // Set the loading image initially
166        containerElement.style.backgroundImage = 'url(' + loadingImagePath + ')';
167
168        var img = new Image();
169
170        img.onload = function () {
171            // On successful load, set the main image as the background
172            containerElement.style.backgroundImage = 'url(' + mainImagePath + '), url(' + loadingImagePath + ')';
173        };
174
175        img.onerror = function () {
176            // On error, set the error image as the background
177            containerElement.style.backgroundImage = 'url(' + errorImagePath + '), url(' + loadingImagePath + ')';
178        };
179
180        // Start loading the main image
181        img.src = mainImagePath;
182    },
183
184    //takes in an array of coordinates, and outputs the average positions
185    calculateAverageGeoPosition: function (coords) {
186        let sumX = 0, sumY = 0, sumZ = 0;
187
188        coords.forEach(coord => {
189            // Convert latitude and longitude from degrees to radians
190            let lat = coord.lat * Math.PI / 180;
191            let lng = coord.lng * Math.PI / 180;
192
193            // Convert from spherical to Cartesian coordinates
194            sumX += Math.cos(lat) * Math.cos(lng);
195            sumY += Math.cos(lat) * Math.sin(lng);
196            sumZ += Math.sin(lat);
197        });
198
199        // Compute average Cartesian coordinates
200        let avgX = sumX / coords.length;
201        let avgY = sumY / coords.length;
202        let avgZ = sumZ / coords.length;
203
204        // Convert back to spherical coordinates
205        let avgLng = Math.atan2(avgY, avgX) * 180 / Math.PI;
206        let avgHyp = Math.sqrt(avgX * avgX + avgY * avgY);
207        let avgLat = Math.atan2(avgZ, avgHyp) * 180 / Math.PI;
208
209        // Return the average position
210        return { lat: avgLat, lng: avgLng };
211    },
212
213    calculateMedianPosition: function (coords) {
214        let lats = coords.map(p => p.lat).sort((a, b) => a - b);
215        let lngs = coords.map(p => p.lng).sort((a, b) => a - b);
216
217        let medianLat = lats[Math.floor(lats.length / 2)];
218        let medianLng = lngs[Math.floor(lngs.length / 2)];
219
220        // If even number of elements, average the two middle values
221        if (lats.length % 2 === 0) {
222            medianLat = (medianLat + lats[Math.floor(lats.length / 2) - 1]) / 2;
223            medianLng = (medianLng + lngs[Math.floor(lngs.length / 2) - 1]) / 2;
224        }
225
226        return { lat: medianLat, lng: medianLng };
227    },
228
229    capitalizeFirstLetter: (str) => {
230        return str[0].toUpperCase() + str.substr(1, str.length - 1)
231    },
232
233    displayAddress: (listing) => {
234        if (listing.IdxFeedProviderID == 6 || listing.IdxFeedProviderID == 11 || listing.IdxFeedTypeID == 10 || listing.IdxFeedTypeID == 215) {
235            if (listing.InternetAddressDisplay == 0) {
236                return false;
237            }
238        }
239        return true;
240    },
241    displayMapLink: (listing) => {
242        if (listing.IdxFeedTypeID == 10) {
243            if (listing.MapLink == 0) {
244                return false;
245            }
246        }
247        return true;
248    },
249
250    getHouseSize: (record) => {
251        let result = "--";
252        if (record.IdxFeedID == 54) {
253            return result;
254        }
255        let houseSize = record.HouseSize;
256        if (houseSize != null) {
257            result = houseSize + " sq.ft.";
258        }
259        return result;
260    },
261
262    toTitleCase: (str) => {
263        str = str.toLowerCase();
264        return str.replace(/\b(\w)/g, (match, p1) => p1.toUpperCase());
265    },
266
267    getLotSize: (record) => {
268        let result = "--";
269        let lotSize = record.LotSize
270        if (lotSize != null) {
271            let fLotSize = parseFloat(lotSize);
272            let acres = (fLotSize / 43560).toFixed(2);
273            if (acres < 0.25) {
274                result = fLotSize.toFixed(0) + " sq.ft.";
275            }
276            else {
277                result = acres + " acres";
278            }
279        }
280        return result;
281    },
282
283    getLoadingImg: (style) => {
284        let loadingGif = document.createElement("img");
285        if (style != null) {
286            for (let key in style) {
287                loadingGif.setAttribute("style", style)
288            }
289        }
290        loadingGif.src = "/img/blue-wheel-loading.gif";
291        return loadingGif;
292    },
293
294    yield: async (time = 0) => {
295        return new Promise((resolve, reject) => {
296            window.setTimeout(() => {
297                resolve();
298            }, time)
299        })
300    },
301    //bootstrap table
302    BT: {
303        getRowFromElement: (bt, element) => {
304            let index = BaseHelper.BT.getRowIndexFromElement(element);
305            if (index != null) {
306                return bt.bootstrapTable("getData")[index];
307            }
308            return null;
309        },
310        getRowIndexFromElement: (element) => {
311            let row = BaseHelper.getParentWithNodeNameFromElement(element, "TR");
312            if (row != null) {
313                let index = row.getAttribute("data-index");
314                if (index != null) {
315                    return parseInt(index);
316                }
317            }
318            return null;
319        },
320        getFieldIndexFromElement: (element) => {
321            let row = BaseHelper.getParentWithNodeNameFromElement(element, "TR");
322            let cell = BaseHelper.getParentWithNodeNameFromElement(element, "TD");
323            for (let i = 0; i < row.children.length; i++) {
324                let child = row.children[i];
325                if (child == cell) {
326                    return i;
327                }
328            }
329            return null;
330        }
331    },
332
333    //grabs first parent with given node name.
334    getParentWithNodeNameFromElement: (element, nodeName) => {
335        let current = element
336        do {
337            let currentNodeName = current.nodeName.toUpperCase()
338            if (currentNodeName == nodeName.toUpperCase()) {
339                return current;
340            }
341            current = current.parentElement;
342        } while (current != null);
343        return null;
344    },
345
346    WidgetReadyWait: async (w) => {
347        return new Promise((resolve, reject) => {
348            w.onReady(() => {
349                resolve(w);
350            })
351        })
352    },
353    //WARNINING, THIS FUNCTION DOES NOT HANDLE CIRCUMSTANCES WHERE THE COORDINATES CROSS THE 0 DEGREE 
354    isPolygonInsideRectangle: (rect, poly) => {
355        if (rect == null || rect.length != 5) {
356            return null;
357        }
358        if (poly == null) {
359            return null;
360        }
361        let minX = null;
362        let maxX = null;
363        let minY = null;
364        let maxY = null;
365        ipt = BaseHelper.getPointXY(rect[0]);
366        minX = ipt.x;
367        maxX = ipt.x;
368        minY = ipt.y;
369        maxY = ipt.y;
370        for (let i = 1; i < rect.length; i++) {
371            let pt = BaseHelper.getPointXY(rect[i]);
372            minX = Math.min(minX, pt.x);
373            maxX = Math.max(maxX, pt.x);
374            minY = Math.min(minY, pt.y);
375            maxY = Math.max(maxY, pt.y);
376        }
377        for (let i = 0; i < poly.length; i++) {
378            let pt = BaseHelper.getPointXY(poly[i]);
379            if (
380                pt.x < minX ||
381                pt.x > maxX ||
382                pt.y < minY ||
383                pt.y > maxY
384            ) {
385                return false
386            }
387        }
388        return true;
389    },
390    getNextPoint: (boundary, currentIndex, direction) => {
391        let nextIndex = currentIndex + direction;
392        while (nextIndex < 0) {
393            nextIndex = boundary.length + nextIndex;
394        }
395        while (nextIndex >= boundary.length) {
396            nextIndex = nextIndex - boundary.length;
397        }
398        return {
399            raw: boundary[nextIndex],
400            point: BaseHelper.getPointXY(boundary[nextIndex]),
401            index: nextIndex
402        }
403    },
404    getPointXY: (b) => {
405        try {
406            let parts = b.split(',');
407            return { x: parseFloat(parts[0]), y: parseFloat(parts[1]) };
408        }
409        catch (ex) {
410            throw ex;
411        }
412    },
413    isBoundaryIdentical: (b0, b1) => {
414        if (b0.length == 0 && b1.length == 0) {
415            return true;
416        }
417        if (b0.length != b1.length) {
418            return false;
419        }
420        let i0 = {
421            raw: b0[0],
422            point: BaseHelper.getPointXY(b0[0]),
423            index: 0
424        };
425        let i1 = null;
426        for (let i = 0; i < b1.length; i++) {
427            let ipt = BaseHelper.getPointXY(b1[i]);
428            if (ipt.x == i0.point.x && ipt.y == i0.point.y) {
429                i1 = {
430                    raw: b1[i],
431                    point: ipt,
432                    index: i
433                }
434                break;
435            }
436        }
437        if (i1 == null) {
438            return false;
439        }
440        let direction = 1;
441        let next0 = BaseHelper.getNextPoint(b0, i0.index, 1);
442        let next1 = BaseHelper.getNextPoint(b1, i1.index, 1);
443        if (next0.point.x == next1.point.x && next0.point.y == next1.point.y) {
444            direction = 1;
445        }
446        else {
447            direction = -1
448        }
449        next0 = i0;
450        next1 = i1;
451        for (let i = 0; i < b0.length; i++) {
452            next0 = BaseHelper.getNextPoint(b0, next0.index, 1);
453            next1 = BaseHelper.getNextPoint(b1, next1.index, direction);
454            if (next0.point.x != next1.point.x || next0.point.y != next1.point.y) {
455                return false;
456            }
457        }
458        return true;
459    },
460    isPromise: (p) => {
461        if (
462            p !== null &&
463            typeof p === 'object' &&
464            typeof p.then === 'function' &&
465            typeof p.catch === 'function'
466        ) {
467            return true;
468        }
469        return false;
470    },
471    maxDaysInMonth: {
472        1: 31,
473        2: 29,
474        3: 31,
475        4: 30,
476        5: 31,
477        6: 30,
478        7: 31,
479        8: 31,
480        9: 30,
481        10: 31,
482        11: 30,
483        12: 31,
484    },
485
486    displayException: true,
487
488    isStringNullOrWhiteSpace: function (s) {
489        if (typeof s !== 'string') {
490            //s is not a string, null, or a whitespace, so it returns false
491            return false;
492        } else if (typeof s === 'undefined' || s === null || s.trim() === '') {
493            return true;
494        } else {
495            return false;
496        }
497    },
498
499    isOverflown(element) {
500        return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
501    },
502
503    getTextFromHtml: function (html) {
504        if (html == null) {
505            return "";
506        }
507        let buffer = document.createElement("div");
508        buffer.innerHTML = html;
509        let result = buffer.innerText;
510        return result;
511    },
512    GetBytesFromBlob: async function (blob, includeLength = true) {
513        return new Promise((resolve, reject) => {
514            try {
515                let fileReader = new FileReader();
516                fileReader.onload = function () {
517                    var data = fileReader.result;
518                    var result = new Uint8Array(data);
519                    if (includeLength == true) {
520                        let length = BaseHelper.GetBytesFromInt(result.length);
521                        result = BaseHelper.MergeBytes(length, result);
522                    }
523                    resolve(result);
524                }
525                fileReader.readAsArrayBuffer(blob);
526            }
527            catch (e) {
528                reject(e);
529            }
530        });
531    },
532
533    GetBytesFromInt: function (num) {
534        /*
535        the bytes require an endian check on server to get correct number
536        if (BitConverter.IsLittleEndian)
537        {
538            Array.Reverse(bytes_DataLength);
539        }
540        */
541        arr = new ArrayBuffer(4); // an Int32 takes 4 bytes
542        view = new DataView(arr);
543        view.setUint32(0, num, false); // byteOffset = 0; litteEndian = false
544        return new Uint8Array(arr);
545    },
546
547    GetBytesFromText: function (text, includeLength = true) {
548        let utf8Encode = new TextEncoder();
549        let result = utf8Encode.encode(text)
550        if (includeLength == true) {
551            let length = BaseHelper.GetBytesFromInt(result.length);
552            result = BaseHelper.MergeBytes(length, result);
553        }
554        return result;
555    },
556
557    MergeBytes: function (a, b) {
558        let newLength = a.length + b.length;
559        let result = new Uint8Array(newLength);
560        for (let i = 0; i < a.length; i++) {
561            result[i] = a[i];
562        }
563        for (let i = 0; i < b.length; i++) {
564            result[a.length + i] = b[i];
565        }
566        return result;
567    },
568
569    //requires importing of sweetalert2 library
570    //<link rel="stylesheet" href="/lib/sweetalert2/sweetalert2.min.css" />
571    //<script src="/lib/sweetalert2/sweetalert2.all.min.js"></script>
572    showAlert: function (message, type, timer) {
573        if (type == null) {
574            type = "info";
575        }
576
577        if (timer == null) {
578            timer = 5000;
579        }
580
581        Swal.fire(
582            {
583                position: 'top-end',
584                type: type,
585                title: message,
586                showConfirmButton: false,
587                timer: timer,
588                toast: true
589            });
590    },
591
592    //requires importing of sweetalert2 library
593    //<link rel="stylesheet" href="/lib/sweetalert2/sweetalert2.min.css" />
594    //<script src="/lib/sweetalert2/sweetalert2.all.min.js"></script>
595    showAlert2: function (message, type, timer, position, showConfirmButton) {
596        message = "<div class='text-start p-3'> " + message + "</div>";
597        if (type == null) { //warning, error, success, info
598            type = "info";
599        }
600
601        if (timer == null) {
602            timer = 5000;
603        }
604
605        if (position == null) {
606            position = 'top-end'; //top, top-start, top-end, center, center-start, center-end, bottom, bottom-start, bottom-end
607        }
608
609        if (showConfirmButton == null) {
610            showConfirmButton = false;
611        }
612
613        let options = {
614            position: position,
615            type: type,
616            html: message,
617            showConfirmButton: showConfirmButton,
618            showCloseButton: false,
619            toast: true
620        }
621        if (timer != 0 && timer != null) {
622            options["timer"] = timer;
623        }
624        Swal.fire(options);
625    },
626
627    //requires importing of sweetalert2 library
628    //<link rel="stylesheet" href="/lib/sweetalert2/sweetalert2.min.css" />
629    //<script src="/lib/sweetalert2/sweetalert2.all.min.js"></script>
630    showAlert3: function (message, icon, timer, position, showConfirmButton) {
631        message = "<div class='text-start p-3'> " + message + "</div>";
632        if (icon == null) { //warning, error, success, info, question
633            icon = "info";
634        }
635
636        if (timer == null) {
637            timer = 5000;
638        }
639
640        if (position == null) {
641            position = 'top-end'; //top, top-start, top-end, center, center-start, center-end, bottom, bottom-start, bottom-end
642        }
643
644        if (showConfirmButton == null) {
645            showConfirmButton = false;
646        }
647
648        let options = {
649            position: position,
650            icon: icon,
651            html: message,
652            showConfirmButton: showConfirmButton,
653            showCloseButton: false,
654            toast: true
655        }
656        if (timer != 0 && timer != null) {
657            options["timer"] = timer;
658        }
659        Swal.fire(options);
660    },
661
662    showAlertPopup: function (messageType, element, title, message, placement, timer, showCloseButton) {
663        //alert(element);
664        $(element).popover("dispose");
665
666        if (messageType == null) {
667            messageType = "info";
668        }
669
670        if (timer == null) {
671            timer = 5000;
672        }
673
674        if (placement == null) {
675            placement = "top";
676        }
677
678        let titleStyle = "";
679        let contentStyle = "";
680
681        switch (messageType) {
682            case "error":
683                titleStyle = "<div class='col text-white bg-danger text-center fw-bold fs-5 p-1 m-0'>" + title + "</div>";
684                contentStyle = "<div class='col pt-1 pb-3 fs-6'>" + message + "</div>";
685                if (showCloseButton) {
686                    contentStyle += "<div class='col text-center mt-2'><a href='#' class='btn btn-sm btn-danger btn-rounded close'>Close</a></div>";
687                }
688                break;
689            case "success":
690                titleStyle = "<div class='col text-white bg-success text-center fw-bold fs-5 p-1 m-0'>" + title + "</div>";
691                contentStyle = "<div class='col pt-1 pb-3 fs-6'>" + message + "</div>";
692                if (showCloseButton) {
693                    contentStyle += "<div class='col text-center mt-2'><a href='#' class='btn btn-sm btn-secondary btn-rounded close'>Close</a></div>";
694                }
695                break;
696            case "info":
697                titleStyle = "<div class='col text-center fw-bold fs-5 p-1 m-0'>" + title + "</div>";
698                contentStyle = "<div class='col pt-1 pb-3 fs-6'>" + message + "</div>";
699                if (showCloseButton) {
700                    contentStyle += "<div class='col text-center mt-2'><a href='#' class='btn btn-sm btn-secondary btn-rounded close'>Close</a></div>";
701                }
702                break;
703            case "tooltip":
704                titleStyle = "<div class='col text-white bg-success text-center fw-bold fs-5 p-1 m-0'>" + title + "</div>";
705                contentStyle = "<div class='col pt-1 pb-3 fs-6'>" + message + "</div>";
706                break;
707            default:
708                titleStyle = "<div class='col text-white bg-secondary text-center fw-bold fs-5 p-1 m-0'>" + title + "</div>";
709                contentStyle = "<div class='col pt-1 pb-3 fs-6'>" + message + "</div>";
710                break;
711        }
712
713        $(element).popover({
714            placement: placement,
715            trigger: "focus",
716            html: true,
717            title: titleStyle,
718            content: contentStyle,
719            animation: true
720        });
721        $(element).popover("show");
722        setTimeout(function () {
723            $(element).popover('hide');
724        }, timer);
725    },
726
727    SwapElements: (parent, item1, item2) => {
728        let iIndex1 = null;
729        let iIndex2 = null;
730        if (item1 == null || item2 == null || item1 == item2) {
731            return;
732        }
733        let children = parent.children;
734        for (let i = 0; i < children.length; i++) {
735            if (children[i] == item1) {
736                iIndex1 = i;
737            }
738            if (children[i] == item2) {
739                iIndex2 = i;
740            }
741        }
742        if (iIndex1 == null || iIndex2 == null) {
743            return;
744        }
745
746        let i1 = Math.min(iIndex1, iIndex2);
747        let i2 = Math.max(iIndex1, iIndex2);
748        let one = children[i1];
749        let two = children[i2];
750
751        let next = null;
752        if (i2 + 1 < children.length) {
753            next = children[i2 + 1];
754        }
755        parent.insertBefore(two, one);
756        if (next == null) {
757            parent.append(one)
758        }
759        else {
760            parent.insertBefore(one, next);
761        }
762    },
763
764    containsClass: function (elem, name) {
765        for (let i = 0; i < elem.classList.length; i++) {
766            if (elem.classList[i] == name) {
767                return true;
768            }
769        }
770        return false;
771    },
772
773    nodeListToArr: function (nodeList) {
774        if (BaseHelper.isNullOrUndefined(nodeList)) {
775            return null;
776        }
777        let result = [];
778        nodeList.forEach((n) => {
779            result.push(n);
780        });
781        return result;
782    },
783
784    showLoading: function (message) {
785        Swal.fire({
786            title: message,
787            onBeforeOpen: function () {
788                Swal.showLoading()
789            },
790            onAfterClose: function () {
791                Swal.hideLoading()
792            },
793            showConfirmButton: false,
794            showCloseButton: false,
795            showCancelButton: false
796        })
797    },
798
799    hideLoading: function () {
800        Swal.close();
801    },
802
803    getUniquifiedElementFromId: function (element, IdLookupUniqueId, id) {
804        let uidList = IdLookupUniqueId[id];
805        if (uidList == null || uidList.length == null || uidList.length == 0) {
806            return null;
807        }
808        let uid = uidList[0];
809        return element.querySelector("#" + uid);
810    },
811    getUniquifiedElementsFromId: function (element, IdLookupUniqueId, id) {
812        let uidList = IdLookupUniqueId[id];
813        if (uidList == null || uidList.length == null || uidList.length == 0) {
814            return null;
815        }
816        let result = [];
817        uidList.forEach((uid) => {
818            let e = element.querySelector("#" + uid);
819            if (e != null) {
820                result.push(e);
821            }
822        });
823        return result;
824    },
825    getAllElements: function (element) {
826        let result = [];
827        result.push(element);
828        let children = element.children;
829        for (child of children) {
830            let grandChildren = this.getAllElements(child);
831            for (grandChild of grandChildren) {
832                result.push(grandChild);
833            }
834        };
835        return result;
836    },
837
838    pushQueue: function (queue, value) {
839        queue.push(value);
840    },
841
842    createRequestQueue: function () {
843        let result = {
844            queue: null,
845        };
846        result.pop = () => {
847            let value = null;
848            let queue = result.queue;
849            if (queue != null && queue.length > 0) {
850                value = queue.splice(0, 1)[0];
851            }
852            return value;
853        };
854        result.push = (value) => {
855            let queue = result.queue;
856            if (queue == null) {
857                queue = [];
858            }
859            queue.push(value);
860        };
861        return result;
862    },
863
864    clearRequestQueue: function (requestQueue) {
865        if (requestQueue.queue != null) {
866            requestQueue.queue = [];
867        }
868    },
869
870    createRequest: function (callback, type = null, url = null, data = null) {
871        return {
872            type: type,
873            url: url,
874            data: data,
875            callback: callback
876        }
877    },
878
879    //if type, url, data is null then it just runs the callback.
880    handleRequest: async function (requestQueue, request) {
881        if (requestQueue.queue == null) {
882            requestQueue.queue = [];
883            let currentRequest = request;
884            while (currentRequest != null) {
885                try {
886                    //if one of the request is invalid, dont prevent the rest of the requests from running.
887                    let url = currentRequest.url;
888                    let type = currentRequest.type;
889                    if (type != null) {
890                        type.toLowerCase();
891                    }
892                    let cb = currentRequest.callback;
893                    let data = currentRequest.data;
894                    let jsonResult = null;
895                    if (type == "get") {
896                        jsonResult = await BaseHelper.MakeAJAXGetRequest(url, data);
897                    }
898                    else if (type == "post") {
899                        jsonResult = await BaseHelper.MakeAJAXPostJSONRequest(url, data);
900                    }
901                    else {
902                        jsonResult = null;
903                    }
904                    if (cb != null) {
905                        cb(jsonResult);
906                    }
907                }
908                catch (e) {
909                    //display the exception to debug.
910                    console.log(e);
911                }
912                currentRequest = requestQueue.pop();
913            }
914            requestQueue.queue = null;
915            return true;
916        }
917        else {
918            requestQueue.push(request);
919            return false
920        }
921    },
922    defaultImageInputText: "Choose file",
923    updateBrowseImage: function (files, imageText) {
924        let path = BaseHelper.defaultImageInputText;
925        if (files != null && files.length > 0) {
926            path = files[0].name;
927        }
928        imageText.innerText = path;
929    },
930
931    blurActive: function () {
932        let activeElement = window.document.activeElement;
933        if (activeElement != null) {
934            activeElement.blur();
935        }
936    },
937
938    onImageChange: function (files, image) {
939        if (files && files.length > 0) {
940            let file = files[0];
941            let reader = new FileReader();
942            reader.onload = async (e) => {
943                image.src = reader.result;
944                image.style.display = "inline-block";
945                //this.drawUrl(canvas, reader.result);
946            };
947            reader.readAsDataURL(file);
948        }
949        else {
950            image.style.display = "none";
951        }
952    },
953
954    UniquifyBootstrapNavs: function (element) {
955        let navItems = element.querySelectorAll("a.nav-item");
956        let IdLookupUniqueId = {};
957        navItems.forEach((nItem, index) => {
958            if (nItem.getAttribute("data-uniquified") == 1) {
959                return;
960            }
961            let uuid = BaseHelper.getUuid();
962            let ac = nItem.getAttribute("aria-controls") + uuid;
963            let href = nItem.getAttribute("href");
964
965            let tPane = element.querySelector(href);
966            let current_tpId = tPane.getAttribute("id");
967            let current_nItemId = nItem.getAttribute("id");
968
969            let tpId = current_tpId + uuid;
970            let nItemId = tpId + "Nav";
971
972            tPane.setAttribute("id", tpId);
973            nItem.setAttribute("id", nItemId);
974            nItem.setAttribute("href", "#" + tpId);
975
976
977            tPane.setAttribute("aria-labelledby", ac + "-tab");
978            tPane.setAttribute("data-uniquified", 1);
979            nItem.setAttribute("data-uniquified", 1);
980
981            tpIdList = IdLookupUniqueId[current_tpId];
982            if (tpIdList == null) {
983                tpIdList = [];
984                IdLookupUniqueId[current_tpId] = tpIdList
985            }
986            tpIdList.push(tpId)
987
988            nItemIdList = IdLookupUniqueId[current_nItemId];
989            if (nItemIdList == null) {
990                nItemIdList = [];
991                IdLookupUniqueId[current_nItemId] = nItemIdList
992            }
993            nItemIdList.push(nItemId);
994        });
995        return IdLookupUniqueId;
996    },
997    //{one} = headingOne
998    //{two} = collapseOne
999    //{three} = #{two}
1000    //{four} = accordionExample
1001    //{five} = #{four}
1002    //<div class="accordion" id="{four}">
1003    //  <div class="accordion-item">
1004    //    <h2 class="accordion-header" id="{one}">
1005    //      <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="{three}" aria-expanded="true" aria-controls="{two}">
1006    //        Background Image
1007    //      </button>
1008    //    </h2>
1009    //    <div id="{two}" class="accordion-collapse collapse show" aria-labelledby="{one}" data-bs-parent="{five}">
1010    //      <div class="accordion-body">
1011    //      </div>
1012    //    </div>
1013    //  </div >
1014    //</div>
1015    UniquifyBootstrapAccordion: function (element) {
1016        let aElementList = element.querySelectorAll(".accordion");
1017        let IdLookupUniqueID = {};
1018        aElementList.forEach((aElement) => {
1019            let oFour = BaseHelper.tryGetAttribute(aElement, "id");
1020            let four = oFour + BaseHelper.getUuid();
1021            let five = "#" + four;
1022            BaseHelper.trySetAttribute(aElement, "id", four);
1023            let accordionItemList = aElement.querySelectorAll(".accordion-item");
1024            IdLookupUniqueID[oFour] = four;
1025            accordionItemList.forEach((aItem) => {
1026                let aHeader = aItem.querySelector(".accordion-header");
1027                let aButton = aHeader.querySelector(".accordion-button");
1028                let aCollapse = aItem.querySelector(".accordion-collapse");
1029
1030                let oOne = BaseHelper.tryGetAttribute(aHeader, "id");
1031                let oTwo = BaseHelper.tryGetAttribute(aCollapse, "id");
1032
1033                let one = oOne + BaseHelper.getUuid();
1034                let two = oTwo + BaseHelper.getUuid();
1035                let three = "#" + two;
1036
1037                BaseHelper.trySetAttribute(aHeader, "id", one);
1038                BaseHelper.trySetAttribute(aCollapse, "id", two);
1039                BaseHelper.trySetAttribute(aCollapse, "aria-labelledby", one);
1040                BaseHelper.trySetAttribute(aButton, "aria-controls", two);
1041                BaseHelper.trySetAttribute(aButton, "data-bs-target", three);
1042                BaseHelper.trySetAttribute(aCollapse, "data-bs-parent", five);
1043                IdLookupUniqueID[oOne] = one;
1044                IdLookupUniqueID[oTwo] = two;
1045            })
1046        })
1047        return IdLookupUniqueID;
1048    },
1049    trySetAttribute: (element, name, value) => {
1050        if (element == null || name == null || value == null) {
1051            return;
1052        }
1053        element.setAttribute(name, value);
1054    },
1055    tryGetAttribute: (element, name) => {
1056        if (element == null || name == null) {
1057            return null;
1058        }
1059        return element.getAttribute(name);
1060    },
1061    //one = umnEdit-tab
1062    //two = {one}-pane = umnEdit-tab-pane
1063    //three = #{two} = #umnEdit-tab-pane
1064    //tab1 = umnTab
1065    //tab2 = {tab1}Content = umnTabContent
1066    //<div class="UserManagerNav">
1067    //  <ul class="nav nav-tabs" id="umnTab" role="tablist">
1068    //    <li class="nav-item" role="presentation">
1069    //      <button class="nav-link active" id="{one}" data-bs-toggle="tab" data-bs-target="#{two}" type="button" role="tab" aria-controls="{two}" aria-selected="true">Edit</button>
1070    //    </li>
1071    //  </ul>
1072    //  <div class="tab-content" id="umnTabContent">
1073    //    <div class="tab-pane fade show active" id="{two}" role="tabpanel" aria-labelledby="{one}" tabindex="0">Edit</div>
1074    //  </div>
1075    //</div>
1076    UniquifyBootstrap5Navs: function (element) {
1077        let tabs = element.querySelector(".nav.nav-tabs");
1078        let tabContent = element.querySelector(".tab-content");
1079
1080        let tab1 = tabs.getAttribute("id") + BaseHelper.getUuid();
1081        let tab2 = tab1 + "Content";
1082
1083        let oTab1 = tabs.getAttribute("id");
1084        tabs.setAttribute("id", tab1);
1085        tabs.setAttribute("data-uniquified", 1);
1086
1087        let oTab2 = tabContent.getAttribute("id");
1088        tabContent.setAttribute("id", tab2);
1089        tabContent.setAttribute("data-uniquified", 1);
1090        let navLinks = element.querySelectorAll(".nav-item .nav-link");
1091
1092        let IdLookupUniqueId = {};
1093        IdLookupUniqueId[oTab1] = tab1;
1094        IdLookupUniqueId[oTab2] = tab2;
1095
1096        navLinks.forEach((nLink, index) => {
1097            let uuid = BaseHelper.getUuid();
1098            let paneTarget = nLink.getAttribute("data-bs-target");
1099            let one = nLink.getAttribute("id") + uuid;
1100            let two = one + "-pane";
1101            let three = "#" + two;
1102
1103            let oOne = nLink.getAttribute("id");
1104            nLink.setAttribute("id", one);
1105            nLink.setAttribute("data-uniquified", 1);
1106            nLink.setAttribute("data-bs-target", three);
1107            nLink.setAttribute("aria-controls", two);
1108            let tPane = tabContent.querySelector(paneTarget);
1109
1110            let oTwo = tPane.getAttribute("id");
1111            tPane.setAttribute("id", two);
1112            tPane.setAttribute("data-uniquified", 1);
1113            tPane.setAttribute("aria-labelledby", one);
1114
1115            IdLookupUniqueId[oOne] = one;
1116            IdLookupUniqueId[oTwo] = two;
1117        });
1118        return IdLookupUniqueId;
1119    },
1120
1121
1122    getBootstrapRowIndex: function (element) {
1123        let currentElement = element;
1124        while (currentElement != null) {
1125            let tag = currentElement.tagName;
1126            let dIndex = currentElement.getAttribute("data-index");
1127            if (tag.toUpperCase() == "TR" && dIndex != null) {
1128                return dIndex;
1129            }
1130            currentElement = currentElement.parentElement;
1131        }
1132        return null;
1133    },
1134
1135    getBootstrapRowFromElement: function (bt, element) {
1136        let rowIndex = BaseHelper.getBootstrapRowIndex(element);
1137        if (rowIndex != null) {
1138            return bt.bootstrapTable("getData")[rowIndex];
1139        }
1140        return null;
1141    },
1142
1143    //makes the ids under the element unique and returns a way to lookup the unique ids.
1144    //the unique ids are in a list in case the id was not actually unique
1145    UniquifyIds: function (element) {
1146        let IdLookupUniqueId = {}
1147        let allElements = this.getAllElements(element);
1148        allElements.forEach(e => {
1149            let id = e.getAttribute("id");
1150            let uniquified = e.getAttribute("data-uniquified");
1151            if (uniquified) {
1152                return;
1153            }
1154            if (id != null) {
1155                let uuid = this.getUuid()
1156                let uniqueId = id + "-" + uuid;
1157                let uidList = IdLookupUniqueId[id];
1158                if (uidList == null) {
1159                    uidList = [];
1160                    IdLookupUniqueId[id] = uidList;
1161                }
1162                e.setAttribute("id", uniqueId);
1163                e.setAttribute("data-uniquified", true);
1164                uidList.push(uniqueId);
1165            }
1166        })
1167        return IdLookupUniqueId;
1168    },
1169
1170    //usage: BaseHelper.dollarFormatter.format({amount});
1171    dollarFormatter: new Intl.NumberFormat('en-US', {
1172        style: 'currency',
1173        currency: 'USD',
1174        minimumFractionDigits: 0
1175
1176        // These options are needed to round to whole numbers if that's what you want.
1177        //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1)
1178        //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501)
1179    }),
1180
1181    //takes in date from the database and output the locale date.
1182    //returns null if its unparsable
1183    dbDateToLocale: function (value) {
1184        if (value == null || value.trim() == "") {
1185            return null;
1186        }
1187        let dt = new Date(value);
1188        if (isNaN(dt)) {
1189            return null;
1190        }
1191        return dt.toLocaleDateString();
1192    },
1193
1194    getUuid: function () {
1195        return this.uuid++;
1196    },
1197
1198    getSubscriberName: function (fullName, firstName, lastName) {
1199        if (fullName == null) {
1200            return firstName + " " + lastName
1201        }
1202        else {
1203            return fullName
1204        }
1205    },
1206
1207    GenerateGlobalFunction: function (f) {
1208        let functionName = "BaseHelperGeneratedGlobalFunction" + this.getUuid();
1209        window[functionName] = f;
1210        return functionName;
1211    },
1212    EncodeObjectToHtmlData: function (data) {
1213        if (data == null) {
1214            return null;
1215        }
1216        else {
1217            urlData = "";
1218            for (let key in data) {
1219                let value = data[key];
1220                urlData += key + "=" + encodeURIComponent(value) + "&";
1221            }
1222            return urlData.substr(0, urlData.length - 1);
1223        }
1224    },
1225
1226    ExportTable: function (table, name, type) {
1227        let wb = XLSX.utils.table_to_book(table, { sheet: name });
1228        XLSX.writeFile(wb, name + "." + type);
1229    },
1230
1231    ExportArray: function (arr, name, type) {
1232        let wb = XLSX.utils.book_new();
1233        let sheet = XLSX.utils.json_to_sheet(arr);
1234        XLSX.utils.book_append_sheet(wb, sheet, name)
1235        XLSX.writeFile(wb, name + "." + type);
1236    },
1237
1238    MakeAJAXGetRequest: async function (url, data = null, onProgress = null, handleException = true) {
1239        let urlData = this.EncodeObjectToHtmlData(data);
1240        let query = url;
1241        if (urlData != null) {
1242            query += "?" + urlData;
1243        }
1244        return new Promise((resolve, reject) => {
1245            let xhttp = new XMLHttpRequest();
1246            xhttp.onload = function () {
1247                if (handleException) {
1248                    BaseHelper.HandleException(this.responseText);
1249                }
1250                resolve(this.responseText);
1251            }
1252            xhttp.onerror = function () {
1253                reject("ajax failed!");
1254            };
1255            if (onProgress != null) {
1256                xhttp.onprogress = onProgress;
1257            }
1258            xhttp.open("GET", query);
1259            xhttp.send();
1260        })
1261    },
1262
1263    GetFormData: function (data) {
1264        let result = new FormData();
1265        for (let key in data) {
1266            if (data[key] instanceof Blob) {
1267                result.append(key, data[key], data[key].name)
1268            }
1269            else {
1270                result.append(key, data[key])
1271            }
1272
1273        }
1274        return result;
1275    },
1276
1277    HandleException: function (response) {
1278        if (!BaseHelper.displayException) {
1279            return false;
1280        }
1281        try {
1282            let rsp = JSON.parse(response);
1283            if (rsp.Type != null) {
1284                if (rsp.Type == "AuthenticationFailedException") {
1285                    window.location = "/login";
1286                    return true;
1287                }
1288                else if (rsp.Type == "Exception") {
1289                    alert(rsp.Message);
1290                    return true;
1291                }
1292            }
1293        }
1294        catch (e) {
1295            //do nothing
1296        }
1297        return false;
1298    },
1299    //Converts data to json and sends it to server.
1300    //obtain parameters data on the server by reading from the form request "HttpContext.Request.Form[{ParameterName}]" or by adding [FromForm] attribute;
1301    MakeAJAXPostFormRequest: async function (url, data = null, onProgress = null, handleException = true) {
1302        return new Promise((resolve, reject) => {
1303            let xhttp = new XMLHttpRequest();
1304            xhttp.onload = function () {
1305                if (handleException) {
1306                    BaseHelper.HandleException(this.responseText);
1307                }
1308                resolve(this.responseText);
1309            }
1310            xhttp.onerror = function () {
1311                reject("ajax failed!");
1312            };
1313            if (onProgress != null) {
1314                xhttp.onprogress = onProgress;
1315            };
1316            xhttp.open("post", url);
1317            xhttp.send(this.EncodeObjectToHtmlData(data));
1318        })
1319    },
1320
1321    MakeAJAXPostFormDataRequest: async function (url, data, onProgress = null, handleException = true) {
1322        return new Promise((resolve, reject) => {
1323            let fData = BaseHelper.GetFormData(data);
1324            let xhttp = new XMLHttpRequest();
1325            xhttp.onload = function () {
1326                if (handleException) {
1327                    BaseHelper.HandleException(this.responseText);
1328                }
1329                resolve(this.responseText);
1330            }
1331            xhttp.onerror = function () {
1332                reject("ajax failed!");
1333            };
1334            if (onProgress != null) {
1335                //xhttp.onprogress = onProgress;
1336                xhttp.upload.onprogress = onProgress;
1337            };
1338            xhttp.open("post", url);
1339            xhttp.send(fData);
1340        })
1341    },
1342
1343    MakeAJAXPostBinaryRequest: async function (url, bytes, onProgress = null, handleException = true) {
1344        return new Promise((resolve, reject) => {
1345            let xhttp = new XMLHttpRequest();
1346            xhttp.onload = function () {
1347                if (handleException) {
1348                    BaseHelper.HandleException(this.responseText);
1349                }
1350                resolve(this.responseText);
1351            }
1352            xhttp.onerror = function () {
1353                reject("ajax failed!");
1354            };
1355            if (onProgress != null) {
1356                xhttp.onprogress = onProgress;
1357            };
1358            xhttp.open("post", url);
1359            xhttp.setRequestHeader("Content-Type", "application/octet-stream");
1360            xhttp.send(bytes);
1361        })
1362    },
1363
1364
1365    DeserializeDataTableToObjects: (dt) => {
1366        try {
1367            const dataArray = dt;
1368
1369            // Extract the column names from the first row
1370            const columnNames = dataArray[0];
1371
1372            // Convert the rest of the rows into an array of objects
1373            const objectsArray = dataArray.slice(1).map(row => {
1374                const obj = {};
1375                columnNames.forEach((columnName, index) => {
1376                    obj[columnName] = row[index];
1377                });
1378                return obj;
1379            });
1380
1381            return objectsArray;
1382        } catch (error) {
1383            console.error('Error parsing JSON:', error);
1384            return null; // or handle the error as needed
1385        }
1386    },
1387
1388    //Converts data to json and sends it to server.
1389    //obtain parameters data on the server by reading from the context request body "(new StreamReader(HttpContext.Request.Body)).ReadToEnd()" and parsing the result as JSON;
1390    MakeAJAXPostJSONRequest: async function (url, data = null, handleException = true) {
1391        return new Promise((resolve, reject) => {
1392            let xhttp = new XMLHttpRequest();
1393            xhttp.onload = function () {
1394                if (handleException) {
1395                    BaseHelper.HandleException(this.responseText);
1396                }
1397                resolve(this.responseText);
1398            }
1399            xhttp.onerror = function () {
1400                reject("ajax failed!");
1401            };
1402            xhttp.open("post", url);
1403            xhttp.setRequestHeader("Content-type", "application/json");
1404            xhttp.send(JSON.stringify(data));
1405        })
1406    },
1407
1408    MergeObjects: function (a, b) {
1409        let result = {};
1410        for (key in a) {
1411            result[key] = a[key];
1412        };
1413        for (key in b) {
1414            result[key] = b[key];
1415        };
1416        return result;
1417    },
1418
1419    MergeArrays: function (a, b) {
1420        let result = []
1421        a.forEach((e) => {
1422            result.push(e);
1423        })
1424        b.forEach((e) => {
1425            result.push(e);
1426        })
1427        return result;
1428    },
1429
1430    MergeIntoObject: function (obj, a) {
1431        if (a == null) {
1432            return obj;
1433        }
1434        for (key in a) {
1435            obj[key] = a[key];
1436        };
1437        return obj;
1438    },
1439
1440    asyncWait:async function(t){
1441        return new Promise((resolve)=>{
1442            setTimeout(()=>{
1443                resolve(true);
1444            }, t)
1445        });
1446    },
1447
1448    CopyToClipboard: function (element) {
1449        if (element == null) {
1450            return false
1451        }
1452        element.select();
1453        if (!document.execCommand("copy")){
1454            return false;
1455        }
1456        return true;
1457    },
1458
1459    GetHousePhotoUrl: function (id = null) {s
1460        let url = "/content/housephoto";
1461        if (id != null) {
1462            url += "?housephotoid=" + id;
1463        }
1464        return url;
1465    },
1466
1467    CreateDialog: function (dialogClassName, title, width, height, animationTimer = 250, options = {}) {
1468        let baseOptions = {
1469            title: title,
1470            width: width,
1471            height: height,
1472            autoOpen: false
1473        }
1474        if (animationTimer != 0 && options.show == null && options.hide == null) {
1475            options.show = {
1476                effect: "blind",
1477                duration: animationTimer
1478            };
1479            options.hide = {
1480                effect: "blind",
1481                duration: animationTimer
1482            };
1483        }
1484        let resultOptions = this.MergeObjects(baseOptions, options);
1485
1486        let dialogElement = document.createElement("div");
1487        dialogElement.classList.add(dialogClassName);
1488        document.body.appendChild(dialogElement);
1489        let dialog = {
1490            element: $(dialogElement).dialog(resultOptions),
1491            cleanup: function () {
1492                $(dialogElement).dialog("destroy");
1493                dialogElement.remove();
1494            }
1495        };
1496        return dialog;
1497    },
1498
1499    getWindowSides: function () {
1500        let top = window.scrollY;
1501        let bottom = top + window.innerHeight;
1502        let left = window.scrollX;
1503        let right = left + window.innerWidth
1504
1505        return {
1506            top: top,
1507            bottom: bottom,
1508            left: left,
1509            right: right
1510        }
1511    },
1512
1513    getBoundSides: function (clientRect) {
1514        let top = clientRect.y + window.scrollY;
1515        let bottom = top + clientRect.height;
1516        let left = clientRect.x + window.scrollX;
1517        let right = left + clientRect.width;
1518        return {
1519            top: top,
1520            bottom: bottom,
1521            left: left,
1522            right: right
1523        }
1524    },
1525
1526    isScrolledIntoView: function (container, elem) {
1527        let docViewBounds = container.getBoundingClientRect();
1528        let elemBounds = elem.getBoundingClientRect();
1529        if (docViewBounds.width == 0 || docViewBounds.height == 0) {
1530            return false;
1531        }
1532        if (elemBounds.width == 0 || elemBounds.height == 0) {
1533            return false;
1534        }
1535        let containerSides = BaseHelper.getBoundSides(docViewBounds);
1536        let elemSides = BaseHelper.getBoundSides(elemBounds);
1537        return BaseHelper.isScrolledIntoViewSides(containerSides, elemSides);
1538    },
1539
1540    isScrolledIntoViewSides: function (containerSides, elemSides) {
1541        if (containerSides.left == containerSides.right || containerSides.top == containerSides.bottom) {
1542            return false;
1543        }
1544        if (elemSides.left == elemSides.right || elemSides.top == elemSides.bottom) {
1545            return false;
1546        }
1547        return ((elemSides.bottom >= containerSides.top) && (elemSides.top <= containerSides.bottom));
1548    },
1549
1550    //------------------------------------------------------------------------------------------------------
1551    //image helpers
1552
1553    //set img width/height based on parent node
1554    //parent node must be visible.
1555    calculateFillType: function (containerWidth, containerHeight, imgWidth, imgHeight) {
1556        let containerRatio = containerWidth / containerHeight;
1557        let imgRatio = imgWidth / imgHeight;
1558
1559        if (imgRatio < containerRatio) {
1560            return "tall";
1561        } else {
1562            return "wide";
1563        }
1564    },
1565
1566    getImageFillType: function (container, img) {
1567        let containerWidth = container.clientWidth;
1568        let containerHeight = container.clientHeight;
1569        let imgWidth = img.width;
1570        let imgHeight = img.height;
1571
1572        return BaseHelper.calculateFillType(containerWidth, containerHeight, imgWidth, imgHeight);
1573    },
1574
1575    setImgWidthHeight: function (img, width, height) {
1576        let nw = img.naturalWidth;
1577        let nh = img.naturalHeight;
1578        let ratio = nw / nh;
1579        if (ratio >= 1) {
1580            img.style.width = width + "px";
1581            img.style.height = "auto";
1582        }
1583        else {
1584            img.style.width = "auto";
1585            img.style.height = height + "px";
1586        }
1587    },
1588    //------------------------------------------------------------------------------------------------------
1589
1590    HasParent: function (parent, element) {
1591        let currentElement = element;
1592        while (currentElement != null) {
1593            if (currentElement == parent) {
1594                return true;
1595            }
1596            currentElement = currentElement.parentElement;
1597        }
1598        return false;
1599    },
1600
1601    post: async function (url, request) {
1602        return new Promise((resolve) => {
1603            $.post("/api/Listing/GetSearchAreaCount", request).done(function (data) {
1604                resolve(data);
1605            });
1606        });
1607    },
1608
1609    getValueFromBoolean: function (b) {
1610        if (b === 0 || b === 1) {
1611            return b;
1612        }
1613        if (b == true) {
1614            return 1;
1615        }
1616        else if (b == false) {
1617            return 0;
1618        }
1619        else if (typeof b === 'string') {
1620            bVal = b.trim().toUpperCase();
1621            if (
1622                bVal == "TRUE" ||
1623                bVal == "Y" ||
1624                bVal == "YES"
1625            ) {
1626                return 1;
1627            }
1628            else if (
1629                bVal == "FALSE" ||
1630                bVal == "N" ||
1631                bVal == "NO"
1632            ) {
1633                return 0;
1634            }
1635        }
1636        return null;
1637    },
1638
1639    /* buttons contains an array of text which is displayed on the buttons
1640     * ["button0", "button1", "button2"]
1641     * the buttons array that is returned should return the buttons in the same order that was listed in the array
1642     */
1643
1644    getRange: function (min, max, symbol, after =false) {
1645        let formatNumber = num => num != null ? Number(num).toLocaleString() : null;
1646        if (symbol == null) {
1647            if (min != null && max == null) {
1648                return formatNumber(min) + "+";
1649            } else if (min != null && max != null) {
1650                return formatNumber(min) + " - " + formatNumber(max);
1651            } else if (min == null && max != null) {
1652                return '<' + formatNumber(max);
1653            } else {
1654                return null;
1655            }
1656        }
1657        else {
1658            if (after) {
1659                if (min != null && max == null) {
1660                    return formatNumber(min) + symbol + "+";
1661                } else if (min != null && max != null) {
1662                    return formatNumber(min) + symbol + " - " + formatNumber(max) + symbol;
1663                } else if (min == null && max != null) {
1664                    return '<' + formatNumber(max) + symbol;
1665                } else {
1666                    return null;
1667                }
1668            }
1669            else {
1670                if (min != null && max == null) {
1671                    return symbol + formatNumber(min) + "+";
1672                } else if (min != null && max != null) {
1673                    return symbol + formatNumber(min) + " - " + symbol + formatNumber(max);
1674                } else if (min == null && max != null) {
1675                    return `< ${symbol}` + formatNumber(max);
1676                } else {
1677                    return null;
1678                }
1679            }
1680        }
1681        
1682    },
1683
1684    ExtractDomain: function extractDomain(input) {
1685        if (typeof input !== 'string') return null;
1686        input = input.trim();
1687        if (!input) return null;
1688
1689        let url;
1690        try {
1691            // If the string doesn't start with a protocol, prepend "http://"
1692            if (!/^https?:\/\//i.test(input)) {
1693                url = new URL('http://' + input);
1694            } else {
1695                url = new URL(input);
1696            }
1697        } catch (e) {
1698            // If URL construction fails, it's not a valid domain/URL.
1699            return null;
1700        }
1701
1702        // Extract the hostname (e.g., "www.test.com" or "test.com")
1703        const hostname = url.hostname;
1704        // Split the hostname by the dot.
1705        const parts = hostname.split('.');
1706        // If there aren't at least two parts, it's not a valid domain.
1707        if (parts.length < 2) return null;
1708
1709        // Return the second-to-last part, which should be the domain name.
1710        return parts[parts.length - 2];
1711    },
1712
1713    CreateModal: async function (modalClassName, title, maxWidth = null, buttons = null) {
1714        let modalElement = document.createElement("div");
1715        let modalMarkup = await this.MakeAJAXGetRequest("/Template/Modal");
1716        modalElement.classList.add(modalClassName);
1717        modalElement.innerHTML = modalMarkup;
1718
1719        let modal = modalElement.querySelector(".modal");
1720        let modalDialog = modalElement.querySelector(".modal-dialog");
1721        let meHeader = modalElement.querySelector(".modal .modal-header");
1722        let meTitle = modalElement.querySelector(".modal .modal-header .modal-title");
1723        let meBody = modalElement.querySelector(".modal .modal-body");
1724        let meFooter = modalElement.querySelector(".modal-footer");
1725        meTitle.innerHTML = title;
1726        if (maxWidth != null) {
1727            modalDialog.style.maxWidth = maxWidth;
1728        }
1729
1730        let result = {
1731            element: modalElement,
1732            modal: modal,
1733            title: meTitle,
1734            body: meBody,
1735            footer: meFooter,
1736            dialog: modalDialog,
1737            cleanup: () => {
1738                modalElement.remove();
1739            }
1740        };
1741
1742        if (title == null) {
1743            meHeader.parentElement.removeChild(meHeader);
1744        }
1745        else {
1746            result.header = meHeader;
1747            result.title = meTitle;
1748        }
1749        let btnList = null;
1750        if (
1751            buttons != null &&
1752            buttons.length != null &&
1753            buttons.length > 0
1754        ) {
1755            btnList = []
1756            meFooter.innerHTML = "";
1757            buttons.forEach((text) => {
1758                let btn = document.createElement("button");
1759                btn.classList.add("btn");
1760                btn.classList.add("btn-info");
1761                btn.innerText = text;
1762                meFooter.appendChild(btn);
1763                btnList.push(btn);
1764            });
1765            result.buttons = btnList;
1766        }
1767        document.body.appendChild(modalElement);
1768        return Promise.resolve(result);
1769    },
1770
1771    ToggleScroll: function (scroll) {
1772        if (scroll == true) {
1773            document.body.style.height = null;
1774            document.body.style.overflow = null;
1775        }
1776        else {
1777            document.body.style.height = "100%";
1778            document.body.style.overflow = "hidden";
1779        }
1780    }
1781};

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.