1(function () { 2 'use strict'; 3 4 var reduceMotion = window.matchMedia && 5 window.matchMedia('(prefers-reduced-motion: reduce)').matches; 6 var mobileQuery = window.matchMedia ? window.matchMedia('(max-width: 767px)') : null; 7 8 function NeuralCanvas(root) { 9 this.root = root; 10 this.canvas = root.querySelector('.zntech-neural-canvas__canvas'); 11 this.ctx = this.canvas ? this.canvas.getContext('2d') : null; 12 this.nodes = []; 13 this.raf = 0; 14 this.width = 0; 15 this.height = 0; 16 this.dpr = 1; 17 this.hidden = false; 18 this.time = 0; 19 this.focusBox = null; 20 this.focusTargetBox = null; 21 this.options = readOptions(root); 22 23 if (!this.canvas || !this.ctx) { 24 return; 25 } 26 27 mountBackground(root); 28 29 if (isMobileViewport()) { 30 root.classList.add('zntech-neural-canvas--disabled-mobile'); 31 return; 32 } 33 34 this.resize(); 35 this.bind(); 36 37 if (isMobileViewport() && this.options.mobile === 'static') { 38 this.drawStatic(); 39 } else if (!reduceMotion) { 40 this.draw(); 41 } else { 42 this.drawStatic(); 43 } 44 } 45 46 NeuralCanvas.prototype.bind = function () { 47 var self = this; 48 49 if (window.ResizeObserver) { 50 this.resizeObserver = new ResizeObserver(function () { 51 self.resize(); 52 }); 53 this.resizeObserver.observe(this.root); 54 } else { 55 window.addEventListener('resize', function () { 56 self.resize(); 57 }); 58 } 59 60 document.addEventListener('visibilitychange', function () { 61 self.hidden = document.hidden; 62 63 if (self.hidden) { 64 window.cancelAnimationFrame(self.raf); 65 self.raf = 0; 66 return; 67 } 68 69 if (!reduceMotion && !self.raf) { 70 self.draw(); 71 } 72 }); 73 74 this.root.addEventListener('zntech-neural-update', function () { 75 self.options = readOptions(self.root); 76 self.initNodes(); 77 78 if (reduceMotion) { 79 self.drawStatic(); 80 } 81 }); 82 }; 83 84 NeuralCanvas.prototype.resize = function () { 85 var rect = this.root.getBoundingClientRect(); 86 var width = Math.max(1, Math.floor(rect.width || this.root.offsetWidth || window.innerWidth)); 87 var height = Math.max(1, Math.floor(rect.height || this.root.offsetHeight || this.options.height || 520)); 88 89 if (width === this.width && height === this.height) { 90 return; 91 } 92 93 this.width = width; 94 this.height = height; 95 this.dpr = Math.min(2, window.devicePixelRatio || 1); 96 this.canvas.width = Math.floor(width * this.dpr); 97 this.canvas.height = Math.floor(height * this.dpr); 98 this.canvas.style.width = width + 'px'; 99 this.canvas.style.height = height + 'px'; 100 this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0); 101 this.initNodes(); 102 103 if (reduceMotion) { 104 this.drawStatic(); 105 } 106 }; 107 108 NeuralCanvas.prototype.initNodes = function () { 109 var focus = getFocusBoxes(this); 110 var areaWidth = focus.box ? focus.box.width : this.width; 111 var areaHeight = focus.box ? focus.box.height : this.height; 112 var count = Math.min( 113 this.options.maxNodes, 114 Math.max(8, Math.floor((areaWidth * areaHeight) / this.options.density)) 115 ); 116 117 if (focus.box && this.options.effect === 'circuit') { 118 count = Math.min(this.options.maxNodes, Math.max(20, Math.round(count * 1.75))); 119 } 120 121 this.focusBox = focus.box; 122 this.focusTargetBox = focus.targetBox; 123 this.nodes = []; 124 125 for (var i = 0; i < count; i += 1) { 126 var paint = pickNodePaint(this.options); 127 var point = focusedPoint(this); 128 129 this.nodes.push({ 130 x: point.x, 131 y: point.y, 132 vx: (Math.random() - 0.5) * this.options.speed, 133 vy: (Math.random() - 0.5) * this.options.speed, 134 r: (Math.random() * 2 + 1) * this.options.nodeScale, 135 bounds: point.bounds || null, 136 colorType: paint.type, 137 dotColor: paint.dotColor, 138 glowColor: paint.glowColor 139 }); 140 } 141 }; 142 143 NeuralCanvas.prototype.drawStatic = function () { 144 this.paint(false); 145 }; 146 147 NeuralCanvas.prototype.draw = function () { 148 var self = this; 149 150 if (this.hidden || isMobileViewport()) { 151 this.root.classList.add('zntech-neural-canvas--disabled-mobile'); 152 this.raf = 0; 153 return; 154 } 155 156 this.time = window.performance ? window.performance.now() : Date.now(); 157 this.paint(true); 158 this.raf = window.requestAnimationFrame(function () { 159 self.draw(); 160 }); 161 }; 162 163 NeuralCanvas.prototype.paint = function (animate) { 164 var ctx = this.ctx; 165 var options = this.options; 166 167 ctx.clearRect(0, 0, this.width, this.height); 168 169 if (options.background !== 'transparent') { 170 ctx.fillStyle = options.background; 171 ctx.fillRect(0, 0, this.width, this.height); 172 } 173 174 if (options.effect === 'circuit') { 175 this.paintCircuit(animate); 176 return; 177 } 178 179 if (options.effect === 'scanner') { 180 this.paintNetwork(animate, 0.68); 181 this.paintScanner(); 182 return; 183 } 184 185 if (options.effect === 'hex') { 186 this.paintHex(); 187 return; 188 } 189 190 this.paintNetwork(animate, 1); 191 }; 192 193 NeuralCanvas.prototype.paintNetwork = function (animate, intensity) { 194 var ctx = this.ctx; 195 var options = this.options; 196 var lineAlpha = options.lineAlpha * (intensity || 1); 197 198 for (var i = 0; i < this.nodes.length; i += 1) { 199 var a = this.nodes[i]; 200 201 if (animate) { 202 a.x += a.vx; 203 a.y += a.vy; 204 205 if (a.x < 0 || a.x > this.width) { 206 a.vx *= -1; 207 } 208 209 if (a.y < 0 || a.y > this.height) { 210 a.vy *= -1; 211 } 212 } 213 214 for (var j = i + 1; j < this.nodes.length; j += 1) { 215 var b = this.nodes[j]; 216 var dx = a.x - b.x; 217 var dy = a.y - b.y; 218 var distance = Math.sqrt(dx * dx + dy * dy); 219 220 if (distance < options.lineDistance) { 221 ctx.strokeStyle = 'rgba(' + lineColorFor(a, b, options) + ',' + ((1 - distance / options.lineDistance) * lineAlpha) + ')'; 222 ctx.lineWidth = 1; 223 ctx.beginPath(); 224 ctx.moveTo(a.x, a.y); 225 ctx.lineTo(b.x, b.y); 226 ctx.stroke(); 227 } 228 } 229 230 var gradient = ctx.createRadialGradient(a.x, a.y, 0, a.x, a.y, a.r * 3); 231 gradient.addColorStop(0, 'rgba(' + a.dotColor + ',0.85)'); 232 gradient.addColorStop(1, 'rgba(' + a.glowColor + ',0)'); 233 ctx.fillStyle = gradient; 234 ctx.beginPath(); 235 ctx.arc(a.x, a.y, a.r * 2.4, 0, Math.PI * 2); 236 ctx.fill(); 237 } 238 }; 239 240 NeuralCanvas.prototype.paintCircuit = function (animate) { 241 var ctx = this.ctx; 242 var options = this.options; 243 var maxLinksPerNode = 3; 244 var clipBox = this.focusBox && options.focus.indexOf('panel') === 0 ? this.focusBox : null; 245
246 if (clipBox) { 247 ctx.save(); 248 ctx.beginPath(); 249 ctx.rect(clipBox.x, clipBox.y, clipBox.width, clipBox.height); 250 ctx.clip(); 251 } 252 253 for (var i = 0; i < this.nodes.length; i += 1) { 254 var a = this.nodes[i]; 255 var links = 0; 256 257 if (animate) { 258 a.x += a.vx * 0.18; 259 a.y += a.vy * 0.18; 260 bounceNode(a, a.bounds || clipBox || { x: 0, y: 0, width: this.width, height: this.height }); 261 } 262 263 for (var j = i + 1; j < this.nodes.length && links < maxLinksPerNode; j += 1) { 264 var b = this.nodes[j]; 265 var dx = a.x - b.x; 266 var dy = a.y - b.y; 267 var distance = Math.sqrt(dx * dx + dy * dy); 268 var limit = options.lineDistance * 1.18; 269 270 if (distance >= limit) { 271 continue; 272 } 273 274 links += 1; 275 var horizontalFirst = ((i + j) % 2) === 0; 276 var mid = horizontalFirst ? { x: b.x, y: a.y } : { x: a.x, y: b.y }; 277 var alpha = (1 - distance / limit) * options.lineAlpha * 0.92; 278 var color = lineColorFor(a, b, options); 279 280 ctx.strokeStyle = 'rgba(' + color + ',' + alpha + ')'; 281 ctx.lineWidth = Math.max(1, Math.min(1.25, options.nodeScale)); 282 ctx.beginPath(); 283 ctx.moveTo(a.x, a.y); 284 ctx.lineTo(mid.x, mid.y); 285 ctx.lineTo(b.x, b.y); 286 ctx.stroke(); 287 288 var pulsePhase = (this.time * options.pulseSpeed / 5200 + i * 0.13 + j * 0.07) % 1; 289 var pulse = pointOnPolyline(a, mid, b, pulsePhase); 290 291 ctx.fillStyle = 'rgba(' + color + ',0.55)'; 292 ctx.beginPath(); 293 ctx.arc(pulse.x, pulse.y, 2.2 * options.nodeScale, 0, Math.PI * 2); 294 ctx.fill(); 295 } 296 297 drawCircuitNode(ctx, a); 298 } 299 300 if (clipBox) { 301 ctx.restore(); 302 } 303 }; 304 305 NeuralCanvas.prototype.paintScanner = function () { 306 var ctx = this.ctx; 307 var x = ((this.time * 0.06) % (this.width + 260)) - 130; 308 var width = 150; 309 var gradient = ctx.createLinearGradient(x - width, 0, x + width, 0); 310 311 gradient.addColorStop(0, 'rgba(190,25,90,0)'); 312 gradient.addColorStop(0.5, 'rgba(190,25,90,0.11)'); 313 gradient.addColorStop(1, 'rgba(190,25,90,0)'); 314 315 ctx.fillStyle = gradient; 316 ctx.fillRect(x - width, 0, width * 2, this.height); 317 318 ctx.strokeStyle = 'rgba(190,25,90,0.32)'; 319 ctx.lineWidth = 1; 320 ctx.beginPath(); 321 ctx.moveTo(x, 0); 322 ctx.lineTo(x, this.height); 323 ctx.stroke(); 324 }; 325 326 NeuralCanvas.prototype.paintHex = function () { 327 var ctx = this.ctx; 328 var options = this.options; 329 var radius = Math.max(42, Math.min(78, Math.floor(Math.min(this.width, this.height) / 11))); 330 var hexHeight = Math.sqrt(3) * radius; 331 var stepX = radius * 1.5; 332 var time = this.time / 1000; 333 334 ctx.lineWidth = 1; 335 336 for (var y = -hexHeight; y < this.height + hexHeight; y += hexHeight) { 337 var row = Math.round(y / hexHeight); 338 339 for (var x = -radius; x < this.width + radius; x += stepX) { 340 var cx = x + (row % 2 ? radius * 0.75 : 0); 341 var cy = y; 342 var wave = (Math.sin(time * 0.9 + cx * 0.012 + cy * 0.01) + 1) / 2; 343 var alpha = 0.045 + wave * options.lineAlpha * 0.23; 344 345 ctx.strokeStyle = 'rgba(' + options.lineColor + ',' + alpha + ')'; 346 drawHexPath(ctx, cx, cy, radius); 347 ctx.stroke(); 348 349 if (wave > 0.92) { 350 ctx.fillStyle = 'rgba(' + options.redColor + ',0.52)'; 351 ctx.beginPath(); 352 ctx.arc(cx + radius, cy, 2.4, 0, Math.PI * 2); 353 ctx.fill(); 354 } else if (wave > 0.82) { 355 ctx.fillStyle = 'rgba(' + options.blueGrayColor + ',0.38)'; 356 ctx.beginPath();
357 ctx.arc(cx - radius * 0.5, cy + hexHeight * 0.5, 2.1, 0, Math.PI * 2); 358 ctx.fill(); 359 } 360 } 361 } 362 }; 363 364 function readOptions(root) { 365 return { 366 density: readNumber(root, 'density', 17000), 367 maxNodes: readNumber(root, 'maxNodes', 64), 368 speed: readFloat(root, 'speed', 0.4), 369 pulseSpeed: clamp(readFloat(root, 'pulseSpeed', 1), 0, 2), 370 palette: root.dataset.palette === 'brand' ? 'brand' : 'dark', 371 effect: readEffect(root), 372 focus: readFocus(root), 373 focusSelector: root.dataset.focusSelector || '.zntech-hero-stage__panel', 374 focusPadding: readNumber(root, 'focusPadding', 190), 375 nodeScale: clamp(readFloat(root, 'nodeScale', 1), 0.6, 2), 376 mobile: readMobile(root), 377 lineDistance: readNumber(root, 'lineDistance', 130), 378 lineAlpha: readFloat(root, 'lineAlpha', 0.32), 379 lineColor: readRgb(root, 'lineColor', '30,40,75'), 380 dotColor: readRgb(root, 'dotColor', '30,40,75'), 381 glowColor: readRgb(root, 'glowColor', '64,88,145'), 382 redColor: readRgb(root, 'redColor', '190,25,90'), 383 blueGrayColor: readRgb(root, 'blueGrayColor', '125,145,170'), 384 redRatio: clamp(readFloat(root, 'redRatio', 0.1), 0, 0.35), 385 blueGrayRatio: clamp(readFloat(root, 'blueGrayRatio', 0.2), 0, 0.55), 386 background: root.dataset.background || 'transparent', 387 height: parseInt(getComputedStyle(root).getPropertyValue('--zntech-neural-height'), 10) || 520 388 }; 389 } 390 391 function readEffect(root) { 392 var effect = root.dataset.effect || 'network'; 393 394 return /^(network|circuit|scanner|hex)$/.test(effect) ? effect : 'network'; 395 } 396 397 function readFocus(root) { 398 var focus = root.dataset.focus || 'none'; 399 400 return /^(none|center|right|panel|panel-right-bottom)$/.test(focus) ? focus : 'none'; 401 } 402 403 function readMobile(root) { 404 var mobile = root.dataset.mobile || 'off'; 405 406 return /^(off|static|on)$/.test(mobile) ? mobile : 'off'; 407 } 408 409 function isMobileViewport() { 410 return !!(mobileQuery && mobileQuery.matches); 411 } 412 413 function pickNodePaint(options) { 414 if (options.palette !== 'brand') { 415 return { 416 type: 'dark', 417 dotColor: options.dotColor, 418 glowColor: options.glowColor 419 }; 420 } 421 422 var redRatio = options.redRatio; 423 var blueGrayRatio = Math.min(options.blueGrayRatio, Math.max(0, 0.9 - redRatio)); 424 var roll = Math.random(); 425 426 if (roll < redRatio) { 427 return { 428 type: 'red', 429 dotColor: options.redColor, 430 glowColor: options.redColor 431 }; 432 } 433 434 if (roll < redRatio + blueGrayRatio) { 435 return { 436 type: 'blue-gray', 437 dotColor: options.blueGrayColor, 438 glowColor: options.blueGrayColor 439 }; 440 } 441 442 return { 443 type: 'dark', 444 dotColor: options.dotColor, 445 glowColor: options.glowColor 446 }; 447 } 448 449 function lineColorFor(a, b, options) { 450 if (options.palette !== 'brand') { 451 return options.lineColor; 452 } 453 454 if (a.colorType === 'red' || b.colorType === 'red') { 455 return options.redColor; 456 } 457 458 if (a.colorType === 'blue-gray' || b.colorType === 'blue-gray') { 459 return options.blueGrayColor; 460 } 461 462 return options.lineColor; 463 } 464 465 function drawCircuitNode(ctx, node) { 466 var size = Math.max(3, node.r * 2.3); 467 var half = size / 2; 468 469 ctx.fillStyle = 'rgba(' + node.dotColor + ',0.48)'; 470 ctx.fillRect(node.x - half, node.y - half, size, size); 471 472 ctx.strokeStyle = 'rgba(' + node.glowColor + ',0.2)'; 473 ctx.strokeRect(node.x - half - 2, node.y - half - 2, size + 4, size + 4); 474 } 475 476 function bounceNode(node, box) { 477 var minX = box.x; 478 var maxX = box.x + box.width; 479 var minY = box.y; 480 var maxY = box.y + box.height; 481 482 if (node.x < minX) { 483 node.x = minX; 484 node.vx = Math.abs(node.vx); 485 } else if (node.x > maxX) { 486 node.x = maxX; 487 node.vx = -Math.abs(node.vx); 488 } 489 490 if (node.y < minY) { 491 node.y = minY; 492 node.vy = Math.abs(node.vy); 493 } else if (node.y > maxY) { 494 node.y = maxY; 495 node.vy = -Math.abs(node.vy); 496 } 497 } 498 499 function getFocusBoxes(instance) { 500 var options = instance.options; 501 var rootBox = instance.root.getBoundingClientRect(); 502 var pad = Math.max(40, options.focusPadding || 130); 503 var targetBox = null; 504 var box = null; 505 506 if (options.focus === 'panel' || options.focus === 'panel-right-bottom') { 507 targetBox = panelFocusBox(instance.root, rootBox, options.focusSelector); 508 509 if (targetBox) { 510 box = clampBox({ 511 x: targetBox.x - pad, 512 y: targetBox.y - pad, 513 width: targetBox.width + pad * 2, 514 height: targetBox.height + pad * 2 515 }, instance.width, instance.height); 516 } 517 } else if (options.focus === 'right') { 518 box = { 519 x: instance.width * 0.48, 520 y: instance.height * 0.06, 521 width: instance.width * 0.5, 522 height: instance.height * 0.88 523 }; 524 } else if (options.focus === 'center') { 525 box = { 526 x: instance.width * 0.18, 527 y: instance.height * 0.12, 528 width: instance.width * 0.64, 529 height: instance.height * 0.76 530 }; 531 } 532 533 return { 534 box: box ? clampBox(box, instance.width, instance.height) : null, 535 targetBox: targetBox 536 }; 537 } 538 539 function panelFocusBox(root, rootBox, selector) { 540 var target = findFocusTarget(root, selector); 541 542 if (!target) { 543 return null; 544 } 545 546 var rect = target.getBoundingClientRect(); 547 548 if (!rect.width || !rect.height) { 549 return null; 550 } 551 552 return { 553 x: rect.left - rootBox.left, 554 y: rect.top - rootBox.top, 555 width: rect.width, 556 height: rect.height 557 }; 558 } 559 560 function findFocusTarget(root, selector) { 561 if (!selector) { 562 return null; 563 } 564 565 try { 566 if (root.parentElement) { 567 var local = root.parentElement.querySelector(selector); 568 569 if (local) { 570 return local; 571 } 572 } 573 574 return document.querySelector(selector); 575 } catch (error) { 576 return null; 577 } 578 } 579 580 function focusedPoint(instance) { 581 if (instance.focusTargetBox && instance.options.focus === 'panel-right-bottom') { 582 return pointRightBottomOfTarget(instance.focusTargetBox, instance.options.focusPadding, instance.width, instance.height); 583 } 584 585 if (instance.focusTargetBox && instance.options.focus === 'panel') { 586 return pointAroundTarget(instance.focusTargetBox, instance.options.focusPadding, instance.width, instance.height); 587 } 588 589 if (instance.focusBox) { 590 return randomPointInBox(instance.focusBox, instance.width, instance.height); 591 } 592 593 return { 594 x: Math.random() * instance.width, 595 y: Math.random() * instance.height 596 }; 597 } 598 599 function pointAroundTarget(target, padding, maxWidth, maxHeight) { 600 var pad = Math.max(40, padding || 130); 601 var gap = Math.min(28, pad * 0.18); 602 var side = Math.floor(Math.random() * 4); 603 var x; 604 var y; 605 606 if (side === 0) { 607 x = randomBetween(target.x - pad * 0.22, target.x + target.width + pad * 0.22);
608 y = randomBetween(target.y - pad, target.y - gap); 609 } else if (side === 1) { 610 x = randomBetween(target.x + target.width + gap, target.x + target.width + pad); 611 y = randomBetween(target.y - pad * 0.18, target.y + target.height + pad * 0.18); 612 } else if (side === 2) { 613 x = randomBetween(target.x - pad * 0.22, target.x + target.width + pad * 0.22); 614 y = randomBetween(target.y + target.height + gap, target.y + target.height + pad); 615 } else { 616 x = randomBetween(target.x - pad, target.x - gap); 617 y = randomBetween(target.y - pad * 0.18, target.y + target.height + pad * 0.18); 618 } 619 620 return { 621 x: clamp(x, 0, maxWidth), 622 y: clamp(y, 0, maxHeight) 623 }; 624 } 625 626 function pointRightBottomOfTarget(target, padding, maxWidth, maxHeight) { 627 var pad = Math.max(40, padding || 130); 628 var gap = Math.min(28, pad * 0.18); 629 var rightBox = clampBox({ 630 x: target.x + target.width + gap, 631 y: target.y + target.height * 0.04, 632 width: pad - gap, 633 height: target.height * 0.96 + pad * 0.32 634 }, maxWidth, maxHeight); 635 var bottomBox = clampBox({ 636 x: target.x - pad * 0.08, 637 y: target.y + target.height + gap, 638 width: target.width + pad * 0.36, 639 height: pad - gap 640 }, maxWidth, maxHeight); 641 var box = Math.random() < 0.54 ? rightBox : bottomBox; 642 var point = randomPointInBox(box, maxWidth, maxHeight); 643 644 point.bounds = box; 645 return point; 646 } 647 648 function randomPointInBox(box, maxWidth, maxHeight) { 649 return { 650 x: clamp(randomBetween(box.x, box.x + box.width), 0, maxWidth), 651 y: clamp(randomBetween(box.y, box.y + box.height), 0, maxHeight) 652 }; 653 } 654 655 function randomBetween(min, max) { 656 if (max < min) { 657 var tmp = min; 658 min = max; 659 max = tmp; 660 } 661 662 return min + Math.random() * (max - min); 663 } 664 665 function clampBox(box, maxWidth, maxHeight) { 666 var x = clamp(box.x, 0, maxWidth); 667 var y = clamp(box.y, 0, maxHeight); 668 var right = clamp(box.x + box.width, 0, maxWidth); 669 var bottom = clamp(box.y + box.height, 0, maxHeight); 670 671 return { 672 x: x, 673 y: y, 674 width: Math.max(1, right - x), 675 height: Math.max(1, bottom - y) 676 }; 677 } 678 679 function pointOnPolyline(a, mid, b, t) { 680 var firstLength = distanceBetween(a, mid); 681 var secondLength = distanceBetween(mid, b); 682 var total = firstLength + secondLength || 1; 683 var target = total * t; 684 685 if (target <= firstLength) { 686 return lerpPoint(a, mid, target / (firstLength || 1)); 687 } 688 689 return lerpPoint(mid, b, (target - firstLength) / (secondLength || 1)); 690 } 691 692 function distanceBetween(a, b) { 693 var dx = a.x - b.x; 694 var dy = a.y - b.y; 695 696 return Math.sqrt(dx * dx + dy * dy); 697 } 698 699 function lerpPoint(a, b, t) { 700 return { 701 x: a.x + (b.x - a.x) * t, 702 y: a.y + (b.y - a.y) * t 703 }; 704 } 705 706 function drawHexPath(ctx, cx, cy, radius) { 707 ctx.beginPath(); 708 709 for (var i = 0; i < 6; i += 1) { 710 var angle = Math.PI / 3 * i; 711 var x = cx + Math.cos(angle) * radius; 712 var y = cy + Math.sin(angle) * radius; 713 714 if (i === 0) { 715 ctx.moveTo(x, y); 716 } else { 717 ctx.lineTo(x, y); 718 } 719 } 720 721 ctx.closePath(); 722 } 723 724 function mountBackground(root) { 725 if (!root.classList.contains('zntech-neural-canvas--background')) { 726 return; 727 } 728 729 var mount = root.dataset.mount || 'elementor'; 730 var host = null; 731 732 if (mount === 'parent') { 733 host = root.parentElement; 734 } else if (mount === 'elementor') { 735 host = root.closest('.e-con, .elementor-section, .elementor-top-section, .elementor-container'); 736 } 737 738 if (!host || host === root || root.parentElement === host) { 739 if (host) { 740 host.classList.add('zntech-neural-canvas-host'); 741 } 742 return; 743 } 744 745 host.classList.add('zntech-neural-canvas-host'); 746 host.insertBefore(root, host.firstChild); 747 } 748 749 function readNumber(root, key, fallback) { 750 var value = parseInt(root.dataset[key], 10); 751 return Number.isFinite(value) ? value : fallback; 752 } 753 754 function readFloat(root, key, fallback) { 755 var value = parseFloat(root.dataset[key]); 756 return Number.isFinite(value) ? value : fallback; 757 } 758 759 function clamp(value, min, max) { 760 return Math.min(max, Math.max(min, value)); 761 } 762 763 function readRgb(root, key, fallback) { 764 var value = root.dataset[key] || fallback; 765 return /^\d{1,3},\d{1,3},\d{1,3}$/.test(value) ? value : fallback; 766 } 767
768 function init() { 769 var roots = document.querySelectorAll('.zntech-neural-canvas'); 770 771 roots.forEach(function (root) { 772 if (root.dataset.zntechNeuralReady === '1') { 773 return; 774 } 775 776 root.dataset.zntechNeuralReady = '1'; 777 new NeuralCanvas(root); 778 }); 779 } 780 781 if (document.readyState === 'loading') { 782 document.addEventListener('DOMContentLoaded', init); 783 } else { 784 init(); 785 } 786})();
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.