Updated women leading the way section
This commit is contained in:
@@ -1,189 +1,158 @@
|
||||
/*! elementor - v3.32.0 - 05-10-2025 */
|
||||
"use strict";
|
||||
(self.webpackChunkelementorFrontend = self.webpackChunkelementorFrontend || []).push([[131], {
|
||||
5249: (e, t, a) => {
|
||||
Object.defineProperty(t, "__esModule", {
|
||||
value: !0
|
||||
}),
|
||||
t.default = void 0,
|
||||
a(6281),
|
||||
a(4846),
|
||||
a(7458),
|
||||
a(6211);
|
||||
class baseTabs extends elementorModules.frontend.handlers.Base {
|
||||
getDefaultSettings() {
|
||||
return {
|
||||
selectors: {
|
||||
tablist: '[role="tablist"]',
|
||||
tabTitle: ".elementor-tab-title",
|
||||
tabContent: ".elementor-tab-content"
|
||||
},
|
||||
classes: {
|
||||
active: "elementor-active"
|
||||
},
|
||||
showTabFn: "show",
|
||||
hideTabFn: "hide",
|
||||
toggleSelf: !0,
|
||||
hidePrevious: !0,
|
||||
autoExpand: !0,
|
||||
keyDirection: {
|
||||
ArrowLeft: elementorFrontendConfig.is_rtl ? 1 : -1,
|
||||
ArrowUp: -1,
|
||||
ArrowRight: elementorFrontendConfig.is_rtl ? -1 : 1,
|
||||
ArrowDown: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
getDefaultElements() {
|
||||
const e = this.getSettings("selectors");
|
||||
return {
|
||||
$tabTitles: this.findElement(e.tabTitle),
|
||||
$tabContents: this.findElement(e.tabContent)
|
||||
}
|
||||
}
|
||||
activateDefaultTab() {
|
||||
const e = this.getSettings();
|
||||
if (!e.autoExpand || "editor" === e.autoExpand && !this.isEdit)
|
||||
return;
|
||||
const t = this.getEditSettings("activeItemIndex") || 1
|
||||
, a = {
|
||||
showTabFn: e.showTabFn,
|
||||
hideTabFn: e.hideTabFn
|
||||
};
|
||||
this.setSettings({
|
||||
showTabFn: "show",
|
||||
hideTabFn: "hide"
|
||||
}),
|
||||
this.changeActiveTab(t),
|
||||
this.setSettings(a)
|
||||
}
|
||||
handleKeyboardNavigation(e) {
|
||||
const t = e.currentTarget
|
||||
, a = jQuery(t.closest(this.getSettings("selectors").tablist))
|
||||
, i = a.find(this.getSettings("selectors").tabTitle)
|
||||
, s = "vertical" === a.attr("aria-orientation");
|
||||
switch (e.key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
if (s)
|
||||
return;
|
||||
break;
|
||||
case "ArrowUp":
|
||||
case "ArrowDown":
|
||||
if (!s)
|
||||
return;
|
||||
e.preventDefault();
|
||||
break;
|
||||
case "Home":
|
||||
return e.preventDefault(),
|
||||
void i.first().trigger("focus");
|
||||
case "End":
|
||||
return e.preventDefault(),
|
||||
void i.last().trigger("focus");
|
||||
default:
|
||||
return
|
||||
}
|
||||
const n = t.getAttribute("data-tab") - 1
|
||||
, r = this.getSettings("keyDirection")[e.key]
|
||||
, o = i[n + r];
|
||||
o ? o.focus() : -1 === n + r ? i.last().trigger("focus") : i.first().trigger("focus")
|
||||
}
|
||||
deactivateActiveTab(e) {
|
||||
const t = this.getSettings()
|
||||
, a = t.classes.active
|
||||
, i = e ? '[data-tab="' + e + '"]' : "." + a
|
||||
, s = this.elements.$tabTitles.filter(i)
|
||||
, n = this.elements.$tabContents.filter(i);
|
||||
s.add(n).removeClass(a + " active"),
|
||||
s.closest(".elementor-accordion-item").removeClass("active"),
|
||||
s.attr({
|
||||
tabindex: "-1",
|
||||
"aria-selected": "false",
|
||||
"aria-expanded": "false"
|
||||
}),
|
||||
n[t.hideTabFn](),
|
||||
n.attr("hidden", "hidden")
|
||||
}
|
||||
activateTab(e) {
|
||||
const t = this.getSettings()
|
||||
, a = t.classes.active
|
||||
, i = this.elements.$tabTitles.filter('[data-tab="' + e + '"]')
|
||||
, s = this.elements.$tabContents.filter('[data-tab="' + e + '"]')
|
||||
, n = "show" === t.showTabFn ? 0 : 400;
|
||||
i.add(s).addClass(a + " active"),
|
||||
i.closest(".elementor-accordion-item").addClass("active"),
|
||||
i.attr({
|
||||
tabindex: "0",
|
||||
"aria-selected": "true",
|
||||
"aria-expanded": "true"
|
||||
}),
|
||||
s[t.showTabFn](n, () => elementorFrontend.elements.$window.trigger("elementor-pro/motion-fx/recalc")),
|
||||
s.removeAttr("hidden")
|
||||
}
|
||||
isActiveTab(e) {
|
||||
return this.elements.$tabTitles.filter('[data-tab="' + e + '"]').hasClass(this.getSettings("classes.active"))
|
||||
}
|
||||
bindEvents() {
|
||||
this.elements.$tabTitles.on({
|
||||
keydown: e => {
|
||||
jQuery(e.target).is("a") && "Enter" === e.key && e.preventDefault(),
|
||||
["End", "Home", "ArrowUp", "ArrowDown"].includes(e.key) && this.handleKeyboardNavigation(e)
|
||||
}
|
||||
,
|
||||
keyup: e => {
|
||||
switch (e.code) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
this.handleKeyboardNavigation(e);
|
||||
break;
|
||||
case "Enter":
|
||||
case "Space":
|
||||
e.preventDefault(),
|
||||
this.changeActiveTab(e.currentTarget.getAttribute("data-tab"))
|
||||
(self.webpackChunkelementorFrontend = self.webpackChunkelementorFrontend || []).push([
|
||||
[131], {
|
||||
5249: (e, t, a) => {
|
||||
Object.defineProperty(t, "__esModule", {
|
||||
value: !0
|
||||
}), t.default = void 0, a(4846), a(7458), a(6211);
|
||||
class baseTabs extends elementorModules.frontend.handlers.Base {
|
||||
getDefaultSettings() {
|
||||
return {
|
||||
selectors: {
|
||||
tablist: '[role="tablist"]',
|
||||
tabTitle: ".elementor-tab-title",
|
||||
tabContent: ".elementor-tab-content"
|
||||
},
|
||||
classes: {
|
||||
active: "elementor-active"
|
||||
},
|
||||
showTabFn: "show",
|
||||
hideTabFn: "hide",
|
||||
toggleSelf: !0,
|
||||
hidePrevious: !0,
|
||||
autoExpand: !0,
|
||||
keyDirection: {
|
||||
ArrowLeft: elementorFrontendConfig.is_rtl ? 1 : -1,
|
||||
ArrowUp: -1,
|
||||
ArrowRight: elementorFrontendConfig.is_rtl ? -1 : 1,
|
||||
ArrowDown: 1
|
||||
}
|
||||
}
|
||||
,
|
||||
click: e => {
|
||||
e.preventDefault(),
|
||||
this.changeActiveTab(e.currentTarget.getAttribute("data-tab"))
|
||||
}
|
||||
getDefaultElements() {
|
||||
const e = this.getSettings("selectors");
|
||||
return {
|
||||
$tabTitles: this.findElement(e.tabTitle),
|
||||
$tabContents: this.findElement(e.tabContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
onInit() {
|
||||
super.onInit(...arguments),
|
||||
this.activateDefaultTab()
|
||||
}
|
||||
onEditSettingsChange(e) {
|
||||
"activeItemIndex" === e && this.activateDefaultTab()
|
||||
}
|
||||
changeActiveTab(e) {
|
||||
const t = this.isActiveTab(e)
|
||||
, a = this.getSettings();
|
||||
if (t) return;
|
||||
!a.toggleSelf && t || !a.hidePrevious || this.deactivateActiveTab(),
|
||||
!a.hidePrevious && t && this.deactivateActiveTab(e),
|
||||
t || this.activateTab(e)
|
||||
}
|
||||
}
|
||||
t.default = baseTabs
|
||||
}
|
||||
,
|
||||
9675: (e, t, a) => {
|
||||
var i = a(6784);
|
||||
Object.defineProperty(t, "__esModule", {
|
||||
value: !0
|
||||
}),
|
||||
t.default = void 0;
|
||||
var s = i(a(5249));
|
||||
class Accordion extends s.default {
|
||||
getDefaultSettings() {
|
||||
return {
|
||||
...super.getDefaultSettings(),
|
||||
showTabFn: "slideDown",
|
||||
hideTabFn: "slideUp"
|
||||
}
|
||||
activateDefaultTab() {
|
||||
const e = this.getSettings();
|
||||
if (!e.autoExpand || "editor" === e.autoExpand && !this.isEdit) return;
|
||||
const t = this.getEditSettings("activeItemIndex") || 1,
|
||||
a = {
|
||||
showTabFn: e.showTabFn,
|
||||
hideTabFn: e.hideTabFn
|
||||
};
|
||||
this.setSettings({
|
||||
showTabFn: "show",
|
||||
hideTabFn: "hide"
|
||||
}), this.changeActiveTab(t), this.setSettings(a)
|
||||
}
|
||||
handleKeyboardNavigation(e) {
|
||||
const t = e.currentTarget,
|
||||
a = jQuery(t.closest(this.getSettings("selectors").tablist)),
|
||||
i = a.find(this.getSettings("selectors").tabTitle),
|
||||
s = "vertical" === a.attr("aria-orientation");
|
||||
switch (e.key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
if (s) return;
|
||||
break;
|
||||
case "ArrowUp":
|
||||
case "ArrowDown":
|
||||
if (!s) return;
|
||||
e.preventDefault();
|
||||
break;
|
||||
case "Home":
|
||||
return e.preventDefault(), void i.first().trigger("focus");
|
||||
case "End":
|
||||
return e.preventDefault(), void i.last().trigger("focus");
|
||||
default:
|
||||
return
|
||||
}
|
||||
const n = t.getAttribute("data-tab") - 1,
|
||||
r = this.getSettings("keyDirection")[e.key],
|
||||
o = i[n + r];
|
||||
o ? o.focus() : -1 === n + r ? i.last().trigger("focus") : i.first().trigger("focus")
|
||||
}
|
||||
deactivateActiveTab(e) {
|
||||
const t = this.getSettings(),
|
||||
a = t.classes.active,
|
||||
i = e ? '[data-tab="' + e + '"]' : "." + a,
|
||||
s = this.elements.$tabTitles.filter(i),
|
||||
n = this.elements.$tabContents.filter(i);
|
||||
s.add(n).removeClass(a), s.attr({
|
||||
tabindex: "-1",
|
||||
"aria-selected": "false",
|
||||
"aria-expanded": "false"
|
||||
}), n[t.hideTabFn](), n.attr("hidden", "hidden")
|
||||
}
|
||||
activateTab(e) {
|
||||
const t = this.getSettings(),
|
||||
a = t.classes.active,
|
||||
i = this.elements.$tabTitles.filter('[data-tab="' + e + '"]'),
|
||||
s = this.elements.$tabContents.filter('[data-tab="' + e + '"]'),
|
||||
n = "show" === t.showTabFn ? 0 : 400;
|
||||
i.add(s).addClass(a), i.attr({
|
||||
tabindex: "0",
|
||||
"aria-selected": "true",
|
||||
"aria-expanded": "true"
|
||||
}), s[t.showTabFn](n, () => elementorFrontend.elements.$window.trigger("elementor-pro/motion-fx/recalc")), s.removeAttr("hidden")
|
||||
}
|
||||
isActiveTab(e) {
|
||||
return this.elements.$tabTitles.filter('[data-tab="' + e + '"]').hasClass(this.getSettings("classes.active"))
|
||||
}
|
||||
bindEvents() {
|
||||
this.elements.$tabTitles.on({
|
||||
keydown: e => {
|
||||
jQuery(e.target).is("a") && "Enter" === e.key && e.preventDefault(), ["End", "Home", "ArrowUp", "ArrowDown"].includes(e.key) && this.handleKeyboardNavigation(e)
|
||||
},
|
||||
keyup: e => {
|
||||
switch (e.code) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
this.handleKeyboardNavigation(e);
|
||||
break;
|
||||
case "Enter":
|
||||
case "Space":
|
||||
e.preventDefault(), this.changeActiveTab(e.currentTarget.getAttribute("data-tab"))
|
||||
}
|
||||
},
|
||||
click: e => {
|
||||
e.preventDefault(), this.changeActiveTab(e.currentTarget.getAttribute("data-tab"))
|
||||
}
|
||||
})
|
||||
}
|
||||
onInit(...e) {
|
||||
super.onInit(...e), this.activateDefaultTab()
|
||||
}
|
||||
onEditSettingsChange(e) {
|
||||
"activeItemIndex" === e && this.activateDefaultTab()
|
||||
}
|
||||
changeActiveTab(e) {
|
||||
const t = this.isActiveTab(e),
|
||||
a = this.getSettings();
|
||||
!a.toggleSelf && t || !a.hidePrevious || this.deactivateActiveTab(), !a.hidePrevious && t && this.deactivateActiveTab(e), t || this.activateTab(e)
|
||||
}
|
||||
}
|
||||
t.default = baseTabs
|
||||
},
|
||||
9675: (e, t, a) => {
|
||||
var i = a(6784);
|
||||
Object.defineProperty(t, "__esModule", {
|
||||
value: !0
|
||||
}), t.default = void 0;
|
||||
var s = i(a(5249));
|
||||
class Accordion extends s.default {
|
||||
getDefaultSettings() {
|
||||
return { ...super.getDefaultSettings(),
|
||||
showTabFn: "slideDown",
|
||||
hideTabFn: "slideUp"
|
||||
}
|
||||
}
|
||||
}
|
||||
t.default = Accordion
|
||||
}
|
||||
t.default = Accordion
|
||||
}
|
||||
}]);
|
||||
]);
|
||||
472
assets/javascript/jquery.cookie.min.js
vendored
472
assets/javascript/jquery.cookie.min.js
vendored
@@ -55,3 +55,475 @@
|
||||
!e.cookie(n))
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Doormile — Premium Interactive Dashboard Logic
|
||||
* Core Interactions: ALT Vision Toggles, Timeline Hover Highlights, Live AI Routing Simulator
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
/* ==========================================================================
|
||||
1. HEADER NAV SCROLL EFFECTS (For smooth page scrolling)
|
||||
========================================================================== */
|
||||
const sections = document.querySelectorAll('section');
|
||||
|
||||
// Smooth navigation anchor links
|
||||
document.querySelectorAll('.scroll-arrow-link').forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
const targetId = this.getAttribute('href');
|
||||
document.querySelector(targetId).scrollIntoView({
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
2. INTERACTIVE STRATEGIC MOAT ACCORDION
|
||||
========================================================================== */
|
||||
const moatCards = document.querySelectorAll('.moat-card');
|
||||
|
||||
moatCards.forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
// Remove active status from all cards
|
||||
moatCards.forEach(c => c.classList.remove('active'));
|
||||
|
||||
// Activate current card
|
||||
card.classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
3. INTERACTIVE ALT VISION TOGGLES (Image 2 exact match)
|
||||
========================================================================== */
|
||||
const altPill1 = document.getElementById('alt-pill-1');
|
||||
const altPill2 = document.getElementById('alt-pill-2');
|
||||
|
||||
const visionTitle = document.querySelector('.vision-main-title');
|
||||
const visionSubtitle = document.querySelector('.vision-main-subtitle');
|
||||
const vision2030CardHeading = document.querySelector('.glowing-vision-card .card-heading');
|
||||
const vision2030CardText = document.querySelector('.glowing-vision-card .card-text');
|
||||
|
||||
// Alt 1 Copy Data
|
||||
const alt1Data = {
|
||||
title: 'The <span class="glowing-rose-text">Intelligence Grid</span> Behind Every Urban Mile',
|
||||
subtitle: 'From Hyderabad EV pilot to nationwide AI logistics intelligence by 2030',
|
||||
cardHeading: 'AI Pulse Layer',
|
||||
cardText: 'Nationwide AI logistics grid reaching 15+ cities, empowering female micro-entrepreneurs.'
|
||||
};
|
||||
|
||||
// Alt 2 Copy Data
|
||||
const alt2Data = {
|
||||
title: 'The <span class="glowing-rose-text">Neural Backbone</span> Behind Every Urban Mile',
|
||||
subtitle: 'From Hyderabad EV pilot to a full algorithmic urban grid routing infrastructure by 2030',
|
||||
cardHeading: 'Neural Backbone',
|
||||
cardText: 'Resilient node logistics framework connecting metropolitan hubs through dynamic autonomous routing.'
|
||||
};
|
||||
|
||||
function applyAltTransition(data, activePill, inactivePill) {
|
||||
activePill.classList.add('active');
|
||||
inactivePill.classList.remove('active');
|
||||
|
||||
// Smooth text transition
|
||||
visionTitle.style.opacity = 0;
|
||||
visionSubtitle.style.opacity = 0;
|
||||
vision2030CardHeading.style.opacity = 0;
|
||||
vision2030CardText.style.opacity = 0;
|
||||
|
||||
setTimeout(() => {
|
||||
visionTitle.innerHTML = data.title;
|
||||
visionSubtitle.textContent = data.subtitle;
|
||||
vision2030CardHeading.textContent = data.cardHeading;
|
||||
vision2030CardText.textContent = data.cardText;
|
||||
|
||||
visionTitle.style.opacity = 1;
|
||||
visionSubtitle.style.opacity = 1;
|
||||
vision2030CardHeading.style.opacity = 1;
|
||||
vision2030CardText.style.opacity = 1;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// Set transition styles for quick animations
|
||||
[visionTitle, visionSubtitle, vision2030CardHeading, vision2030CardText].forEach(el => {
|
||||
el.style.transition = 'opacity 0.25s ease';
|
||||
});
|
||||
|
||||
altPill1.addEventListener('click', () => {
|
||||
if (!altPill1.classList.contains('active')) {
|
||||
applyAltTransition(alt1Data, altPill1, altPill2);
|
||||
addSimLog('Toggled view to ALT 1: AI Pulse Layer model.', true);
|
||||
}
|
||||
});
|
||||
|
||||
altPill2.addEventListener('click', () => {
|
||||
if (!altPill2.classList.contains('active')) {
|
||||
applyAltTransition(alt2Data, altPill2, altPill1);
|
||||
addSimLog('Toggled view to ALT 2: Neural Backbone architecture.', true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
4. TIMELINE HOVER SYNCRONIZATION
|
||||
========================================================================== */
|
||||
const roadmapCardsList = document.querySelectorAll('.roadmap-col-card');
|
||||
const timelineDots = document.querySelectorAll('.node-dot-item');
|
||||
|
||||
roadmapCardsList.forEach((card, idx) => {
|
||||
card.addEventListener('mouseenter', () => {
|
||||
// Highlight the corresponding dot on timeline line
|
||||
timelineDots.forEach(d => d.classList.remove('dot-hover'));
|
||||
if (timelineDots[idx]) {
|
||||
timelineDots[idx].classList.add('dot-hover');
|
||||
// Temporarily expand scale in styling
|
||||
timelineDots[idx].style.transform = 'translate(-50%, -50%) scale(1.5)';
|
||||
timelineDots[idx].style.transition = 'transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1)';
|
||||
}
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
if (timelineDots[idx]) {
|
||||
timelineDots[idx].classList.remove('dot-hover');
|
||||
timelineDots[idx].style.transform = 'translate(-50%, -50%) scale(1)';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
5. HTML5 CANVAS: LIVE AI ROUTING SIMULATOR
|
||||
========================================================================== */
|
||||
const canvas = document.getElementById('simCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Controls
|
||||
const toggleManualBtn = document.getElementById('toggle-manual');
|
||||
const toggleAiBtn = document.getElementById('toggle-ai');
|
||||
const speedBtn = document.getElementById('sim-speed-btn');
|
||||
const refreshBtn = document.getElementById('sim-refresh-btn');
|
||||
const riderCountSpan = document.getElementById('rider-count');
|
||||
const consoleLogs = document.getElementById('sim-console');
|
||||
|
||||
// Metric DOM Handles
|
||||
const simSlaVal = document.getElementById('sim-sla-val');
|
||||
const simSlaDelta = document.getElementById('sim-sla-delta');
|
||||
const simSlaProgress = document.getElementById('sim-sla-progress');
|
||||
|
||||
const simTimeVal = document.getElementById('sim-time-val');
|
||||
const simTimeDelta = document.getElementById('sim-time-delta');
|
||||
const simTimeProgress = document.getElementById('sim-time-progress');
|
||||
|
||||
const simCo2Val = document.getElementById('sim-co2-val');
|
||||
const simCo2Delta = document.getElementById('sim-co2-delta');
|
||||
const simCo2Progress = document.getElementById('sim-co2-progress');
|
||||
|
||||
// Simulation States
|
||||
let isAiMode = false;
|
||||
let animSpeed = 1; // Speed multiplier
|
||||
let networkNodes = [];
|
||||
let riders = [];
|
||||
let lastTime = 0;
|
||||
|
||||
// Grid System Dimensions
|
||||
function resizeCanvas() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = rect.height * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
}
|
||||
resizeCanvas();
|
||||
window.addEventListener('resize', resizeCanvas);
|
||||
|
||||
// Dynamic Log Handler
|
||||
function addSimLog(message, isAI = false) {
|
||||
const timeStr = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||
const logEntry = document.createElement('div');
|
||||
logEntry.className = 'log-entry';
|
||||
logEntry.innerHTML = `<span class="time">[${timeStr}]</span> ${isAI ? '<span class="ai">[MileTruth AI]</span> ' : ''}${message}`;
|
||||
|
||||
consoleLogs.appendChild(logEntry);
|
||||
consoleLogs.scrollTop = consoleLogs.scrollHeight;
|
||||
|
||||
// Keep maximum 8 log entries in terminal
|
||||
if (consoleLogs.childElementCount > 8) {
|
||||
consoleLogs.removeChild(consoleLogs.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize Network Grid Nodes (Hyderabad Mock Grid)
|
||||
function initNetworkGrid() {
|
||||
networkNodes = [];
|
||||
const width = canvas.width / (window.devicePixelRatio || 1);
|
||||
const height = canvas.height / (window.devicePixelRatio || 1);
|
||||
|
||||
// Generate central hubs
|
||||
const centralHub = { x: width * 0.5, y: height * 0.5, type: 'central', label: 'Central Hub EV' };
|
||||
networkNodes.push(centralHub);
|
||||
|
||||
// Generate suburban delivery clusters
|
||||
const clusterCount = 5;
|
||||
const nodesPerCluster = 5;
|
||||
|
||||
for (let i = 0; i < clusterCount; i++) {
|
||||
const angle = (i * Math.PI * 2) / clusterCount;
|
||||
const dist = Math.min(width, height) * 0.3;
|
||||
const cx = centralHub.x + Math.cos(angle) * dist;
|
||||
const cy = centralHub.y + Math.sin(angle) * dist;
|
||||
|
||||
const clusterHub = { x: cx, y: cy, type: 'subhub', label: `Hub Zone ${String.fromCharCode(65 + i)}` };
|
||||
networkNodes.push(clusterHub);
|
||||
|
||||
// Inner delivery terminal nodes
|
||||
for (let j = 0; j < nodesPerCluster; j++) {
|
||||
const subAngle = (j * Math.PI * 2) / nodesPerCluster;
|
||||
const subDist = 35 + Math.random() * 25;
|
||||
networkNodes.push({
|
||||
x: cx + Math.cos(subAngle) * subDist,
|
||||
y: cy + Math.sin(subAngle) * subDist,
|
||||
type: 'delivery',
|
||||
label: `Point ${i}-${j}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn EV Rider particles
|
||||
spawnRiders();
|
||||
}
|
||||
|
||||
function spawnRiders() {
|
||||
riders = [];
|
||||
const hubNodes = networkNodes.filter(n => n.type === 'central' || n.type === 'subhub');
|
||||
const deliveryNodes = networkNodes.filter(n => n.type === 'delivery');
|
||||
|
||||
const riderCount = 10;
|
||||
riderCountSpan.textContent = `${riderCount} EVs`;
|
||||
|
||||
for (let i = 0; i < riderCount; i++) {
|
||||
const startHub = hubNodes[Math.floor(Math.random() * hubNodes.length)];
|
||||
const target = deliveryNodes[Math.floor(Math.random() * deliveryNodes.length)];
|
||||
|
||||
riders.push({
|
||||
x: startHub.x,
|
||||
y: startHub.y,
|
||||
source: startHub,
|
||||
target: target,
|
||||
progress: Math.random(),
|
||||
speed: 0.003 + Math.random() * 0.002,
|
||||
id: `EV-${100 + i}`,
|
||||
color: i % 2 === 0 ? 'rgba(255, 42, 95, 0.95)' : 'rgba(6, 182, 212, 0.95)'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Grid rendering logic
|
||||
function drawSimulationGrid() {
|
||||
const width = canvas.width / (window.devicePixelRatio || 1);
|
||||
const height = canvas.height / (window.devicePixelRatio || 1);
|
||||
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// 1. Draw connecting arterial routes
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.025)';
|
||||
ctx.lineWidth = 1;
|
||||
const hubNodes = networkNodes.filter(n => n.type === 'central' || n.type === 'subhub');
|
||||
|
||||
ctx.beginPath();
|
||||
for (let i = 0; i < hubNodes.length; i++) {
|
||||
for (let j = i + 1; j < hubNodes.length; j++) {
|
||||
ctx.moveTo(hubNodes[i].x, hubNodes[i].y);
|
||||
ctx.lineTo(hubNodes[j].x, hubNodes[j].y);
|
||||
}
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// 2. Draw active delivery paths
|
||||
ctx.beginPath();
|
||||
riders.forEach(rider => {
|
||||
if (isAiMode) {
|
||||
ctx.strokeStyle = 'rgba(255, 42, 95, 0.07)';
|
||||
ctx.lineWidth = 2.5;
|
||||
ctx.moveTo(rider.source.x, rider.source.y);
|
||||
ctx.lineTo(rider.target.x, rider.target.y);
|
||||
} else {
|
||||
ctx.strokeStyle = 'rgba(156, 163, 175, 0.04)';
|
||||
ctx.lineWidth = 1.5;
|
||||
|
||||
const midX = (rider.source.x + rider.target.x) / 2 + 50;
|
||||
const midY = (rider.source.y + rider.target.y) / 2 - 50;
|
||||
|
||||
ctx.moveTo(rider.source.x, rider.source.y);
|
||||
ctx.quadraticCurveTo(midX, midY, rider.target.x, rider.target.y);
|
||||
}
|
||||
});
|
||||
ctx.stroke();
|
||||
|
||||
// 3. Draw grid node points
|
||||
networkNodes.forEach(node => {
|
||||
if (node.type === 'central') {
|
||||
ctx.fillStyle = '#ff2a5f';
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, 8, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = 'rgba(255, 42, 95, 0.2)';
|
||||
ctx.lineWidth = 6;
|
||||
ctx.stroke();
|
||||
} else if (node.type === 'subhub') {
|
||||
ctx.fillStyle = '#06b6d4';
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, 5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.12)';
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, 2.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
});
|
||||
|
||||
// 4. Animate EV Riders (Moving dots)
|
||||
riders.forEach(rider => {
|
||||
let riderX, riderY;
|
||||
|
||||
if (isAiMode) {
|
||||
riderX = rider.source.x + (rider.target.x - rider.source.x) * rider.progress;
|
||||
riderY = rider.source.y + (rider.target.y - rider.source.y) * rider.progress;
|
||||
} else {
|
||||
const t = rider.progress;
|
||||
const midX = (rider.source.x + rider.target.x) / 2 + 50;
|
||||
const midY = (rider.source.y + rider.target.y) / 2 - 50;
|
||||
|
||||
riderX = (1 - t) * (1 - t) * rider.source.x + 2 * (1 - t) * t * midX + t * t * rider.target.x;
|
||||
riderY = (1 - t) * (1 - t) * rider.source.y + 2 * (1 - t) * t * midY + t * t * rider.target.y;
|
||||
}
|
||||
|
||||
ctx.shadowBlur = 8;
|
||||
ctx.shadowColor = rider.color;
|
||||
ctx.fillStyle = rider.color;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(riderX, riderY, 4.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
const speedMultiplier = isAiMode ? 1.6 : 1.0;
|
||||
rider.progress += rider.speed * animSpeed * speedMultiplier;
|
||||
|
||||
if (rider.progress >= 1.0) {
|
||||
const deliveries = networkNodes.filter(n => n.type === 'delivery');
|
||||
|
||||
rider.source = rider.target;
|
||||
rider.target = deliveries[Math.floor(Math.random() * deliveries.length)];
|
||||
rider.progress = 0;
|
||||
rider.speed = 0.003 + Math.random() * 0.002;
|
||||
|
||||
if (Math.random() > 0.6) {
|
||||
if (isAiMode) {
|
||||
addSimLog(`${rider.id} completed optimized route. SLA Locked.`, true);
|
||||
} else {
|
||||
addSimLog(`${rider.id} delivered package. Latency buffer +3.2s.`, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Telemetry dashboard dynamic transitions
|
||||
function updateMetricsDashboard() {
|
||||
if (isAiMode) {
|
||||
simSlaVal.textContent = '98.4%';
|
||||
simSlaVal.style.color = 'var(--primary-glow)';
|
||||
simSlaDelta.textContent = '+30.2% vs Manual';
|
||||
simSlaDelta.className = 'delta';
|
||||
simSlaProgress.style.width = '98.4%';
|
||||
simSlaProgress.style.background = 'var(--primary)';
|
||||
|
||||
simTimeVal.textContent = '18.2 min';
|
||||
simTimeVal.style.color = '#fff';
|
||||
simTimeDelta.textContent = '-24.3 min threshold';
|
||||
simTimeDelta.className = 'delta';
|
||||
simTimeProgress.style.width = '24%';
|
||||
simTimeProgress.style.background = 'var(--primary)';
|
||||
|
||||
simCo2Val.textContent = '340 kg';
|
||||
simCo2Val.style.color = 'var(--accent-glow)';
|
||||
simCo2Delta.textContent = '+183% baseline';
|
||||
simCo2Progress.style.width = '92%';
|
||||
} else {
|
||||
simSlaVal.textContent = '68.2%';
|
||||
simSlaVal.style.color = 'var(--text-secondary)';
|
||||
simSlaDelta.textContent = '-30.2% vs AI';
|
||||
simSlaDelta.className = 'delta negative';
|
||||
simSlaProgress.style.width = '68.2%';
|
||||
simSlaProgress.style.background = 'var(--text-muted)';
|
||||
|
||||
simTimeVal.textContent = '42.5 min';
|
||||
simTimeVal.style.color = 'var(--text-secondary)';
|
||||
simTimeDelta.textContent = '+24.3 min delayed';
|
||||
simTimeDelta.className = 'delta negative';
|
||||
simTimeProgress.style.width = '82%';
|
||||
simTimeProgress.style.background = 'var(--text-muted)';
|
||||
|
||||
simCo2Val.textContent = '120 kg';
|
||||
simCo2Val.style.color = 'var(--text-secondary)';
|
||||
simCo2Delta.textContent = '+8% baseline';
|
||||
simCo2Progress.style.width = '35%';
|
||||
}
|
||||
}
|
||||
|
||||
// Loop
|
||||
function loop(timestamp) {
|
||||
drawSimulationGrid();
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
// Click triggers
|
||||
toggleManualBtn.addEventListener('click', () => {
|
||||
if (isAiMode) {
|
||||
isAiMode = false;
|
||||
toggleAiBtn.classList.remove('active');
|
||||
toggleManualBtn.classList.add('active');
|
||||
addSimLog('Switched to unoptimized Manual Dispatch mode.');
|
||||
updateMetricsDashboard();
|
||||
}
|
||||
});
|
||||
|
||||
toggleAiBtn.addEventListener('click', () => {
|
||||
if (!isAiMode) {
|
||||
isAiMode = true;
|
||||
toggleManualBtn.classList.remove('active');
|
||||
toggleAiBtn.classList.add('active');
|
||||
addSimLog('MileTruth AI optimization activated. Resolving city bottlenecks...', true);
|
||||
updateMetricsDashboard();
|
||||
}
|
||||
});
|
||||
|
||||
speedBtn.addEventListener('click', () => {
|
||||
if (animSpeed === 1) {
|
||||
animSpeed = 2.5;
|
||||
speedBtn.style.color = 'var(--primary-glow)';
|
||||
addSimLog('Boost mode active. Dispatch rate x2.5.');
|
||||
} else {
|
||||
animSpeed = 1;
|
||||
speedBtn.style.color = 'var(--text-primary)';
|
||||
addSimLog('Grid speed returned to normal.');
|
||||
}
|
||||
});
|
||||
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
initNetworkGrid();
|
||||
addSimLog('Urban network grid re-routed and refreshed.');
|
||||
});
|
||||
|
||||
// Start
|
||||
initNetworkGrid();
|
||||
requestAnimationFrame(loop);
|
||||
|
||||
});
|
||||
|
||||
44
assets/javascript/scroll-to-top.js
Normal file
44
assets/javascript/scroll-to-top.js
Normal file
@@ -0,0 +1,44 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var scrollTopButtons = document.querySelectorAll(".footer-scroll-top");
|
||||
|
||||
if (!scrollTopButtons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
function goToPageTop() {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
|
||||
function updateButtonVisibility() {
|
||||
var shouldShowButton = window.scrollY > 300;
|
||||
|
||||
scrollTopButtons.forEach(function (button) {
|
||||
button.classList.toggle("active", shouldShowButton);
|
||||
});
|
||||
}
|
||||
|
||||
scrollTopButtons.forEach(function (button) {
|
||||
button.setAttribute("role", "button");
|
||||
button.setAttribute("tabindex", "0");
|
||||
button.setAttribute("aria-label", "Scroll to top");
|
||||
|
||||
button.addEventListener("click", goToPageTop);
|
||||
button.addEventListener("keydown", function (event) {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
goToPageTop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", updateButtonVisibility, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
updateButtonVisibility();
|
||||
})();
|
||||
@@ -343,14 +343,6 @@ function background_image_parallax(object, multiplier) {
|
||||
});
|
||||
}
|
||||
}
|
||||
function scroll_to_top_init() {
|
||||
let scrollTop = jQuery(window).scrollTop();
|
||||
if (scrollTop > 100) {
|
||||
jQuery(".footer-scroll-top").addClass("active");
|
||||
} else {
|
||||
jQuery(".footer-scroll-top").removeClass("active");
|
||||
}
|
||||
}
|
||||
function check_custom_field(element) {
|
||||
if (element.val() || jQuery("option.placeholder", element).is(":selected")) {
|
||||
element
|
||||
@@ -596,19 +588,6 @@ function genre_get_posts(
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
setTimeout(sticky_menu_active, 300);
|
||||
jQuery(".footer-scroll-top").on("pointerdown", function (e) {
|
||||
jQuery(this).data("fastScrolled", true);
|
||||
window.scrollTo(0, 0);
|
||||
setTimeout(() => jQuery(this).data("fastScrolled", false), 500);
|
||||
});
|
||||
jQuery(".footer-scroll-top").on("click", function (e) {
|
||||
if (jQuery(this).data("fastScrolled")) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
jQuery("html, body").stop(true, true).animate({ scrollTop: 0 }, 150);
|
||||
});
|
||||
side_panel_open();
|
||||
search_panel_open();
|
||||
overlay_close_all();
|
||||
@@ -636,7 +615,6 @@ jQuery(document).ready(function () {
|
||||
logico_custom_cursor();
|
||||
logico_ticker();
|
||||
scroll_to_anchor();
|
||||
scroll_to_top_init();
|
||||
jQuery(".elementor-widget").on(
|
||||
"click",
|
||||
".content-pagination a",
|
||||
@@ -704,9 +682,6 @@ jQuery(window).on("resize", function () {
|
||||
logico_custom_cursor();
|
||||
logico_ticker();
|
||||
});
|
||||
jQuery(window).on("scroll", function () {
|
||||
scroll_to_top_init();
|
||||
});
|
||||
jQuery(document).on(
|
||||
"elementor/popup/show",
|
||||
function (event, popupId, popupDocument) {
|
||||
|
||||
Reference in New Issue
Block a user