1/** 2 * Motorica â Float-Label Initialisierung + Legacy-Formular-Migration 3 */ 4(function (window, $) { 5 'use strict'; 6 7 var SKIP_SELECTOR = [ 8 '[data-motorica-no-float]', 9 '.motorica-invoices-filter-card', 10 '.motorica-invoices-date-field', 11 '.customer-search-float', 12 '.motorica-toolbar-row', 13 '.motorica-toolbar-forms', 14 '.dataTables_filter', 15 '.dataTables_length', 16 '.dt-length', 17 '.dt-search', 18 '.form-check', 19 '.form-check-inline', 20 '.custom-control', 21 '.custom-file', 22 '.input-group', 23 '.motorica-image-upload', 24 '.btn-group', 25 '.select2-container', 26 '.calendar-picker-wrap', 27 '.doc-form-section--discount', 28 ].join(', '); 29 30 var CONTROL_SELECTOR = 'input.form-control:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]), select.form-control, textarea.form-control'; 31 32 function selectFloatAlwaysActive(select) { 33 return select && (select.dataset.motoricaFloatAlways === '1' 34 || select.getAttribute('data-motorica-float-always') === '1'); 35 } 36 37 function syncSelectHasValue(select) { 38 if (!select || select.tagName !== 'SELECT') { 39 return; 40 } 41 if (selectFloatAlwaysActive(select)) { 42 select.classList.add('has-value'); 43 return; 44 } 45 var val = select.value; 46 if (val !== null && String(val).trim() !== '') { 47 select.classList.add('has-value'); 48 } else { 49 select.classList.remove('has-value'); 50 } 51 } 52 53 function syncReadonlyFloat(input) { 54 if (!input || !input.readOnly) { 55 return; 56 } 57 if (String(input.value || '').trim() !== '') { 58 input.setAttribute('placeholder', ' '); 59 } 60 } 61 62 function syncInputHasValue(input) { 63 if (!input || input.tagName !== 'INPUT') { 64 return; 65 } 66 var type = (input.getAttribute('type') || 'text').toLowerCase(); 67 if (type === 'checkbox' || type === 'radio' || type === 'hidden' || type === 'file') { 68 return; 69 } 70 if (type === 'number' || type === 'range') { 71 if (input.value !== '' && input.value !== null) { 72 input.classList.add('has-value'); 73 } else { 74 input.classList.remove('has-value'); 75 } 76 return; 77 } 78 if (String(input.value || '').trim() !== '') { 79 input.classList.add('has-value'); 80 } else { 81 input.classList.remove('has-value'); 82 } 83 } 84 85 function normalizeFloatLabel(label, floatWrap, container) { 86 if (!label) { 87 return; 88 } 89 var extraNodes = []; 90 var br = label.querySelector('br'); 91 if (br) { 92 var node = br.nextSibling; 93 while (node) { 94 var next = node.nextSibling; 95 extraNodes.push(node); 96 node = next; 97 } 98 br.remove(); 99 extraNodes.forEach(function (n) { 100 if (n.parentNode === label) { 101 label.removeChild(n); 102 } 103 }); 104 } 105 if (extraNodes.length) { 106 var sub = document.createElement('div'); 107 sub.className = 'motorica-field-sublabel'; 108 extraNodes.forEach(function (n) { 109 sub.appendChild(n); 110 }); 111 if (floatWrap.nextSibling) { 112 container.insertBefore(sub, floatWrap.nextSibling); 113 } else { 114 container.appendChild(sub); 115 } 116 } 117 var text = (label.textContent || '').replace(/\s+/g, ' ').trim(); 118 if (text) { 119 label.textContent = text; 120 } 121 } 122 123 function shouldSkipContainer(container) { 124 if (!container || container.closest(SKIP_SELECTOR)) { 125 return true; 126 } 127 if (container.closest('[data-motorica-no-float]')) { 128 return true; 129 } 130 if (container.querySelector('.form-check, .form-check-inline, .custom-control, .custom-file')) { 131 return true; 132 } 133 return false; 134 } 135 136 function findLabelForControl(container, control) { 137 if (!control || !control.id) { 138 return null; 139 } 140 var label = container.querySelector(':scope > label[for="' + control.id + '"]'); 141 if (label) { 142 return label; 143 } 144 label = container.querySelector('label[for="' + control.id + '"]:not(.float-label)'); 145 return label; 146 } 147 148 function findPrimaryControl(container) { 149 var direct = container.querySelector(':scope > ' + CONTROL_SELECTOR); 150 if (direct) { 151 return direct; 152 } 153 return container.querySelector(CONTROL_SELECTOR); 154 } 155 156 function isSelect2Candidate(select) { 157 if (!select || select.tagName !== 'SELECT') { 158 return false; 159 } 160 if (select.classList.contains('select2-hidden-accessible')) { 161 return true; 162 } 163 if (select.classList.contains('motorica-select2-source')) { 164 return true; 165 } 166 if (select.dataset.select2Id !== undefined) { 167 return true; 168 } 169 if (select.classList.contains('select2')) { 170 return true; 171 } 172 return false;
173 } 174 175 function prepareControl(control) { 176 control.classList.remove('form-control-lg', 'form-control-sm'); 177 if (control.tagName === 'INPUT') { 178 var type = (control.getAttribute('type') || 'text').toLowerCase(); 179 if (type !== 'date' && type !== 'color' && type !== 'time' && type !== 'datetime-local') { 180 if (!control.getAttribute('placeholder')) { 181 control.setAttribute('placeholder', ' '); 182 } 183 } 184 } 185 if (control.tagName === 'TEXTAREA' && !control.getAttribute('placeholder')) { 186 control.setAttribute('placeholder', ' '); 187 } 188 } 189 190 function destroySelect2IfNeeded(control) { 191 if (!$ || !$.fn || typeof $.fn.select2 !== 'function') { 192 return null; 193 } 194 var $control = $(control); 195 if (!$control.hasClass('select2-hidden-accessible')) { 196 return null; 197 } 198 var data = $control.data('select2'); 199 var options = {}; 200 if (data && data.options) { 201 options = $.extend({}, data.options); 202 } 203 $control.select2('destroy'); 204 return options; 205 } 206 207 function reinitSelect2(control, previousOptions) { 208 if (!window.MotoricaFormSelect2 || typeof window.MotoricaFormSelect2.init !== 'function') { 209 return; 210 } 211 window.MotoricaFormSelect2.init($(control), previousOptions || {}); 212 } 213 214 function wrapControlAsFloat(container, control, label) { 215 if (!control || control.closest('.form-float')) { 216 return false; 217 } 218 if (label && label.classList.contains('float-label')) { 219 return false; 220 } 221 222 var select2Options = null; 223 if (control.tagName === 'SELECT' && isSelect2Candidate(control)) { 224 select2Options = destroySelect2IfNeeded(control); 225 } 226 227 var floatWrap = document.createElement('div'); 228 floatWrap.className = 'form-float'; 229 if (control.tagName === 'TEXTAREA') { 230 floatWrap.classList.add('form-float--textarea'); 231 } 232 if (control.tagName === 'SELECT' && (isSelect2Candidate(control) || control.classList.contains('motorica-select2'))) { 233 floatWrap.classList.add('form-float--select2'); 234 } else if (control.tagName === 'SELECT') { 235 floatWrap.classList.add('form-float--select-native'); 236 } 237 238 if (label) { 239 label.parentNode.insertBefore(floatWrap, label); 240 } else { 241 container.insertBefore(floatWrap, control); 242 } 243 244 floatWrap.appendChild(control); 245 prepareControl(control); 246 247 if (label) { 248 normalizeFloatLabel(label, floatWrap, container); 249 label.classList.add('float-label'); 250 label.classList.remove('col-form-label', 'sr-only'); 251 floatWrap.appendChild(label); 252 } 253 254 container.classList.add('motorica-field-group', 'motorica-float-migrated'); 255 container.dataset.motoricaFloatMigrated = '1'; 256 257 if (control.tagName === 'SELECT') { 258 syncSelectHasValue(control); 259 } 260 syncInputHasValue(control); 261 syncReadonlyFloat(control); 262 263 if (select2Options !== null) { 264 reinitSelect2(control, select2Options); 265 } 266 267 return true; 268 } 269 270 function migrateFormGroup(group) { 271 if (!group || group.dataset.motoricaFloatMigrated === '1') { 272 return false; 273 } 274 if (shouldSkipContainer(group)) { 275 return false; 276 } 277 if (group.querySelector('.form-float')) { 278 group.dataset.motoricaFloatMigrated = '1'; 279 group.classList.add('motorica-field-group'); 280 return false; 281 } 282 283 var control = findPrimaryControl(group); 284 if (!control) { 285 return false; 286 } 287 var label = findLabelForControl(group, control); 288 if (!label) { 289 return false; 290 } 291 if (label.classList.contains('sr-only')) { 292 return false; 293 } 294 295 return wrapControlAsFloat(group, control, label); 296 } 297 298 function migrateLabeledBlock(block) { 299 if (!block || block.dataset.motoricaFloatMigrated === '1') { 300 return false; 301 } 302 if (shouldSkipContainer(block)) { 303 return false; 304 } 305 if (!block.classList.contains('mb-3') && !block.classList.contains('mb-2') && !block.classList.contains('mb-4')) { 306 return false; 307 } 308 if (block.querySelector('.form-float')) { 309 return false; 310 } 311 var control = findPrimaryControl(block); 312 if (!control) { 313 return false; 314 } 315 var label = findLabelForControl(block, control); 316 if (!label) { 317 return false; 318 } 319 return wrapControlAsFloat(block, control, label); 320 } 321 322 function migrateRoot(root) { 323 if (!root || !root.querySelectorAll) { 324 return; 325 } 326 327 root.querySelectorAll('.form-group:not([data-motorica-float-migrated])').forEach(function (group) { 328 migrateFormGroup(group); 329 }); 330
331 root.querySelectorAll('.mb-3:not([data-motorica-float-migrated]), .mb-2:not([data-motorica-float-migrated]), .mb-4:not([data-motorica-float-migrated])').forEach(function (block) { 332 migrateLabeledBlock(block); 333 }); 334 335 root.querySelectorAll('.form-float--select2 select.form-control, select.motorica-select2, select[data-motorica-select2="1"]').forEach(function (select) { 336 var wrap = select.closest('.form-float'); 337 if (wrap && !wrap.classList.contains('form-float--select2')) { 338 wrap.classList.add('form-float--select2'); 339 } 340 if (isSelect2Candidate(select) && !select.classList.contains('select2-hidden-accessible')) { 341 reinitSelect2(select, {}); 342 } 343 }); 344 } 345 346 function init(scope) { 347 var root = scope && scope.querySelector ? scope : document; 348 if (typeof scope === 'string') { 349 root = document.querySelector(scope) || document; 350 } 351 352 migrateRoot(root); 353 354 root.querySelectorAll('select.form-control').forEach(function (select) { 355 syncSelectHasValue(select); 356 if (!select.dataset.motoricaFloatSelectBound) { 357 select.dataset.motoricaFloatSelectBound = '1'; 358 select.addEventListener('change', function () { 359 syncSelectHasValue(select); 360 }); 361 } 362 }); 363 364 root.querySelectorAll('.form-float input.form-control, .form-float input[type="text"], .form-float input[type="email"], .form-float input[type="tel"], .form-float input[type="number"], .form-float input[type="password"], .form-float input[type="date"], .form-float input[type="time"], .form-float input[type="datetime-local"]').forEach(function (input) { 365 syncInputHasValue(input); 366 if (!input.dataset.motoricaFloatInputBound) { 367 input.dataset.motoricaFloatInputBound = '1'; 368 input.addEventListener('input', function () { 369 syncInputHasValue(input); 370 }); 371 input.addEventListener('change', function () { 372 syncInputHasValue(input); 373 }); 374 } 375 }); 376 377 root.querySelectorAll('.form-float input[readonly], .form-float .motorica-settings-readonly').forEach(syncReadonlyFloat); 378 379 root.querySelectorAll('input[type="date"].form-control').forEach(function (input) { 380 if (String(input.value || '').trim() !== '' && !input.getAttribute('placeholder')) { 381 input.setAttribute('placeholder', ' '); 382 } 383 }); 384 385 try { 386 document.dispatchEvent(new CustomEvent('motorica:form-float-ready', { detail: { root: root } })); 387 } catch (e) { 388 /* IE11 fallback not required */ 389 } 390 } 391 392 function ensureScopes() { 393 var wrappers = document.querySelectorAll('#content-wrapper, .tableBackground, .login-content-wrap, .motorica-form-float-scope'); 394 wrappers.forEach(function (el) { 395 if (!el.classList.contains('motorica-form-float-scope')) { 396 el.classList.add('motorica-form-float-scope'); 397 } 398 }); 399 } 400 401 function boot() { 402 ensureScopes(); 403 document.querySelectorAll('.motorica-form-float-scope').forEach(function (scope) { 404 init(scope); 405 }); 406 } 407 408 var observerStarted = false; 409 410 function startObserver() { 411 if (observerStarted || typeof MutationObserver === 'undefined') { 412 return; 413 } 414 observerStarted = true; 415 var timer = null; 416 var observer = new MutationObserver(function (mutations) { 417 var needsInit = false; 418 mutations.forEach(function (mutation) { 419 if (mutation.addedNodes && mutation.addedNodes.length) { 420 needsInit = true; 421 } 422 }); 423 if (!needsInit) { 424 return; 425 } 426 clearTimeout(timer); 427 timer = setTimeout(function () { 428 document.querySelectorAll('.motorica-form-float-scope').forEach(function (scope) { 429 init(scope); 430 }); 431 }, 50); 432 }); 433 observer.observe(document.body, { childList: true, subtree: true }); 434 } 435 436 function scheduleBoot() { 437 if (document.readyState === 'loading') { 438 document.addEventListener('DOMContentLoaded', function () { 439 boot(); 440 startObserver(); 441 }); 442 } else { 443 boot(); 444 startObserver(); 445 } 446 } 447 448 window.MotoricaFormFloat = { 449 init: init, 450 migrate: migrateRoot, 451 syncSelect: syncSelectHasValue, 452 syncSelect2: function (select) { 453 syncSelectHasValue(select); 454 if ($ && window.MotoricaFormSelect2 && typeof window.MotoricaFormSelect2.sync === 'function') { 455 window.MotoricaFormSelect2.sync($(select)); 456 } 457 }, 458 }; 459 460 scheduleBoot(); 461})(window, window.jQuery);
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.