PageSourceSearch

https://www.predictvia.com/js/main.js

js predictvia.com collected 2026-10-09 04:05:52 UTC 7,573 bytes, 270 lines download raw bytes

1/* ==========================================================================
2   Predictvia - Main JavaScript
3   ========================================================================== */
4
5document.addEventListener('DOMContentLoaded', () => {
6  initNavigation();
7  initScrollAnimations();
8  // Only init canvas network if element exists (on How We Work page)
9  if (document.getElementById('network-canvas')) {
10    initCanvasNetwork();
11  }
12});
13
14/* --------------------------------------------------------------------------
15   Navigation
16   -------------------------------------------------------------------------- */
17function initNavigation() {
18  const nav = document.querySelector('.nav');
19  const topBar = document.querySelector('.top-bar');
20  const toggle = document.querySelector('.nav__toggle');
21  const links = document.querySelector('.nav__links');
22  
23  // Scroll effect - hide top bar and adjust nav on scroll
24  let lastScroll = 0;
25  window.addEventListener('scroll', () => {
26    const currentScroll = window.pageYOffset;
27    
28    if (currentScroll > 50) {
29      nav.classList.add('scrolled');
30      if (topBar) topBar.classList.add('hidden');
31    } else {
32      nav.classList.remove('scrolled');
33      if (topBar) topBar.classList.remove('hidden');
34    }
35    
36    lastScroll = currentScroll;
37  });
38  
39  // Mobile menu toggle
40  if (toggle && links) {
41    toggle.addEventListener('click', () => {
42      links.classList.toggle('open');
43      toggle.classList.toggle('active');
44    });
45    
46    // Close menu when clicking a link
47    links.querySelectorAll('a').forEach(link => {
48      link.addEventListener('click', () => {
49        links.classList.remove('open');
50        toggle.classList.remove('active');
51      });
52    });
53  }
54}
55
56/* --------------------------------------------------------------------------
57   Scroll Animations
58   -------------------------------------------------------------------------- */
59function initScrollAnimations() {
60  const observerOptions = {
61    root: null,
62    rootMargin: '0px',
63    threshold: 0.1
64  };
65  
66  const observer = new IntersectionObserver((entries) => {
67    entries.forEach(entry => {
68      if (entry.isIntersecting) {
69        entry.target.classList.add('visible');
70      }
71    });
72  }, observerOptions);
73  
74  // Observe fade-in elements
75  document.querySelectorAll('.fade-in, .stagger-children').forEach(el => {
76    observer.observe(el);
77  });
78}
79
80/* --------------------------------------------------------------------------
81   Canvas Network Animation (Background)
82   -------------------------------------------------------------------------- */
83function initCanvasNetwork() {
84  const canvas = document.getElementById('network-canvas');
85  if (!canvas) return;
86  
87  const ctx = canvas.getContext('2d');
88  let animationId;
89  let nodes = [];
90  let edges = [];
91  let pulses = [];
92  
93  // Colors
94  const colors = {
95    node: '#3B82F6',
96    nodeDim: 'rgba(59, 130, 246, 0.3)',
97    edge: 'rgba(255, 255, 255, 0.1)',
98    pulse: '#3B82F6',
99    text: 'rgba(255, 255, 255, 0.6)'
100  };
101  
102  // Node labels
103  const labels = [
104    'Ops Agent',
105    'Data Pipeline',
106    'Compliance',
107    'Analytics',
108    'Monitoring',
109    'Orchestrator'
110  ];
111  
112  function resize() {
113    const rect = canvas.parentElement.getBoundingClientRect();
114    canvas.width = rect.width * window.devicePixelRatio;
115    canvas.height = rect.height * window.devicePixelRatio;
116    canvas.style.width = rect.width + 'px';
117    canvas.style.height = rect.height + 'px';
118    ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
119    
120    initNodes(rect.width, rect.height);
121  }
122  
123  function initNodes(width, height) {
124    nodes = [];
125    edges = [];
126    
127    const centerX = width / 2;
128    const centerY = height / 2;
129    const radius = Math.min(width, height) * 0.35;
130    
131    // Create nodes in a circular pattern
132    const nodeCount = 6;
133    for (let i = 0; i < nodeCount; i++) {
134      const angle = (i / nodeCount) * Math.PI * 2 - Math.PI / 2;
135      const x = centerX + Math.cos(angle) * radius;
136      const y = centerY + Math.sin(angle) * radius;
137      
138      nodes.push({
139        x,
140        y,
141        radius: i === 0 ? 12 : 8,
142        label: labels[i],
143        isMain: i === 0,
144        pulsePhase: Math.random() * Math.PI * 2,
145        hover: false
146      });
147    }
148    
149    // Add center node (orchestrator)
150    nodes.push({
151      x: centerX,
152      y: centerY,
153      radius: 14,
154      label: 'Core System',
155      isMain: true,
156      pulsePhase: 0,
157      hover: false
158    });
159    
160    // Create edges from center to all nodes
161    const centerNode = nodes[nodes.length - 1];
162    for (let i = 0; i < nodes.length - 1; i++) {
163      edges.push({
164        from: centerNode,
165        to: nodes[i],
166        pulseOffset: i * 0.3
167      });
168    }
169    
170    // Add some connections between adjacent nodes
171    for (let i = 0; i < nodes.length - 2; i++) {
172      edges.push({
173        from: nodes[i],
174        to: nodes[(i + 1) % (nodes.length - 1)],
175        pulseOffset: i * 0.5 + 2
176      });
177    }
178  }
179  
180  function drawNode(node, time) {
181    // Glow effect
182    const glowIntensity = 0.3 + 0.2 * Math.sin(time * 0.002 + node.pulsePhase);
183    const gradient = ctx.createRadialGradient(
184      node.x, node.y, 0,
185      node.x, node.y, node.radius * 3
186    );
187    gradient.addColorStop(0, `rgba(59, 130, 246, ${glowIntensity})`);
188    gradient.addColorStop(1, 'rgba(59, 130, 246, 0)');
189    
190    ctx.beginPath();
191    ctx.arc(node.x, node.y, node.radius * 3, 0, Math.PI * 2);
192    ctx.fillStyle = gradient;
193    ctx.fill();
194    
195    // Node circle
196    ctx.beginPath();
197    ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
198    ctx.fillStyle = node.isMain ? colors.node : colors.nodeDim;
199    ctx.fill();
200    
201    // Inner highlight
202    ctx.beginPath();
203    ctx.arc(node.x, node.y, node.radius * 0.5, 0, Math.PI * 2);
204    ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
205    ctx.fill();
206  }
207  
208  function drawEdge(edge, time) {
209    ctx.beginPath();
210    ctx.moveTo(edge.from.x, edge.from.y);
211    ctx.lineTo(edge.to.x, edge.to.y);
212    ctx.strokeStyle = colors.edge;
213    ctx.lineWidth = 1;
214    ctx.stroke();
215    
216    // Draw pulse
217    const pulseProgress = ((time * 0.0005 + edge.pulseOffset) % 1);
218    const pulseX = edge.from.x + (edge.to.x - edge.from.x) * pulseProgress;
219    const pulseY = edge.from.y + (edge.to.y - edge.from.y) * pulseProgress;
220    
221    const pulseGradient = ctx.createRadialGradient(
222      pulseX, pulseY, 0,
223      pulseX, pulseY, 8
224    );
225    pulseGradient.addColorStop(0, 'rgba(59, 130, 246, 0.8)');
226    pulseGradient.addColorStop(1, 'rgba(59, 130, 246, 0)');
227    
228    ctx.beginPath();
229    ctx.arc(pulseX, pulseY, 8, 0, Math.PI * 2);
230    ctx.fillStyle = pulseGradient;
231    ctx.fill();
232  }
233  
234  function draw(time) {
235    const rect = canvas.getBoundingClientRect();
236    ctx.clearRect(0, 0, rect.width, rect.height);
237    
238    // Draw edges first
239    edges.forEach(edge => drawEdge(edge, time));
240    
241    // Draw nodes on top
242    nodes.forEach(node => drawNode(node, time));
243    
244    animationId = requestAnimationFrame(draw);
245  }
246  
247  // Handle hover
248  canvas.addEventListener('mousemove', (e) => {
249    const rect = canvas.getBoundingClientRect();
250    const x = e.clientX - rect.left;
251    const y = e.clientY - rect.top;
252    
253    nodes.forEach(node => {
254      const dist = Math.sqrt((x - node.x) ** 2 + (y - node.y) ** 2);
255      node.hover = dist < node.radius * 2;
256    });
257  });
258  
259  // Initialize
260  resize();
261  draw(0);
262  
263  // Handle resize
264  window.addEventListener('resize', () => {
265    cancelAnimationFrame(animationId);
266    resize();
267    draw(0);
268  });
269}
270

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.