PageSourceSearch

https://assets.coolpopup.com/themes/cptheme/assets/js/custom.js?v=5321

js coolpopup.com collected 2026-09-28 06:10:45 UTC 41,500 bytes, 611 lines download raw bytes

1
2
3'use strict';
4
5/* File upload handler */
6document.querySelectorAll('[data-file-input-wrapper-size-limit]').forEach(element => {
7    element.querySelector('input[type="file"]').addEventListener('change', event => {
8        let input = event.currentTarget;
9
10        if(input.files && input.files[0] && (input.files[0].size / 1024 / 1024) > element.getAttribute('data-file-input-wrapper-size-limit')) {
11            alert(element.getAttribute('data-file-input-wrapper-size-limit-error'));
12            input.value = '';
13            event.stopPropagation();
14        }
15    })
16});
17
18/* Image upload preview handler */
19document.querySelectorAll('[data-file-image-input-wrapper]').forEach(element => {
20    element.querySelector('input[type="file"]').addEventListener('change', event => {
21        let input = event.currentTarget;
22        let preview_image = element.querySelector('[id*="_preview"] img');
23
24        /* Preview image */
25        if(input.files && input.files[0]) {
26            let reader = new FileReader();
27
28            reader.onload = event => {
29                /* Display preview wrapper */
30                element.querySelector('[id*="_preview"]').classList.remove('d-none');
31
32                /* Mark it as no default */
33                if(!preview_image.getAttribute('src') && !preview_image.getAttribute('data-src')) {
34                    preview_image.setAttribute('data-no-default-src', 'yes');
35                }
36
37                /* Make sure to save the default src */
38                if(preview_image.getAttribute('src') && !preview_image.getAttribute('data-src') && !preview_image.getAttribute('data-no-default-src')) {
39                    preview_image.setAttribute('data-src', preview_image.getAttribute('src'))
40                }
41
42                /* Display new preview */
43                preview_image.setAttribute('src', event.target.result);
44                preview_image.classList.remove('d-none');
45
46                /* Remove selected file handler */
47                element.querySelector('[id$="_remove_selected_file_wrapper"]').classList.remove('d-none');
48
49                /* Main remove wrapper */
50                element.querySelector('[id$="_remove_wrapper"]').classList.add('d-none');
51
52                /* Disable clicks */
53                element.querySelector('[id*="_preview"] a').style.pointerEvents = 'none';
54            };
55
56            reader.readAsDataURL(input.files[0]);
57        }
58
59        /* Remove image preview / fallback to original */
60        else {
61            if(preview_image.getAttribute('data-src')) {
62                preview_image.setAttribute('src', preview_image.getAttribute('data-src'));
63
64                /* Enable clicks */
65                element.querySelector('[id*="_preview"] a').style.pointerEvents = 'inherit';
66            } else {
67                /* Hide preview wrapper */
68                element.querySelector('[id*="_preview"]').classList.add('d-none');
69                preview_image.classList.add('d-none');
70
71                /* Remove image link */
72                preview_image.setAttribute('src', '')
73            }
74
75            /* Remove selected file handler */
76            element.querySelector('[id$="_remove_selected_file_wrapper"]').classList.add('d-none');
77
78            /* Main remove wrapper */
79            if(preview_image.getAttribute('src')) {
80                element.querySelector('[id$="_remove_wrapper"]').classList.remove('d-none');
81            }
82        }
83    });
84
85    /* Handle remove checkbox */
86    element.querySelector('[id$="_remove"]').addEventListener('change', event => {
87        let is_checked = event.currentTarget.checked;
88
89        if(is_checked) {
90            element.querySelector('input[type="file"]').classList.add('container-disabled');
91            element.querySelector('[id*="_preview"]').classList.add('container-disabled');
92        } else {
93            element.querySelector('input[type="file"]').classList.remove('container-disabled');
94            element.querySelector('[id*="_preview"]').classList.remove('container-disabled');
95        }
96    });
97
98    /* Handle remove selected file */
99    element.querySelector('[id$="_remove_selected_file"]').addEventListener('click', event => {
100        let input = event.currentTarget.closest('.row').querySelector('input');
101        input.value = '';
102        input.dispatchEvent(new Event('change'));
103    });
104});
105
106/* Global type handler */
107let type_handler = (selector, data_key, matching_selector = '=') => {
108    if(!document.querySelector(selector)) {
109        return;
110    }
111
112    let type = null;
113    if(['radio', 'checkbox'].includes(document.querySelector(selector).type)) {
114        type = document.querySelector(`${selector}:checked`) ? document.querySelector(`${selector}:checked`).value : null;
115    } else {
116        type = document.querySelector(selector).value;
117    }
118
119    /* Hide all elements that do not match the current value of the selector */
120    document.querySelectorAll(`[${data_key}]:not([${data_key}${matching_selector}"${type}"])`).forEach(element => {
121        element.classList.add('d-none');
122        let input = element.querySelector('input,select,textarea');
123
124        if(input) {
125            if(input.getAttribute('required')) {
126                input.setAttribute('data-is-required', 'true');
127            }
128            input.setAttribute('disabled', 'disabled');
129            input.removeAttribute('required');
130        }
131    });
132
133    /* Display all elements that do match the current value of the selector */
134    document.querySelectorAll(`[${data_key}${matching_selector}"${type}"]`).forEach(element => {
135        element.classList.remove('d-none');
136        let input = element.querySelector('input,select,textarea');
137
138        if(input) {
139            input.removeAttribute('disabled');
140            if(input.getAttribute('data-is-required')) {
141                input.setAttribute('required', 'required')
142            }
143        }
144    });
145}
146
147/* Temporarily disable the form submission button to avoid multiple submissions */
148document.querySelectorAll('[type="submit"][name="submit"]:not([data-is-ajax])').forEach(element => {
149    element.addEventListener('click', event => {
150        let button = event.currentTarget;
151
152        /* Disable the button */
153        button.classList.add('disabled');
154        button.classList.add('container-disabled-simple');
155
156        /* Save the current button text */
157        let current_button_text = button.innerText;
158
159        /* Show a loading spinner instead of the text */
160        button.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"></div>';
161
162        /* Go back to the button default state */
163        setTimeout(() => {
164            button.classList.remove('disabled');
165            button.classList.remove('container-disabled-simple');
166            button.innerText = current_button_text;
167        }, 2500);
168
169    });
170});
171
172/* Enable tooltips everywhere */
173let tooltips_initiate = () => {
174    if(typeof $ == 'function') {
175        let tooltips = $('[data-toggle="tooltip"],[data-tooltip]');
176        tooltips.tooltip('dispose');
177        tooltips.tooltip({ boundary: 'window' });
178
179        $('[data-tooltip-hide-on-click]').on('click', event => {
180            $(event.currentTarget).tooltip('hide');
181        });
182    }
183
184    window.addEventListener('beforeprint', () => {
185        $('[data-toggle="tooltip"]').tooltip('hide');
186    });
187}
188
189tooltips_initiate();
190
191if(typeof $ == 'function') {
192    $('.table-responsive table .dropdown').on('show.bs.dropdown', function () {
193        $(this).find('.dropdown-menu').appendTo('body');
194    }).on('hide.bs.dropdown', function () {
195        $(this).find('.dropdown-menu').appendTo($(this));
196    });
197}
198
199
200
201/* Helpful functions */
202const pause_submit_button = element => {
203
204    /* Check for full form blackout */
205    if(element.tagName == 'FORM') {
206        /* Make sure form is position relative */
207        element.classList.add('position-relative');
208
209        /* Add overlay to form */
210        let overlay = document.createElement('div');
211        overlay.classList.add('loading-overlay');
212        overlay.innerHTML = '<div class="spinner-border spinner-border-lg" role="status"></div>';
213
214        element.prepend(overlay);
215
216        /* Button */
217        element = element.querySelector('button[type="submit"]');
218    }
219
220    /* Disable the button */
221    element.setAttribute('disabled', 'disabled');
222
223    /* Save the current button text */
224    element.setAttribute('data-inner-text', btoa(unescape(encodeURIComponent(element.innerHTML))));
225
226    /* Show a loading spinner instead of the text */
227    element.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"></div>';
228}
229
230const enable_submit_button = element => {
231
232    /* Check for full form blackout */
233    if(element.tagName == 'FORM') {
234        /* Make sure form is position relative */
235        element.classList.remove('position-relative');
236
237        /* Remove overlay from form */
238        element.querySelector('.loading-overlay').remove();
239
240        /* Button */
241        element = element.querySelector('button[type="submit"]');
242    }
243
244    /* Enable the button */
245    element.removeAttribute('disabled');
246
247    /* Show the original button text */
248    element.innerHTML = decodeURIComponent(escape(atob(element.getAttribute('data-inner-text'))));
249}
250
251const display_notifications = (messages, type, selector) => {
252    let html = '';
253
254    if(typeof  messages === 'string') {
255        messages = [messages];
256    }
257
258    let icon = null;
259    switch(type) {
260        case 'success': icon = 'fa-check-circle'; break;
261        case 'error': icon = 'fa-times-circle'; break;
262        case 'info': icon = 'fa-circle-info'; break;
263    }
264
265    type = type == 'error' ? 'danger' : type;
266
267    for(let message of messages) {
268        if(message.trim() == '') continue;
269
270        html += `
271            <div class="alert alert-${type} altum-animate altum-animate-fill-none altum-animate-fade-in">
272                <button type="button" class="close ml-2" data-dismiss="alert">&times;</button>
273                <i class="fas fa-fw ${icon} mr-2"></i> ${message}
274            </div>`;
275    }
276
277    selector.innerHTML = html;
278};
279
280const redirect = (path, is_full_url = false) => {
281    window.location.href = is_full_url ? path : `${url}${path}`;
282};
283
284const number_format = (number, decimals, dec_point = '.', thousands_point = ',') 
284=> {
285
286    if(number == null || !isFinite(number)) {
287        throw new TypeError('number is not valid');
288    }
289
290    if(!decimals) {
291        let len = number.toString().split('.').length;
292        decimals = len > 1 ? len : 0;
293    }
294
295    number = parseFloat(number).toFixed(decimals);
296
297    number = number.replace('.', dec_point);
298
299    let splitNum = number.split(dec_point);
300    splitNum[0] = splitNum[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousands_point);
301    number = splitNum.join(dec_point);
302
303    return number;
304};
305
306const nr = (number, decimals = 0, display_decimals_if_zero = false) => {
307    if(!display_decimals_if_zero && decimals > 0) {
308        decimals = Math.floor(number) != number ? decimals : 0;
309    }
310
311    return number_format(number, decimals, decimal_point, thousands_separator);
312};
313
314const get_cookie = name => {
315    let v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
316
317    return v ? v[2] : null;
318};
319
320const set_cookie = (name, value, days, path) => {
321    let d = new Date;
322    d.setTime(d.getTime() + 24*60*60*1000*days);
323
324    document.cookie = `${name}=${value};path=${path};expires=${d.toGMTString()}`;
325};
326
327const delete_cookie = (name, path) => {
328    set_cookie(name, '', -1, path);
329};
330
331const get_slug = (string, delimiter = '-', lowercase = true) => {
332    let emojis = ['1F600','1F603','1F604','1F601','1F606','1F605','1F923','1F602','1F642','1F643','1F609','1F60A','1F607','1F970','1F60D','1F929','1F618','1F617','263A','1F61A','1F619','1F60B','1F61B','1F61C','1F92A','1F61D','1F911','1F917','1F92D','1F92B','1F914','1F910','1F928','1F610','1F611','1F636','1F60F','1F612','1F644','1F62C','1F925','1F60C','1F614','1F62A','1F924','1F634','1F637','1F912','1F915','1F922','1F92E','1F927','1F975','1F976','1F974','1F635','1F92F','1F920','1F973','1F60E','1F913','1F9D0','1F615','1F61F','1F641','2639','1F62E','1F62F','1F632','1F633','1F97A','1F626','1F627','1F628','1F630','1F625','1F622','1F62D','1F631','1F616','1F623','1F61E','1F613','1F629','1F62B','1F971','1F624','1F621','1F620','1F92C','1F608','1F47F','1F480','2620','1F4A9','1F921','1F479','1F47A','1F47B','1F47D','1F47E','1F916','1F63A','1F638','1F639','1F63B','1F63C','1F63D','1F640','1F63F','1F63E','1F648','1F649','1F64A','1F48B','1F48C','1F498','1F49D','1F496','1F497','1F493','1F49E','1F495','1F49F','2763','1F494','2764','1F9E1','1F49B','1F49A','1F499','1F49C','1F90E','1F5A4','1F90D','1F4AF','1F4A2','1F4A5','1F4AB','1F4A6','1F4A8','1F573','1F4A3','1F4AC','1F441','FE0F','200D','1F5E8','FE0F','1F5E8','1F5EF','1F4AD','1F4A4','1F44B','1F91A','1F590','270B','1F596','1F44C','1F90F','270C','1F91E','1F91F','1F918','1F919','1F448','1F449','1F446','1F595','1F447','261D','1F44D','1F44E','270A','1F44A','1F91B','1F91C','1F44F','1F64C','1F450','1F932','1F91D','1F64F','270D','1F485','1F933','1F4AA','1F9BE','1F9BF','1F9B5','1F9B6','1F442','1F9BB','1F443','1F9E0','1F9B7','1F9B4','1F440','1F441','1F445','1F444','1F476','1F9D2','1F466','1F467','1F9D1','1F471','1F468','1F9D4','1F471','200D','2642','FE0F','1F468','200D','1F9B0','1F468','200D','1F9B1','1F468','200D','1F9B3','1F468','200D','1F9B2','1F469','1F471','200D','2640','FE0F','1F469','200D','1F9B0','1F469','200D','1F9B1','1F469','200D','1F9B3','1F469','200D','1F9B2','1F9D3','1F474','1F475','1F64D','1F64D','200D','2642','FE0F','1F64D','200D','2640','FE0F','1F64E','1F64E','200D','2642','FE0F','1F64E','200D','2640','FE0F','1F645','1F645','200D','2642','FE0F','1F645','200D','2640','FE0F','1F646','1F646','200D','2642','FE0F','1F646','200D','2640','FE0F','1F481','1F481','200D','2642','FE0F','1F481','200D','2640','FE0F','1F64B','1F64B','200D','2642','FE0F','1F64B','200D','2640','FE0F','1F9CF','1F9CF','200D','2642','FE0F','1F9CF','200D','2640','FE0F','1F647','1F647','200D','2642','FE0F','1F647','200D','2640','FE0F','1F926','1F926','200D','2642','FE0F','1F926','200D','2640','FE0F','1F937','1F937','200D','2642','FE0F','1F937','200D','2640','FE0F','1F468','200D','2695','FE0F','1F469','200D','2695','FE0F','1F468','200D','1F393','1F469','200D','1F393','1F468','200D','1F3EB','1F469','200D','1F3EB','1F468','200D','2696','FE0F','1F469','200D','2696','FE0F','1F468','200D','1F33E','1F469','200D','1F33E','1F468','200D','1F373','1F469','200D','1F373','1F468','200D','1F527','1F469','200D','1F527','1F468','200D','1F3ED','1F469','200D','1F3ED','1F468','200D','1F4BC','1F469','200D','1F4BC','1F468','200D','1F52C','1F469','200D','1F52C','1F468','200D','1F4BB','1F469','200D','1F4BB','1F468','200D','1F3A4','1F469','200D','1F3A4','1F468','200D','1F3A8','1F469','200D','1F3A8','1F468','200D','2708','FE0F','1F469','200D','2708','FE0F','1F468','200D','1F680','1F469','200D','1F680','1F468','200D','1F692','1F469','200D','1F692','1F46E','1F46E','200D','2642','FE0F','1F46E','200D','2640','FE0F','1F575','1F575','FE0F','200D','2642','FE0F','1F575','FE0F','200D','2640','FE0F','1F482','1F482','200D','2642','FE0F','1F482','200D','2640','FE0F','1F477','1F477','200D','2642','FE0F','1F477','200D','2640','FE0F','1F934','1F478','1F473','1F473','200D','2642','FE0F','1F473','200D','2640','FE0F','1F472','1F9D5','1F935','1F470','1F930','1F931','1F47C','1F385','1F936','1F9B8','1F9B8','200D','2642','FE0F','1F9B8','200D','2640','FE0F','1F9B9','1F9B9','200D','2642','FE0F','1F9B9','200D','2640','FE0F','1F9D9','1F9D9','200D','2642','FE0F','1F9D9','200D','2640','FE0F','1F9DA','1F9DA','200D','2642','FE0F','1F9DA','200D','2640','FE0F','1F9DB','1F9DB','200D','2642','FE0F','1F9DB','200D','2640','FE0F','1F9DC','1F9DC','200D','2642','FE0F','1F9DC','200D','2640','FE0F','1F9DD','1F9DD','200D','2642','FE0F','1F9DD','200D','2640','FE0F','1F9DE','1F9DE','200D','2642','FE0F','1F9DE','200D','2640','FE0F','1F9DF','1F9DF','200D','2642','FE0F','1F9DF','200D','2640','FE0F','1F486','1F486','200D','2642','FE0F','1F486','200D','2640','FE0F','1F487','1F487','200D','2642','FE0F','1F487','200D','2640','FE0F','1F6B6','1F6B6','200D','2642','FE0F','1F6B6','200D','2640','FE0F','1F9CD','1F9CD','200D','2642','FE0F','1F9CD','200D','2640','FE0F','1F9CE','1F9CE','200D','2642','FE0F','1F9CE','200D','2640','FE0F','1F468','200D','1F9AF','1F469','200D','1F9AF','1F468','200D','1F9BC','1F469','200D','1F9BC','1F468','200D','1F9BD','1F469','200D','1F9BD','1F3C3','1F3C3','200D','2642','FE0F','1F3C3','200D','2640','FE0F','1F483','1F57A','1F574','1F46F','1F46F','200D','2642','FE0F','1F46F','200D','2640','FE0F','1F9D6','1F9D6','200D','2642','FE0F','1F9D6','200D','2640','FE0F','1F9D7','1F9D7','200D','2642','FE0F','1F9D7','200D','2640','FE0F','1F93A','1F3C7','26F7','1F3C2','1F3CC','1F3CC','FE0F','200D','2642','FE0F','1F3CC','FE0F','200D','2640','FE0F','1F3C4','1F3C4','200D','2642','FE0F','1F3C4','200D','2640','FE0F','1F6A3','1F6A3','200D','2642','FE0F','1F6A3','200D','2640','FE0F','1F3CA','1F3CA','200D','2642','FE0F','1F3CA','200D','2640','FE0F','26F9','26F9','FE0F','200D','2642','FE0F','26F9','FE0F','200D','2640','FE0F','1F3CB','1F3CB','FE0F','200D','2642','FE0F','1F3CB','FE0F','200D','2640','FE0F','1F6B4','1F6B4','200D','2642','FE0F','1F6B4','200D','2640','FE0F','1F6B5','1F6B5','200D','2642','FE0F','1F6B5','200D','2640','FE0F','1F938','1F938','200D','2642','FE0F','1F938','200D','2640','FE0F','1F93C','1F93C','200D','2642','FE0F','1F93C','200D','2640','FE0F','1F93D','1F93D','200D','2642','FE0F','1F93D','200D','2640','FE0F','1F93E','1F93E','200D','2642','FE0F','1F93E','200D','2640','FE0F','1F939','1F939','200D','2642','FE0F','1F939','200D','2640','FE0F','1F9D8','1F9D8','200D','2642','FE0F','1F9D8','200D','2640','FE0F','1F6C0','1F6CC','1F9D1','200D','1F91D','200D','1F9D1','1F46D','1F46B','1F46C','1F48F','1F469','200D','2764','FE0F','200D','1F48B','200D','1F468','1F468','200D','2764','FE0F','200D','1F48B','200D','1F468','1F469','200D','2764','FE0F','200D','1F48B','200D','1F469','1F491','1F469','200D','2764','FE0F','200D','1F468','1F468','200D','2764','FE0F','200D','1F468','1F469','200D','2764','FE0F','200D','1F469','1F46A','1F468','200D','1F469','200D','1F466','1F468','200D','1F469','200D','1F467','1F468','200D','1F469','200D','1F467','200D','1F466','1F468','200D','1F469','200D','1F466','200D','1F466','1F468','200D','1F469','200D','1F467','200D','1F467','1F468','200D','1F468','200D','1F466','1F468','200D','1F468','200D','1F467','1F468','200D','1F468','200D','1F467','200D','1F466','1F468','200D','1F468','200D','1F466','200D','1F466','1F468','200D','1F468','200D','1F467','200D','1F467','1F469','200D','1F469','200D','1F466','1F469','200D','1F469','200D','1F467','1F469','200D','1F469','200D','1F467','200D','1F466','1F469','200D','1F469','200D','1F466','200D','1F466','1F469','200D','1F469','200D','1F467','200D','1F467','1F468','200D','1F466','1F468','200D','1F466','200D','1F466','1F468','200D','1F467','1F468','200D','1F467','200D','1F466','1F468','200D','1F467','200D','1F467','1F469','200D','1F466','1F469','200D','1F466','200D','1F466','1F469','200D','1F467','1F469','200D','1F467','200D','1F466','1F469','200D','1F467','200D','1F467','1F5E3','1F464','1F465','1F463','1F9B0','1F9B1','1F9B3','1F9B2','1F435','1F412','1F98D','1F9A7','1F436','1F415','1F9AE','1F415','200D','1F9BA','1F429','1F43A','1F98A','1F99D','1F431','1F408','1F981','1F42F','1F405','1F406','1F434','1F40E','1F984','1F993','1F98C','1F42E','1F402','1F403','1F404','1F437','1F416','1F417','1F43D','1F40F','1F411','1F410','1F42A','1F42B','1F999','1F992','1F418','1F98F','1F99B','1F42D','1F401','1F400','1F439','1F430','1F407','1F43F','1F994','1F987','1F43B','1F428','1F43C','1F9A5','1F9A6','1F9A8','1F998','1F9A1','1F43E','1F983','1F414','1F413','1F423','1F424','1F425','1F426','1F427','1F54A','1F985','1F986','1F9A2','1F989','1F9A9','1F99A','1F99C','1F438','1F40A','1F422','1F98E','1F40D','1F432','1F409','1F995','1F996','1F433','1F40B','1F42C','1F41F','1F420','1F421','1F988','1F419','1F41A','1F40C','1F98B','1F41B','1F41C','1F41D','1F41E','1F997','1F577','1F578','1F982','1F99F','1F9A0','1F490','1F338','1F4AE','1F3F5','1F339','1F940','1F33A','1F33B','1F33C','1F337','1F331','1F332','1F333','1F334','1F335','1F33E','1F33F','2618','1F340','1F341','1F342','1F343','1F347','1F348','1F349','1F34A','1F34B','1F34C','1F34D','1F96D','1F34E','1F34F','1F350','1F351','1F352','1F353','1F95D','1F345','1F965','1F951','1F346','1F954','1F955','1F33D','1F336','1F952','1F96C','1F966','1F9C4','1F9C5','1F344','1F95C','1F330','1F35E','1F950','1F956','1F968','1F96F','1F95E','1F9C7','1F9C0','1F356','1F357','1F969','1F953','1F354','1F35F','1F355','1F32D','1F96A','1F32E','1F32F','1F959','1F9C6','1F95A','1F373','1F958','1F372','1F963','1F957','1F37F','1F9C8','1F9C2','1F96B','1F371','1F358','1F359','1F35A','1F35B','1F35C','1F35D','1F360','1F362','1F363','1F364','1F365','1F96E','1F361','1F95F','1F960','1F961','1F980','1F99E','1F990','1F991','1F9AA','1F366','1F367','1F368','1F369','1F36A','1F382','1F370','1F9C1','1F967','1F36B','1F36C','1F36D','1F36E','1F36F','1F37C','1F95B','2615','1F375','1F376','1F37E','1F377','1F378','1F379','1F37A','1F37B','1F942','1F943','1F964','1F9C3','1F9C9','1F9CA','1F962','1F37D','1F374','1F944','1F52A','1F3FA','1F30D','1F30E','1F30F','1F310','1F5FA','1F5FE','1F9ED','1F3D4','26F0','1F30B','1F5FB','1F3D5','1F3D6','1F3DC','1F3DD','1F3DE','1F3DF','1F3DB','1F3D7','1F9F1','1F3D8','1F3DA','1F3E0','1F3E1','1F3E2','1F3E3','1F3E4','1F3E5','1F3E6','1F3E8','1F3E9','1F3EA','1F3EB','1F3EC','1F3ED','1F3EF','1F3F0','1F492','1F5FC','1F5FD','26EA','1F54C','1F6D5','1F54D','26E9','1F54B','26F2','26FA','1F301','1F303','1F3D9','1F304','1F305','1F306','1F307','1F309','2668','1F3A0','1F3A1','1F3A2','1F488','1F3AA','1F682','1F683','1F684','1F685','1F686','1F687','1F688','1F689','1F68A','1F69D','1F69E','1F68B','1F68C','1F68D','1F68E','1F690','1F691','1F692','1F693','1F694','1F695','1F696','1F697','1F698','1F699','1F69A','1F69B','1F69C','1F3CE','1F3CD','1F6F5','1F9BD','1F9BC','1F6FA','1F6B2','1F6F4','1F6F9','1F68F','1F6E3','1F6E4','1F6E2','26FD','1F6A8','1F6A5','1F6A6','1F6D1','1F6A7','2693','26F5','1F6F6','1F6A4','1F6F3','26F4','1F6E5','1F6A2','2708','1F6E9','1F6EB','1F6EC','1FA82','1F4BA','1F681','1F69F','1F6A0','1F6A1','1F6F0','1F680','1F6F8','1F6CE','1F9F3','231B','23F3','231A','23F0','23F1','23F2','1F570','1F55B','1F567','1F550','1F55C','1F551','1F55D','1F552','1F55E','1F553','1F55F','1F554','1F560','1F555','1F561','1F556','1F562','1F557','1F563','1F558','1F564','1F559','1F565','1F55A','1F566','1F311','1F312','1F313','1F314','1F315','1F316','1F317','1F318','1F319','1F31A','1F31B','1F31C','1F321','2600','1F31D','1F31E','1FA90','2B50','1F31F','1F320','1F30C','2601','26C5','26C8','1F324','1F325','1F326','1F327','1F328','1F329','1F32A','1F32B','1F32C','1F300','1F308','1F302','2602','2614','26F1','26A1','2744','2603','26C4','2604','1F525','1F4A7','1F30A','1F383','1F384','1F386','1F387','1F9E8','2728','1F388','1F389','1F38A','1F38B','1F38D','1F38E','1F38F','1F390','1F391','1F9E7','1F380','1F381','1F397','1F39F','1F3AB','1F396','1F3C6','1F3C5','1F947','1F948','1F949','26BD','26BE','1F94E','1F3C0','1F3D0','1F3C8','1F3C9','1F3BE','1F94F','1F3B3','1F3CF','1F3D1','1F3D2','1F94D','1F3D3','1F3F8','1F94A','1F94B','1F945','26F3','26F8','1F3A3','1F93F','1F3BD','1F3BF','1F6F7','1F94C','1F3AF','1FA80','1FA81','1F3B1','1F52E','1F9FF','1F3AE','1F579','1F3B0','1F3B2','1F9E9','1F9F8','2660','2665','2666','2663','265F','1F0CF','1F004','1F3B4','1F3AD','1F5BC','1F3A8','1F9F5','1F9F6','1F453','1F576','1F97D','1F97C','1F9BA','1F454','1F455','1F456','1F9E3','1F9E4','1F9E5','1F9E6','1F457','1F458','1F97B','1FA71','1FA72','1FA73','1F459','1F45A','1F45B','1F45C','1F45D','1F6CD','1F392','1F45E','1F45F','1F97E','1F97F','1F460','1F461','1FA70','1F462','1F451','1F452','1F3A9','1F393','1F9E2','26D1','1F4FF','1F484','1F48D','1F48E','1F507','1F508','1F509','1F50A','1F4E2','1F4E3','1F4EF','1F514','1F515','1F3BC','1F3B5','1F3B6','1F399','1F39A','1F39B','1F3A4','1F3A7','1F4FB','1F3B7','1F3B8','1F3B9','1F3BA','1F3BB','1FA95','1F941','1F4F1','1F4F2','260E','1F4DE','1F4DF','1F4E0','1F50B','1F50C','1F4BB','1F5A5','1F5A8','2328','1F5B1','1F5B2','1F4BD','1F4BE','1F4BF','1F4C0','1F9EE','1F3A5','1F39E','1F4FD','1F3AC','1F4FA','1F4F7','1F4F8','1F4F9','1F4FC','1F50D','1F50E','1F56F','1F4A1','1F526','1F3EE','1FA94','1F4D4','1F4D5','1F4D6','1F4D7','1F4D8','1F4D9','1F4DA','1F4D3','1F4D2','1F4C3','1F4DC','1F4
332C4','1F4F0','1F5DE','1F4D1','1F516','1F3F7','1F4B0','1F4B4','1F4B5','1F4B6','1F4B7','1F4B8','1F4B3','1F9FE','1F4B9','1F4B1','1F4B2','2709','1F4E7','1F4E8','1F4E9','1F4E4','1F4E5','1F4E6','1F4EB','1F4EA','1F4EC','1F4ED','1F4EE','1F5F3','270F','2712','1F58B','1F58A','1F58C','1F58D','1F4DD','1F4BC','1F4C1','1F4C2','1F5C2','1F4C5','1F4C6','1F5D2','1F5D3','1F4C7','1F4C8','1F4C9','1F4CA','1F4CB','1F4CC','1F4CD','1F4CE','1F587','1F4CF','1F4D0','2702','1F5C3','1F5C4','1F5D1','1F512','1F513','1F50F','1F510','1F511','1F5DD','1F528','1FA93','26CF','2692','1F6E0','1F5E1','2694','1F52B','1F3F9','1F6E1','1F527','1F529','2699','1F5DC','2696','1F9AF','1F517','26D3','1F9F0','1F9F2','2697','1F9EA','1F9EB','1F9EC','1F52C','1F52D','1F4E1','1F489','1FA78','1F48A','1FA79','1FA7A','1F6AA','1F6CF','1F6CB','1FA91','1F6BD','1F6BF','1F6C1','1FA92','1F9F4','1F9F7','1F9F9','1F9FA','1F9FB','1F9FC','1F9FD','1F9EF','1F6D2','1F6AC','26B0','26B1','1F5FF','1F3E7','1F6AE','1F6B0','267F','1F6B9','1F6BA','1F6BB','1F6BC','1F6BE','1F6C2','1F6C3','1F6C4','1F6C5','26A0','1F6B8','26D4','1F6AB','1F6B3','1F6AD','1F6AF','1F6B1','1F6B7','1F4F5','1F51E','2622','2623','2B06','2197','27A1','2198','2B07','2199','2B05','2196','2195','2194','21A9','21AA','2934','2935','1F503','1F504','1F519','1F51A','1F51B','1F51C','1F51D','1F6D0','269B','1F549','2721','2638','262F','271D','2626','262A','262E','1F54E','1F52F','2648','2649','264A','264B','264C','264D','264E','264F','2650','2651','2652','2653','26CE','1F500','1F501','1F502','25B6','23E9','23ED','23EF','25C0','23EA','23EE','1F53C','23EB','1F53D','23EC','23F8','23F9','23FA','23CF','1F3A6','1F505','1F506','1F4F6','1F4F3','1F4F4','2640','2642','2695','267E','267B','269C','1F531','1F4DB','1F530','2B55','2705','2611','2714','2716','274C','274E','2795','2796','2797','27B0','27BF','303D','2733','2734','2747','203C','2049','2753','2754','2755','2757','3030','00A9','00AE','2122','0023','FE0F','20E3','002A','FE0F','20E3','0030','FE0F','20E3','0031','FE0F','20E3','0032','FE0F','20E3','0033','FE0F','20E3','0034','FE0F','20E3','0035','FE0F','20E3','0036','FE0F','20E3','0037','FE0F','20E3','0038','FE0F','20E3','0039','FE0F','20E3','1F51F','1F520','1F521','1F522','1F523','1F524','1F170','1F18E','1F171','1F191','1F192','1F193','2139','1F194','24C2','1F195','1F196','1F17E','1F197','1F17F','1F198','1F199','1F19A','1F201','1F202','1F237','1F236','1F22F','1F250','1F239','1F21A','1F232','1F251','1F238','1F234','1F233','3297','3299','1F23A','1F235','1F534','1F7E0','1F7E1','1F7E2','1F535','1F7E3','1F7E4','26AB','26AA','1F7E5','1F7E7','1F7E8','1F7E9','1F7E6','1F7EA','1F7EB','2B1B','2B1C','25FC','25FB','25FE','25FD','25AA','25AB','1F536','1F537','1F538','1F539','1F53A','1F53B','1F4A0','1F518','1F533','1F532','1F3C1','1F6A9','1F38C','1F3F4','1F3F3','1F3F3','FE0F','200D','1F308','1F3F4','200D','2620','FE0F','1F1E6','1F1E8','1F1E6','1F1E9','1F1E6','1F1EA','1F1E6','1F1EB','1F1E6','1F1EC','1F1E6','1F1EE','1F1E6','1F1F1','1F1E6','1F1F2','1F1E6','1F1F4','1F1E6','1F1F6','1F1E6','1F1F7','1F1E6','1F1F8','1F1E6','1F1F9','1F1E6','1F1FA','1F1E6','1F1FC','1F1E6','1F1FD','1F1E6','1F1FF','1F1E7','1F1E6','1F1E7','1F1E7','1F1E7','1F1E9','1F1E7','1F1EA','1F1E7','1F1EB','1F1E7','1F1EC','1F1E7','1F1ED','1F1E7','1F1EE','1F1E7','1F1EF','1F1E7','1F1F1','1F1E7','1F1F2','1F1E7','1F1F3','1F1E7','1F1F4','1F1E7','1F1F6','1F1E7','1F1F7','1F1E7','1F1F8','1F1E7','1F1F9','1F1E7','1F1FB','1F1E7','1F1FC','1F1E7','1F1FE','1F1E7','1F1FF','1F1E8','1F1E6','1F1E8','1F1E8','1F1E8','1F1E9','1F1E8','1F1EB','1F1E8','1F1EC','1F1E8','1F1ED','1F1E8','1F1EE','1F1E8','1F1F0','1F1E8','1F1F1','1F1E8','1F1F2','1F1E8','1F1F3','1F1E8','1F1F4','1F1E8','1F1F5','1F1E8','1F1F7','1F1E8','1F1FA','1F1E8','1F1FB','1F1E8','1F1FC','1F1E8','1F1FD','1F1E8','1F1FE','1F1E8','1F1FF','1F1E9','1F1EA','1F1E9','1F1EC','1F1E9','1F1EF','1F1E9','1F1F0','1F1E9','1F1F2','1F1E9','1F1F4','1F1E9','1F1FF','1F1EA','1F1E6','1F1EA','1F1E8','1F1EA','1F1EA','1F1EA','1F1EC','1F1EA','1F1ED','1F1EA','1F1F7','1F1EA','1F1F8','1F1EA','1F1F9','1F1EA','1F1FA','1F1EB','1F1EE','1F1EB','1F1EF','1F1EB','1F1F0','1F1EB','1F1F2','1F1EB','1F1F4','1F1EB','1F1F7','1F1EC','1F1E6','1F1EC','1F1E7','1F1EC','1F1E9','1F1EC','1F1EA','1F1EC','1F1EB','1F1EC','1F1EC','1F1EC','1F1ED','1F1EC','1F1EE','1F1EC','1F1F1','1F1EC','1F1F2','1F1EC','1F1F3','1F1EC','1F1F5','1F1EC','1F1F6','1F1EC','1F1F7','1F1EC','1F1F8','1F1EC','1F1F9','1F1EC','1F1FA','1F1EC','1F1FC','1F1EC','1F1FE','1F1ED','1F1F0','1F1ED','1F1F2','1F1ED','1F1F3','1F1ED','1F1F7','1F1ED','1F1F9','1F1ED','1F1FA','1F1EE','1F1E8','1F1EE','1F1E9','1F1EE','1F1EA','1F1EE','1F1F1','1F1EE','1F1F2','1F1EE','1F1F3','1F1EE','1F1F4','1F1EE','1F1F6','1F1EE','1F1F7','1F1EE','1F1F8','1F1EE','1F1F9','1F1EF','1F1EA','1F1EF','1F1F2','1F1EF','1F1F4','1F1EF','1F1F5','1F1F0','1F1EA','1F1F0','1F1EC','1F1F0','1F1ED','1F1F0','1F1EE','1F1F0','1F1F2','1F1F0','1F1F3','1F1F0','1F1F5','1F1F0','1F1F7','1F1F0','1F1FC','1F1F0','1F1FE','1F1F0','1F1FF','1F1F1','1F1E6','1F1F1','1F1E7','1F1F1','1F1E8','1F1F1','1F1EE','1F1F1','1F1F0','1F1F1','1F1F7','1F1F1','1F1F8','1F1F1','1F1F9','1F1F1','1F1FA','1F1F1','1F1FB','1F1F1','1F1FE','1F1F2','1F1E6','1F1F2','1F1E8','1F1F2','1F1E9','1F1F2','1F1EA','1F1F2','1F1EB','1F1F2','1F1EC','1F1F2','1F1ED','1F1F2','1F1F0','1F1F2','1F1F1','1F1F2','1F1F2','1F1F2','1F1F3','1F1F2','1F1F4','1F1F2','1F1F5','1F1F2','1F1F6','1F1F2','1F1F7','1F1F2','1F1F8','1F1F2','1F1F9','1F1F2','1F1FA','1F1F2','1F1FB','1F1F2','1F1FC','1F1F2','1F1FD','1F1F2','1F1FE','1F1F2','1F1FF','1F1F3','1F1E6','1F1F3','1F1E8','1F1F3','1F1EA','1F1F3','1F1EB','1F1F3','1F1EC','1F1F3','1F1EE','1F1F3','1F1F1','1F1F3','1F1F4','1F1F3','1F1F5','1F1F3','1F1F7','1F1F3','1F1FA','1F1F3','1F1FF','1F1F4','1F1F2','1F1F5','1F1E6','1F1F5','1F1EA','1F1F5','1F1EB','1F1F5','1F1EC','1F1F5','1F1ED','1F1F5','1F1F0','1F1F5','1F1F1','1F1F5','1F1F2','1F1F5','1F1F3','1F1F5','1F1F7','1F1F5','1F1F8','1F1F5','1F1F9','1F1F5','1F1FC','1F1F5','1F1FE','1F1F6','1F1E6','1F1F7','1F1EA','1F1F7','1F1F4','1F1F7','1F1F8','1F1F7','1F1FA','1F1F7','1F1FC','1F1F8','1F1E6','1F1F8','1F1E7','1F1F8','1F1E8','1F1F8','1F1E9','1F1F8','1F1EA','1F1F8','1F1EC','1F1F8','1F1ED','1F1F8','1F1EE','1F1F8','1F1EF','1F1F8','1F1F0','1F1F8','1F1F1','1F1F8','1F1F2','1F1F8','1F1F3','1F1F8','1F1F4','1F1F8','1F1F7','1F1F8','1F1F8','1F1F8','1F1F9','1F1F8','1F1FB','1F1F8','1F1FD','1F1F8','1F1FE','1F1F8','1F1FF','1F1F9','1F1E6','1F1F9','1F1E8','1F1F9','1F1E9','1F1F9','1F1EB','1F1F9','1F1EC','1F1F9','1F1ED','1F1F9','1F1EF','1F1F9','1F1F0','1F1F9','1F1F1','1F1F9','1F1F2','1F1F9','1F1F3','1F1F9','1F1F4','1F1F9','1F1F7','1F1F9','1F1F9','1F1F9','1F1FB','1F1F9','1F1FC','1F1F9','1F1FF','1F1FA','1F1E6','1F1FA','1F1EC','1F1FA','1F1F2','1F1FA','1F1F3','1F1FA','1F1F8','1F1FA','1F1FE','1F1FA','1F1FF','1F1FB','1F1E6','1F1FB','1F1E8','1F1FB','1F1EA','1F1FB','1F1EC','1F1FB','1F1EE','1F1FB','1F1F3','1F1FB','1F1FA','1F1FC','1F1EB','1F1FC','1F1F8','1F1FD','1F1F0','1F1FE','1F1EA','1F1FE','1F1F9','1F1FF','1F1E6','1F1FF','1F1F2','1F1FF','1F1FC','1F3F4','E0067','E0062','E0065','E006E','E0067','E007F','1F3F4','E0067','E0062','E0073','E0063','E0074','E007F','1F3F4','E0067','E0062','E0077','E006C','E0073','E007F'];
333    let emojis_regex = '\\u{' + emojis.join('}\\u{') + '}';
334
335    let regex = new RegExp(`[^\.\\p{L}\\d\\-\_${emojis_regex}]+`, 'ug');
336    string = string.replace(regex, delimiter);
337
338    regex = new RegExp(`${delimiter}+`, 'g');
339    string = string.replace(regex, delimiter);
340
341    string = string.trim().replaceAll('#', '').replaceAll('\\', '').replaceAll('/', '').replaceAll(':', '');
342
343    if(lowercase) {
344        string.toLowerCase();
345    }
346
347    return string;
348}
349
350let build_url_query = data => {
351    if(typeof (data) === 'string') return data;
352
353    let query = [];
354    for (let key in data) {
355        if(data.hasOwnProperty(key)) {
356            query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
357        }
358    }
359
360    return query.join('&');
361};
362
363let build_form_data = data => {
364    let form_data = new FormData();
365
366    for(let key in data) {
367        form_data.append(key, data[key]);
368    }
369
370    return form_data
371}
372
373const update_this_value = (this_element, function_name) => {
374    this_element.value = function_name(this_element.value);
375}
376
377/* Character counter */
378document.querySelectorAll('[data-character-counter]').forEach(element => {
379    let target = element.getAttribute('data-character-counter');
380
381    ['change', 'paste', 'keyup', 'search'].forEach(event_type => {
382        element.querySelector(`${target}`).addEventListener(event_type, event => {
383            character_counter_count(element, target);
384        })
385    })
386});
387
388let character_counter_count = (element, target) => {
389    let current_characters = element.querySelector(`${target}`).value.length;
390    let allowed_characters = element.querySelector(`${target}`).getAttribute('maxlength');
391
392    let filled_percentage = 0;
393
394    if(current_characters > 0) {
395        element.querySelector('[data-character-counter-wrapper]').classList.remove('d-none');
396        filled_percentage = parseInt(current_characters / allowed_characters * 100);
397    } else {
398        element.querySelector('[data-character-counter-wrapper]').classList.add('d-none');
399    }
400
401    let color_cass = 'text-muted';
402    if(filled_percentage > 75) color_cass = 'text-info';
403    if(filled_percentage > 90) color_cass = 'text-warning';
404    if(filled_percentage > 97) color_cass = 'text-danger';
405
406    element.querySelector('[data-character-counter-wrapper]').innerHTML = `
407        <span data-current-characters>${current_characters}</span>/<span data-allowed-characters>${allowed_characters}</span> (<span class="${color_cass}">${filled_percentage}%</span>)
408    `;
409}
410
411let range_counter_initiate = () => {
412    document.querySelectorAll('[data-range-counter]').forEach(element => {
413        let label = element.querySelector('label');
414        let label_html = label.innerHTML;
415
416        /* Create a span element to hold the label text */
417        let label_span = document.createElement('span');
418        label_span.innerHTML = label_html;
419
420        /* Create small element which will display the current range input value */
421        let label_small = document.createElement('small');
422        label_small.classList.add('text-muted');
423        label_small.setAttribute('data-range-counter-wrapper', '');
424
425        /* Add new classes to the already existing label */
426        label.classList.add('d-flex', 'justify-content-between', 'align-items-center');
427
428        /* Replace existing label with the new generated content */
429        label.innerHTML = ``;
430        label.appendChild(label_span);
431        label.appendChild(label_small);
432
433        /* Add the event listeners for the range input */
434        element.querySelector(`input`).addEventListener('change', event => {
435            let current_value = event.currentTarget.value;
436            let suffix = element.getAttribute('data-range-counter-suffix') ?? '';
437
438            element.querySelector('[data-range-counter-wrapper]').innerText = `${current_value}${suffix}`;
439        });
440
441        /* Trigger first change event */
442        let event = new Event('change');
443        element.querySelector('input').dispatchEvent(event);
444    });
445}
446
447range_counter_initiate();
448
449let password_toggle_view_initiate = () => {
450    document.querySelectorAll('[data-password-toggle-view]').forEach(element => {
451        let label = element.querySelector('label');
452        let label_html = label.innerHTML;
453
454        /* Create a span element to hold the label text */
455        let label_span = document.createElement('span');
456        label_span.innerHTML = label_html;
457
458        /* Create small element which will display the current range input value */
459        let label_icon = document.createElement('a');
460        label_icon.classList.add('text-muted');
461        label_icon.setAttribute('role', 'button');
462        label_icon.setAttribute('tabindex', '0');
463        label_icon.innerHTML = '<i class="fas fa-fw fa-sm fa-eye-slash"></i>'
464        label_icon.setAttribute('title', element.getAttribute('data-password-toggle-view-show'));
465        label_icon.setAttribute('data-toggle', 'tooltip');
466        label_icon.setAttribute('data-tooltip-hide-on-click', '');
467
468        /* Add the event listeners for the eye */
469        label_icon.addEventListener('click', event => {
470            let input = element.querySelector(`input`);
471            input.type = input.type === 'text' ? 'password' : 'text';
472            label_icon.innerHTML = input.type === 'text' ? '<i class="fas fa-fw fa-sm fa-eye"></i>' : '<i class="fas fa-fw fa-sm fa-eye-slash"></i>';
473            label_icon.setAttribute('title', input.type === 'text' ? element.getAttribute('data-password-toggle-view-hide') : element.getAttribute('data-password-toggle-view-show'));
474            label_icon.setAttribute('data-original-title', input.type === 'text' ? element.getAttribute('data-password-toggle-view-hide') : element.getAttribute('data-password-toggle-view-show'));
475            tooltips_initiate();
476        });
477
478        /* Add new classes to the already existing label */
479        label.classList.add('d-flex', 'justify-content-between', 'align-items-center');
480
481        /* Replace existing label with the new generated content */
482        label.innerHTML = ``;
483        label.appendChild(label_span);
484        label.appendChild(label_icon);
485
486        tooltips_initiate();
487    });
488}
489
490password_toggle_view_initiate();
491
492/* Filters detect and shortcut CMD/CTRL+SHIFT+F */
493document.addEventListener('keydown', event => {
494    if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key.toLowerCase() === 'f') {
495        event.preventDefault();
496
497        const filters_button = $('.filters-button');
498
499        /* temporarily disable tooltip */
500        filters_button.tooltip('disable');
501
502        /* toggle dropdown */
503        filters_button.dropdown('toggle');
504
505        /* focus on search input */
506        setTimeout(function() {
507            document.getElementById('filters_search').focus();
508
509            /* re-enable tooltip after short delay */
510            filters_button.tooltip('enable');
511        }, 100);
512    }
513});
514
515const get_plan_feature_limit_info = (used, total, should_display = true, info_message) => {
516    /* return null if not to be displayed */
517    if (!should_display) return null;
518
519    /* calculate percentage used */
520    let percentage_used = (total === -1 || total === 0) ? 0 : (used / total * 100);
521
522    /* determine remaining percentage or unlimited */
523    let percentage_remaining = (total === -1) ? 'Unlimited' : nr(100 - percentage_used) + '%';
524
525    /* build the final message */
526    return info_message
527        .replace('%1$s', '<strong>' + nr(used) + '</strong>')
528        .replace('%2$s', '<strong>' + (total === -1 ? 'Unlimited' : nr(total)) + '</strong>')
529        .replace('%3$s', '<strong>' + percentage_remaining + '</strong>');
530};
531
532document.querySelectorAll('input[type="url"]').forEach(input => {
533    const fixURL = () => input.value = input.value.trim().replace(/^(?!https?:\/\/)(.+\..+)/i, 'https://$1').replace(/^(https?:\/\/)/i, m => m.toLowerCase());
534
535    input.addEventListener('blur', fixURL);
536    input.addEventListener('keydown', e => e.key === 'Enter' && fixURL());
537});
538
539document.querySelectorAll('form input, form select, form textarea').forEach(field => {
540    field.addEventListener('invalid', () => {
541        const invalid_field = field;
542
543        /* find the nearest collapsed parent */
544        const collapse_container = invalid_field.closest('.collapse');
545
546        if (!collapse_container) {
547            return;
548        }
549
550        /* if collapse already open, scroll immediately */
551        if ($(collapse_container).hasClass('show')) {
552            invalid_field.scrollIntoView({ behavior: 'smooth', block: 'center' });
553            setTimeout(() => invalid_field.focus(), 100);
554            return;
555        }
556
557        /* wait for the collapse animation to finish */
558        $(collapse_container).one('shown.bs.collapse', () => {
559            invalid_field.scrollIntoView({ behavior: 'smooth', block: 'center' });
560            setTimeout(() => invalid_field.focus(), 100);
561        });
562
563        /* trigger the open animation */
564        $(collapse_container).collapse('show');
565    });
566});
567
568
569
570/* Product specific functions */
571const ajax_call_helper = (event, controller, request_type, success_callback = () => {}) => {
572    let row_id = $(event.currentTarget).data('row-id');
573
574    let data = {
575        global_token,
576        request_type
577    };
578
579    switch(controller) {
580        case 'campaigns-ajax':
581            data.campaign_id = row_id;
582            break;
583
584        case 'notifications-ajax':
585            data.notification_id = row_id;
586            break;
587
588        default:
589            data.id = row_id;
590    }
591
592    $.ajax({
593        type: 'POST',
594        url: `${url}${controller}`,
595        data: data,
596        success: (data) => {
597            if(data.status == 'error') {
598                alert(data.message[0]);
599            }
600
601            else if(data.status == 'success') {
602
603                success_callback(data)
604
605            }
606        },
607        dataType: 'json'
608    });
609
610    event.preventDefault();
611};

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.