Complete rebuild of the single page from the design-taste-frontend skill (direction: sentence-as-layout, seed 1084447843). CSS and JS moved out of the markup into css/site.css and js/site.js. - navy field #1a2039 with pink ink #f9a3c0: no Odoo plum, no teal, no violet - hero headline carries a 1689 world map inside the sentence - claims as full-bleed switchable bands, tips table, module picker - four archival Wikimedia pictures, credited in the footer - inspector: 0 FAIL at 1440 / 768 / 390 (lint clean)
149 lines
5.4 KiB
JavaScript
149 lines
5.4 KiB
JavaScript
/* The OC Guy — page behaviour.
|
|
Everything here degrades: without JS the page still reads, the bands still
|
|
show their titles, the tips still list, the form still shows. */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var doc = document.documentElement;
|
|
doc.classList.add('js');
|
|
|
|
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
/* ---------- signature move: the shutter on the hero photograph ---------- */
|
|
var hero = document.querySelector('.hero-line');
|
|
if (hero) {
|
|
if (reduce) {
|
|
hero.classList.add('shutter');
|
|
} else {
|
|
requestAnimationFrame(function () {
|
|
setTimeout(function () { hero.classList.add('shutter'); }, 120);
|
|
});
|
|
}
|
|
}
|
|
|
|
/* ---------- entrance for the parts of every section ---------- */
|
|
var targets = [];
|
|
document.querySelectorAll('.intro .statement, .open h2, .open p, .map-figure figcaption, .tips h2, .tips-note, .picker, .tips-list li, .about h2, .bio, .about-facts, .ask-line, .ask-note, .band-text, .foot-invite, .foot-links').forEach(function (el) {
|
|
targets.push(el);
|
|
});
|
|
|
|
if (!reduce && 'IntersectionObserver' in window) {
|
|
var io = new IntersectionObserver(function (entries) {
|
|
entries.forEach(function (entry) {
|
|
if (!entry.isIntersecting) return;
|
|
entry.target.classList.add('rv-in');
|
|
io.unobserve(entry.target);
|
|
});
|
|
}, { rootMargin: '0px 0px -12% 0px', threshold: 0.12 });
|
|
|
|
targets.forEach(function (el, i) {
|
|
el.setAttribute('data-rv', String((i % 4) + 1));
|
|
io.observe(el);
|
|
});
|
|
} else {
|
|
targets.forEach(function (el) { el.classList.add('rv-in'); });
|
|
}
|
|
|
|
/* ---------- switches: five claims ---------- */
|
|
document.querySelectorAll('.band-switch').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var band = btn.closest('.band');
|
|
var on = btn.getAttribute('aria-checked') === 'true';
|
|
btn.setAttribute('aria-checked', String(!on));
|
|
band.setAttribute('data-on', String(!on));
|
|
});
|
|
});
|
|
|
|
/* ---------- the live thing: pick what the company does ---------- */
|
|
var chips = Array.prototype.slice.call(document.querySelectorAll('.chip'));
|
|
var out = document.querySelector('.picker-out');
|
|
var installOrder = ['Sales', 'CRM', 'Purchase', 'Manufacturing', 'Inventory', 'Project', 'Timesheets', 'Expenses', 'Invoicing', 'Accounting'];
|
|
|
|
function render(mods) {
|
|
if (!mods.length) {
|
|
out.textContent = 'Choose one, or several. The modules stack in the order you would install them.';
|
|
return;
|
|
}
|
|
out.innerHTML = '';
|
|
mods.forEach(function (name, i) {
|
|
var span = document.createElement('span');
|
|
span.className = 'mod';
|
|
span.textContent = name;
|
|
out.appendChild(span);
|
|
if (i < mods.length - 1) {
|
|
var arrow = document.createElement('span');
|
|
arrow.className = 'arrow';
|
|
arrow.textContent = ' → ';
|
|
out.appendChild(arrow);
|
|
}
|
|
});
|
|
var note = document.createElement('span');
|
|
note.className = 'arrow';
|
|
note.textContent = ' — that order, top to bottom.';
|
|
out.appendChild(note);
|
|
}
|
|
|
|
chips.forEach(function (chip) {
|
|
chip.setAttribute('aria-pressed', 'false');
|
|
chip.addEventListener('click', function () {
|
|
var pressed = chip.getAttribute('aria-pressed') === 'true';
|
|
chip.setAttribute('aria-pressed', String(!pressed));
|
|
|
|
var picked = chips.filter(function (c) { return c.getAttribute('aria-pressed') === 'true'; })
|
|
.reduce(function (all, c) {
|
|
c.dataset.modules.split(',').forEach(function (m) {
|
|
var name = m.trim();
|
|
if (all.indexOf(name) === -1) all.push(name);
|
|
});
|
|
return all;
|
|
}, []);
|
|
|
|
picked.sort(function (a, b) { return installOrder.indexOf(a) - installOrder.indexOf(b); });
|
|
render(picked);
|
|
});
|
|
});
|
|
|
|
/* ---------- ask: one question, by mail ---------- */
|
|
var send = document.querySelector('.ask-send');
|
|
var mail = document.getElementById('ask-mail');
|
|
if (send && mail) {
|
|
send.addEventListener('click', function () {
|
|
var addr = (mail.value || '').trim();
|
|
var body = 'My question about Odoo Community:\n\n\n';
|
|
if (addr) body += '\n— reply to: ' + addr + '\n';
|
|
var href = 'mailto:o.arnold@projektbox.de'
|
|
+ '?subject=' + encodeURIComponent('Odoo Community question')
|
|
+ '&body=' + encodeURIComponent(body);
|
|
if (addr && addr.indexOf('@') === -1) {
|
|
mail.focus();
|
|
mail.setAttribute('aria-invalid', 'true');
|
|
return;
|
|
}
|
|
mail.removeAttribute('aria-invalid');
|
|
window.location.href = href;
|
|
});
|
|
mail.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Enter') send.click();
|
|
});
|
|
}
|
|
|
|
/* ---------- one moving picture: the world map drifts against the scroll ---------- */
|
|
var map = document.getElementById('worldmap');
|
|
if (map && !reduce) {
|
|
var ticking = false;
|
|
var update = function () {
|
|
var rect = map.getBoundingClientRect();
|
|
var vh = window.innerHeight || 800;
|
|
if (rect.bottom > -200 && rect.top < vh + 200) {
|
|
var progress = (rect.top + rect.height / 2 - vh / 2) / vh;
|
|
map.style.transform = 'translate3d(0,' + (progress * -26).toFixed(2) + 'px,0) scale(1.06)';
|
|
}
|
|
ticking = false;
|
|
};
|
|
window.addEventListener('scroll', function () {
|
|
if (!ticking) { ticking = true; requestAnimationFrame(update); }
|
|
}, { passive: true });
|
|
update();
|
|
}
|
|
})();
|