1/** 2 * Utility Functions - Bootstrap Removal Phase 9 3 * 4 * Helper functions to replace common jQuery patterns with vanilla JavaScript. 5 * These utilities provide a cleaner API for common DOM operations. 6 */ 7 8(function(window) { 9 'use strict'; 10 11 /** 12 * Select single element (querySelector shorthand) 13 * @param {string} selector - CSS selector 14 * @param {Element|Document} context - Context element (defaults to document) 15 * @returns {Element|null} 16 */ 17 function $(selector, context) { 18 return (context || document).querySelector(selector); 19 } 20 21 /** 22 * Select multiple elements (querySelectorAll shorthand, returns array) 23 * @param {string} selector - CSS selector 24 * @param {Element|Document} context - Context element (defaults to document) 25 * @returns {Element[]} 26 */ 27 function $$(selector, context) { 28 return Array.from((context || document).querySelectorAll(selector)); 29 } 30 31 /** 32 * Execute function when DOM is ready 33 * @param {Function} fn - Function to execute 34 */ 35 function ready(fn) { 36 if (document.readyState !== 'loading') { 37 fn(); 38 } else { 39 document.addEventListener('DOMContentLoaded', fn); 40 } 41 } 42 43 /** 44 * Delegate event handler 45 * @param {Element} parent - Parent element to attach listener to 46 * @param {string} eventType - Event type (e.g., 'click') 47 * @param {string} selector - CSS selector for matching children 48 * @param {Function} handler - Event handler function 49 */ 50 function delegate(parent, eventType, selector, handler) { 51 parent.addEventListener(eventType, function(e) { 52 var target = e.target.closest(selector); 53 if (target && parent.contains(target)) { 54 handler.call(target, e); 55 } 56 }); 57 } 58 59 /** 60 * Serialize form data to URL-encoded string 61 * @param {HTMLFormElement} form - Form element 62 * @returns {string} 63 */ 64 function serialize(form) { 65 return new URLSearchParams(new FormData(form)).toString(); 66 } 67 68 /** 69 * Fade in element using Web Animations API 70 * @param {Element} el - Element to fade in 71 * @param {number} duration - Duration in milliseconds (default: 300) 72 * @param {Function} callback - Optional callback when animation completes 73 */ 74 function fadeIn(el, duration, callback) { 75 duration = duration || 300; 76 el.style.display = ''; 77 el.style.opacity = '0'; 78 79 var animation = el.animate( 80 [{ opacity: 0 }, { opacity: 1 }], 81 { duration: duration, fill: 'forwards' } 82 ); 83 84 if (callback) { 85 animation.onfinish = callback; 86 } 87 } 88 89 /** 90 * Fade out element using Web Animations API 91 * @param {Element} el - Element to fade out 92 * @param {number} duration - Duration in milliseconds (default: 300) 93 * @param {Function} callback - Optional callback when animation completes 94 */ 95 function fadeOut(el, duration, callback) { 96 duration = duration || 300; 97 98 var animation = el.animate( 99 [{ opacity: 1 }, { opacity: 0 }], 100 { duration: duration, fill: 'forwards' } 101 ); 102 103 animation.onfinish = function() { 104 el.style.display = 'none'; 105 if (callback) callback(); 106 }; 107 } 108 109 /** 110 * Slide down element (expand) 111 * @param {Element} el - Element to expand 112 * @param {number} duration - Duration in milliseconds (default: 300) 113 * @param {Function} callback - Optional callback when animation completes 114 */ 115 function slideDown(el, duration, callback) { 116 duration = duration || 300; 117 118 /* Reset to get natural height */ 119 el.style.display = ''; 120 el.style.overflow = 'hidden'; 121 var height = el.scrollHeight; 122 123 el.style.height = '0'; 124 125 var animation = el.animate( 126 [{ height: '0px' }, { height: height + 'px' }], 127 { duration: duration, easing: 'ease-out' } 128 ); 129 130 animation.onfinish = function() { 131 el.style.height = ''; 132 el.style.overflow = ''; 133 if (callback) callback(); 134 }; 135 } 136 137 /** 138 * Slide up element (collapse) 139 * @param {Element} el - Element to collapse 140 * @param {number} duration - Duration in milliseconds (default: 300) 141 * @param {Function} callback - Optional callback when animation completes 142 */ 143 function slideUp(el, duration, callback) { 144 duration = duration || 300; 145 146 el.style.overflow = 'hidden'; 147 var height = el.scrollHeight; 148 149 var animation = el.animate( 150 [{ height: height + 'px' }, { height: '0px' }], 151 { duration: duration, easing: 'ease-out' } 152 ); 153 154 animation.onfinish = function() { 155 el.style.display = 'none'; 156 el.style.height = ''; 157 el.style.overflow = ''; 158 if (callback) callback(); 159 }; 160 } 161 162 /** 163 * Toggle element visibility with slide animation 164 * @param {Element} el - Element to toggle 165 * @param {number} duration - Duration in milliseconds (default: 300) 166 * @param {Function} callback - Optional callback when animation completes 167 */ 168 function slideToggle(el, duration, callback) { 169 if (el.offsetHeight === 0 || getComputedStyle(el).display === 'none') { 170 slideDown(el, duration, callback); 171 } else { 172 slideUp(el, duration, callback); 173 } 174 } 175 176 /** 177 * Add class to element(s) 178 * @param {Element|Element[]} elements - Element or array of elements 179 * @param {string} className - Class name to add 180 */ 181 function addClass(elements, className) { 182 var els = Array.isArray(elements) ? elements : [elements];
183 els.forEach(function(el) { 184 if (el) el.classList.add(className); 185 }); 186 } 187 188 /** 189 * Remove class from element(s) 190 * @param {Element|Element[]} elements - Element or array of elements 191 * @param {string} className - Class name to remove 192 */ 193 function removeClass(elements, className) { 194 var els = Array.isArray(elements) ? elements : [elements]; 195 els.forEach(function(el) { 196 if (el) el.classList.remove(className); 197 }); 198 } 199 200 /** 201 * Toggle class on element(s) 202 * @param {Element|Element[]} elements - Element or array of elements 203 * @param {string} className - Class name to toggle 204 */ 205 function toggleClass(elements, className) { 206 var els = Array.isArray(elements) ? elements : [elements]; 207 els.forEach(function(el) { 208 if (el) el.classList.toggle(className); 209 }); 210 } 211 212 /** 213 * Check if element has class 214 * @param {Element} element - Element to check 215 * @param {string} className - Class name to check 216 * @returns {boolean} 217 */ 218 function hasClass(element, className) { 219 return element ? element.classList.contains(className) : false; 220 } 221 222 /** 223 * Simple AJAX GET request 224 * @param {string} url - URL to fetch 225 * @param {Object} options - Optional fetch options 226 * @returns {Promise} 227 */ 228 function ajax(url, options) { 229 options = options || {}; 230 options.headers = options.headers || {}; 231 232 /* Add XMLHttpRequest header for Nette compatibility */ 233 if (!options.headers['X-Requested-With']) { 234 options.headers['X-Requested-With'] = 'XMLHttpRequest'; 235 } 236 237 return fetch(url, options).then(function(response) { 238 if (!response.ok) { 239 throw new Error('Network response was not ok'); 240 } 241 var contentType = response.headers.get('content-type'); 242 if (contentType && contentType.includes('application/json')) { 243 return response.json(); 244 } 245 return response.text(); 246 }); 247 } 248 249 /** 250 * POST request with form data 251 * @param {string} url - URL to post to 252 * @param {FormData|Object|string} data - Data to send 253 * @returns {Promise} 254 */ 255 function post(url, data) { 256 var body; 257 var headers = { 258 'X-Requested-With': 'XMLHttpRequest' 259 }; 260 261 if (data instanceof FormData) { 262 body = data; 263 } else if (typeof data === 'object') { 264 body = JSON.stringify(data); 265 headers['Content-Type'] = 'application/json'; 266 } else { 267 body = data; 268 headers['Content-Type'] = 'application/x-www-form-urlencoded'; 269 } 270 271 return fetch(url, { 272 method: 'POST', 273 headers: headers, 274 body: body 275 }).then(function(response) { 276 if (!response.ok) { 277 throw new Error('Network response was not ok'); 278 } 279 var contentType = response.headers.get('content-type'); 280 if (contentType && contentType.includes('application/json')) { 281 return response.json(); 282 } 283 return response.text(); 284 }); 285 } 286 287 /* Export utilities to window.FreeloUtils namespace */ 288 window.FreeloUtils = { 289 $: $, 290 $$: $$, 291 ready: ready, 292 delegate: delegate, 293 serialize: serialize, 294 fadeIn: fadeIn, 295 fadeOut: fadeOut, 296 slideDown: slideDown, 297 slideUp: slideUp, 298 slideToggle: slideToggle, 299 addClass: addClass, 300 removeClass: removeClass, 301 toggleClass: toggleClass, 302 hasClass: hasClass, 303 ajax: ajax, 304 post: post 305 }; 306 307 /* Also expose commonly used functions globally for convenience */ 308 /* Note: These won't override jQuery's $ if it's loaded */ 309 if (typeof window.FreeloSelect === 'undefined') { 310 window.FreeloSelect = $; 311 window.FreeloSelectAll = $$; 312 } 313 314})(window);
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.