From d8622b7f9eef4d70cc247715b0e0635a46753dec Mon Sep 17 00:00:00 2001 From: root Date: Mon, 12 Jan 2026 20:33:56 +0000 Subject: [PATCH] Remove ISO 27001 certification claims Co-Authored-By: Claude Opus 4.5 --- about.php | 4 - assets/js/main.js | 2 - assets/js/main.min.js | 991 +++++++++++++++++- .../healthcare-research-data-collection.php | 1 - faq-enhanced.php | 4 +- includes/schema/faq-schema.php | 2 +- index.php | 5 - 7 files changed, 993 insertions(+), 16 deletions(-) diff --git a/about.php b/about.php index bec084e..7d650d2 100644 --- a/about.php +++ b/about.php @@ -74,10 +74,6 @@ $breadcrumbs = [ "Data Processing" ], "hasCredential": [ - { - "@type": "EducationalOccupationalCredential", - "name": "ISO 27001 Data Security" - }, { "@type": "EducationalOccupationalCredential", "name": "GDPR Compliance Certification" diff --git a/assets/js/main.js b/assets/js/main.js index 71556ce..bf6f431 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -527,8 +527,6 @@ document.addEventListener('DOMContentLoaded', function() { return; // Keep as is, don't animate currency } else if (originalText.includes('99.8%')) { animateNumber(stat, 0, 99.8, '%'); - } else if (originalText.includes('ISO 27001')) { - return; // Keep as is, don't animate certification } }); } diff --git a/assets/js/main.min.js b/assets/js/main.min.js index 0c329da..bf6f431 100644 --- a/assets/js/main.min.js +++ b/assets/js/main.min.js @@ -1 +1,990 @@ -document.addEventListener("DOMContentLoaded",(function(){const e=document.getElementById("rotating-text"),t=document.getElementById("hero-subtitle");if(e&&t){const C=[{title:"Voted UK's No.1 Web Scraping Service",subtitle:"We are experts in web scraping, data analysis and competitor price monitoring."},{title:"UK-based Team",subtitle:"Our team is based in the UK for a clearer, faster response."},{title:"Professional Price Monitoring",subtitle:"Let us monitor your competitor's pricing and product ranges."},{title:"Bespoke Software Solutions",subtitle:"Let our experts build your ideal scraping solution."}];let T=0;function n(){e.style.opacity="0",t.style.opacity="0",setTimeout((()=>{e.textContent=C[T].title,t.textContent=C[T].subtitle,e.style.opacity="1",t.style.opacity="1",T=(T+1)%C.length}),500)}e.style.transition="opacity 0.5s ease-in-out",t.style.transition="opacity 0.5s ease-in-out",setTimeout((()=>{n(),setInterval(n,4e3)}),2e3),console.log("Hero text rotation initialized")}else console.log("Rotating text elements not found");const o=document.getElementById("nav-toggle"),a=document.getElementById("nav-menu");if(o&&a){const P=()=>a.querySelectorAll("a[href], button:not([disabled])"),B=e=>{if(!a.classList.contains("active"))return;const t=P(),n=t[0],o=t[t.length-1];"Tab"===e.key&&(e.shiftKey&&document.activeElement===n?(e.preventDefault(),o.focus()):e.shiftKey||document.activeElement!==o||(e.preventDefault(),n.focus())),"Escape"===e.key&&D()},M=()=>{a.classList.add("active"),o.classList.add("active"),o.setAttribute("aria-expanded","true"),document.addEventListener("keydown",B);const e=P()[0];e&&setTimeout((()=>e.focus()),100)},D=()=>{a.classList.remove("active"),o.classList.remove("active"),o.setAttribute("aria-expanded","false"),document.removeEventListener("keydown",B),o.focus()};o.addEventListener("click",(function(){"true"===o.getAttribute("aria-expanded")?D():M()}));document.querySelectorAll(".nav-link").forEach((e=>{e.addEventListener("click",(()=>{D()}))})),document.addEventListener("click",(e=>{!a.classList.contains("active")||a.contains(e.target)||o.contains(e.target)||D()}))}const s=document.getElementById("navbar");function i(){window.scrollY>50?s.classList.add("scrolled"):s.classList.remove("scrolled")}window.addEventListener("scroll",i);document.querySelectorAll('a[href^="#"]').forEach((e=>{e.addEventListener("click",(function(e){e.preventDefault();const t=this.getAttribute("href"),n=document.querySelector(t);if(n){const e=80,t=n.getBoundingClientRect().top+window.pageYOffset-e;window.scrollTo({top:t,behavior:"smooth"})}}))}));const r=document.querySelectorAll(".animate-on-scroll, .service-card, .feature, .step"),c=new IntersectionObserver((function(e){e.forEach((e=>{e.isIntersecting&&(e.target.classList.add("animated"),e.target.style.opacity="1",e.target.style.transform="translateY(0)",c.unobserve(e.target))}))}),{threshold:.1,rootMargin:"0px 0px -50px 0px"});r.forEach(((e,t)=>{e.style.opacity="0",e.style.transform="translateY(30px)",e.style.transition=`opacity 0.8s ease-out ${.1*t}s, transform 0.8s ease-out ${.1*t}s`,c.observe(e)}));document.querySelectorAll(".service-card").forEach((e=>{e.addEventListener("mouseenter",(function(){this.style.transform="translateY(-10px) scale(1.02)",this.style.boxShadow="0 20px 40px rgba(0, 0, 0, 0.15)"})),e.addEventListener("mouseleave",(function(){this.style.transform="translateY(0) scale(1)",this.style.boxShadow="0 4px 20px rgba(0, 0, 0, 0.08)"}))}));document.querySelectorAll(".btn-primary").forEach((e=>{e.addEventListener("mouseenter",(function(){this.style.animation="pulse 0.5s ease-in-out"})),e.addEventListener("mouseleave",(function(){this.style.animation="none"}))})),console.log("Enhanced animations initialized");let l=0,d=Date.now();document.addEventListener("mousemove",(()=>l+=1)),document.addEventListener("keydown",(()=>l+=2)),document.addEventListener("click",(()=>l+=3));const u=document.getElementById("form_timestamp");u&&(u.value=d);const m=document.querySelector(".contact-form form");function p(e,t){const n=document.getElementById(e),o=document.getElementById(e+"-error");n&&o&&(n.setAttribute("aria-invalid","true"),o.textContent=t)}function y(e){const t=document.getElementById(e),n=document.getElementById(e+"-error");t&&n&&(t.setAttribute("aria-invalid","false"),n.textContent="")}if(m){function g(e){const t=e.value.trim(),n=e.id;return"name"===n&&t.length<2?(p(n,"Please enter a valid name (at least 2 characters)"),!1):"email"!==n||f(t)?"company"===n&&t.length<2?(p(n,"Please enter your organisation name"),!1):"message"===n&&t.length<10?(p(n,"Please provide more details (at least 10 characters)"),!1):(y(n),!0):(p(n,"Please enter a valid email address"),!1)}m.querySelectorAll("[required]").forEach((e=>{e.addEventListener("blur",(function(){g(this)})),e.addEventListener("input",(function(){"true"===this.getAttribute("aria-invalid")&&g(this)}))})),m.addEventListener("submit",(function(e){e.preventDefault(),["name","email","company","message"].forEach(y);const t=new FormData(this),n=t.get("name"),o=t.get("email"),a=t.get("company"),s=t.get("message");let i=!0,r=null;if((!n||n.trim().length<2)&&(p("name","Please enter a valid name (at least 2 characters)"),i=!1,r||(r=document.getElementById("name"))),o&&f(o)||(p("email","Please enter a valid email address"),i=!1,r||(r=document.getElementById("email"))),(!a||a.trim().length<2)&&(p("company","Please enter your organisation name"),i=!1,r||(r=document.getElementById("company"))),(!s||s.trim().length<10)&&(p("message","Please provide more details (at least 10 characters)"),i=!1,r||(r=document.getElementById("message"))),i||!r)if(i){const e=this.querySelector('button[type="submit"]'),t=e.textContent;e.textContent="Sending...",e.disabled=!0;const n=document.getElementById("form_timestamp");n&&(n.value=Date.now());const o=new FormData(this);"undefined"!=typeof grecaptcha?grecaptcha.ready((()=>{grecaptcha.execute(window.recaptchaSiteKey,{action:"contact_form"}).then((n=>{o.set("recaptcha_response",n),o.set("interaction_token",btoa(JSON.stringify({score:Math.min(l,100),time:Date.now()-d}))),fetch("contact-handler.php",{method:"POST",headers:{"X-Requested-With":"XMLHttpRequest"},body:o}).then((e=>e.json())).then((e=>{e.success?(h("Message sent successfully! We'll get back to you soon.","success"),this.reset()):h(e.message||"There was an error sending your message. Please try again.","error")})).catch((e=>{console.error("Error:",e),h("There was an error sending your message. Please try again.","error")})).finally((()=>{e.textContent=t,e.disabled=!1}))}))})):(h("Security verification not available. Please refresh the page.","error"),e.textContent=t,e.disabled=!1)}else h(errors.join("
"),"error");else r.focus()}))}function f(e){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)}function h(e,t="info"){const n=document.querySelector(".notification");n&&n.remove();const o=document.createElement("div");o.className=`notification notification-${t}`,o.innerHTML=`\n
\n ${e}\n \n
\n `,o.style.cssText=`\n position: fixed;\n top: 20px;\n right: 20px;\n z-index: 10000;\n background: ${"success"===t?"#10b981":"error"===t?"#ef4444":"#3b82f6"};\n color: white;\n padding: 16px 20px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n max-width: 400px;\n font-family: 'Inter', sans-serif;\n font-size: 14px;\n opacity: 0;\n transform: translateX(100%);\n transition: all 0.3s ease;\n `,o.querySelector(".notification-content").style.cssText="\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n ",o.querySelector(".notification-close").style.cssText="\n background: none;\n border: none;\n color: white;\n font-size: 18px;\n cursor: pointer;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n ",document.body.appendChild(o),setTimeout((()=>{o.style.opacity="1",o.style.transform="translateX(0)"}),100),o.querySelector(".notification-close").addEventListener("click",(()=>{v(o)})),setTimeout((()=>{document.body.contains(o)&&v(o)}),5e3)}function v(e){e.style.opacity="0",e.style.transform="translateX(100%)",setTimeout((()=>{document.body.contains(e)&&e.remove()}),300)}const b=document.querySelectorAll(".stat-number");function w(){b.forEach((e=>{const t=e.textContent.trim();if(console.log("Animating stat:",t),!t.includes("£2.5M+"))if(t.includes("99.8%"))!function(e,t,n,o=""){let a=t;const s=(n-t)/60,i=setInterval((()=>{a+=s,a>=n&&(a=n,clearInterval(i)),e.textContent=a.toFixed(1)+o}),50)}(e,0,99.8,"%");else if(t.includes("ISO 27001"))return}))}const x=document.querySelector(".hero-stats");if(x){const z=new IntersectionObserver((function(e){e.forEach((e=>{e.isIntersecting&&(console.log("Stats section is visible, starting animation"),setTimeout((()=>{w()}),500),z.unobserve(e.target))}))}),{threshold:.3});z.observe(x)}else console.log("Stats section not found");const E=document.querySelectorAll('img[loading="lazy"]');function L(){const e=document.createElement("canvas");return e.width=1,e.height=1,-1!==e.toDataURL("image/webp").indexOf("webp")}if("IntersectionObserver"in window){const O=new IntersectionObserver((function(e){e.forEach((e=>{if(e.isIntersecting){const t=e.target;t.dataset.src&&(t.src=t.dataset.src),t.dataset.webp&&L()&&(t.src=t.dataset.webp),t.classList.add("loaded"),t.style.opacity="1",O.unobserve(t)}}))}),{rootMargin:"50px 0px",threshold:.1});E.forEach((e=>{"lazy"===e.loading&&(e.style.opacity="0",e.style.transition="opacity 0.3s ease"),O.observe(e)}))}const S=document.createElement("button");function A(){window.scrollY>500?(S.style.opacity="1",S.style.visibility="visible"):(S.style.opacity="0",S.style.visibility="hidden")}let I;S.innerHTML="↑",S.className="scroll-top-btn",S.style.cssText="\n position: fixed;\n bottom: 30px;\n right: 30px;\n width: 50px;\n height: 50px;\n border: none;\n border-radius: 50%;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n font-size: 20px;\n cursor: pointer;\n opacity: 0;\n visibility: hidden;\n transition: all 0.3s ease;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n ",document.body.appendChild(S),S.addEventListener("click",(()=>{window.scrollTo({top:0,behavior:"smooth"})})),window.addEventListener("scroll",A);const k=[i,A];function q(e,t="image",n=null){const o=document.createElement("link");o.rel="preload",o.href=e,o.as=t,n&&(o.type=n),document.head.appendChild(o)}window.removeEventListener("scroll",i),window.removeEventListener("scroll",A),window.addEventListener("scroll",(function(){I||(I=setTimeout((()=>{k.forEach((e=>e())),I=null}),16))})),["assets/images/ukds-main-logo.png","assets/images/hero-data-analytics.svg"].forEach((e=>{L()?q(e.replace(/\.(jpg|jpeg|png)$/i,".webp"),"image","image/webp"):q(e,"image")}));document.querySelectorAll("[data-tooltip]").forEach((e=>{e.addEventListener("mouseenter",(function(){const e=this.getAttribute("data-tooltip"),t=document.createElement("div");t.className="tooltip",t.textContent=e,t.style.cssText="\n position: absolute;\n background: #1a1a1a;\n color: white;\n padding: 8px 12px;\n border-radius: 6px;\n font-size: 14px;\n white-space: nowrap;\n z-index: 10000;\n opacity: 0;\n transition: opacity 0.3s ease;\n pointer-events: none;\n ",document.body.appendChild(t);const n=this.getBoundingClientRect();t.style.left=n.left+n.width/2-t.offsetWidth/2+"px",t.style.top=n.top-t.offsetHeight-10+"px",setTimeout((()=>{t.style.opacity="1"}),100),this.addEventListener("mouseleave",(function(){t.style.opacity="0",setTimeout((()=>{document.body.contains(t)&&t.remove()}),300)}),{once:!0})}))})),"serviceWorker"in navigator&&window.addEventListener("load",(()=>{navigator.serviceWorker.register("/sw.js").then((e=>{console.log("SW registered: ",e)})).catch((e=>{console.log("SW registration failed: ",e)}))})),"performance"in window&&window.addEventListener("load",(function(){setTimeout((()=>{const e=performance.getEntriesByType("navigation")[0];e&&console.log("Page Load Performance:",{"DNS Lookup":Math.round(e.domainLookupEnd-e.domainLookupStart),"TCP Connection":Math.round(e.connectEnd-e.connectStart),"Request/Response":Math.round(e.responseEnd-e.requestStart),"DOM Processing":Math.round(e.domComplete-e.domLoading),"Total Load Time":Math.round(e.loadEventEnd-e.navigationStart)})}),0)})),console.log("UK Data Services website initialized successfully"),console.log("Performance optimizations: Lazy loading, WebP support, and preloading enabled"),function(){const e=document.querySelector(".blog-pagination"),t=document.querySelector(".articles-grid");if(!e||!t)return;const n=e.querySelector("button:first-child"),o=e.querySelector("button:last-child"),a=e.querySelector(".pagination-info");if(!n||!o||!a)return;const s=new URLSearchParams(window.location.search);let i=parseInt(s.get("page"))||1;const r=Array.from(t.querySelectorAll(".article-card")),c=Math.ceil(r.length/6);if(c<=1)return void(e.style.display="none");function l(e,s=!1){r.forEach((e=>{e.style.display="none"}));const i=6*(e-1),l=i+6;for(let e=i;e=c;const d=new URL(window.location);e>1?d.searchParams.set("page",e):d.searchParams.delete("page"),window.history.replaceState({},"",d),s&&t.scrollIntoView({behavior:"smooth",block:"start"})}n.addEventListener("click",(function(e){e.preventDefault(),i>1&&(i--,l(i,!0))})),o.addEventListener("click",(function(e){e.preventDefault(),i{e.forEach((e=>{if(e.isIntersecting){const n=e.target;n.dataset.src&&(n.src=n.dataset.src,n.removeAttribute("data-src")),n.dataset.webp&&new Promise((e=>{const t=new Image;t.onload=t.onerror=()=>e(2===t.height),t.src="data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA"}))&&(n.src=n.dataset.webp),t.unobserve(n)}}))}),{rootMargin:"50px 0px",threshold:.01});document.querySelectorAll("img[data-src]").forEach((t=>{e.observe(t)}))}(),"ontouchstart"in window&&(document.querySelectorAll('button, .btn, a[role="button"]').forEach((e=>{const t=e.getBoundingClientRect();(t.width<44||t.height<44)&&(e.style.minWidth="44px",e.style.minHeight="44px",e.style.display="inline-flex",e.style.alignItems="center",e.style.justifyContent="center")})),document.querySelectorAll(".nav-menu a, .social-links a").forEach((e=>{e.style.padding="12px 16px",e.style.margin="4px"})))})); \ No newline at end of file +// Enhanced JavaScript for UK Data Services Website +document.addEventListener('DOMContentLoaded', function() { + + // Rotating Hero Text Effect (like original UK Data Services site) + const rotatingText = document.getElementById('rotating-text'); + const heroSubtitle = document.getElementById('hero-subtitle'); + + if (rotatingText && heroSubtitle) { + const slides = [ + { + title: "Voted UK's No.1 Web Scraping Service", + subtitle: "We are experts in web scraping, data analysis and competitor price monitoring." + }, + { + title: "UK-based Team", + subtitle: "Our team is based in the UK for a clearer, faster response." + }, + { + title: "Professional Price Monitoring", + subtitle: "Let us monitor your competitor's pricing and product ranges." + }, + { + title: "Bespoke Software Solutions", + subtitle: "Let our experts build your ideal scraping solution." + } + ]; + + let currentSlide = 0; + + function rotateSlide() { + // Fade out + rotatingText.style.opacity = '0'; + heroSubtitle.style.opacity = '0'; + + setTimeout(() => { + // Change text + rotatingText.textContent = slides[currentSlide].title; + heroSubtitle.textContent = slides[currentSlide].subtitle; + + // Fade in + rotatingText.style.opacity = '1'; + heroSubtitle.style.opacity = '1'; + + currentSlide = (currentSlide + 1) % slides.length; + }, 500); + } + + // Add transition styles immediately + rotatingText.style.transition = 'opacity 0.5s ease-in-out'; + heroSubtitle.style.transition = 'opacity 0.5s ease-in-out'; + + // Start rotation after a short delay + setTimeout(() => { + rotateSlide(); + setInterval(rotateSlide, 4000); // Change every 4 seconds + }, 2000); // Start after 2 seconds + + console.log('Hero text rotation initialized'); + } else { + console.log('Rotating text elements not found'); + } + + // Mobile Navigation Toggle with ARIA and Focus Trap + const navToggle = document.getElementById('nav-toggle'); + const navMenu = document.getElementById('nav-menu'); + + if (navToggle && navMenu) { + // Get focusable elements in the menu + const getFocusableElements = () => { + return navMenu.querySelectorAll('a[href], button:not([disabled])'); + }; + + // Focus trap handler + const handleFocusTrap = (e) => { + if (!navMenu.classList.contains('active')) return; + + const focusableElements = getFocusableElements(); + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + + if (e.key === 'Tab') { + if (e.shiftKey && document.activeElement === firstElement) { + e.preventDefault(); + lastElement.focus(); + } else if (!e.shiftKey && document.activeElement === lastElement) { + e.preventDefault(); + firstElement.focus(); + } + } + + // Close menu on Escape key + if (e.key === 'Escape') { + closeMenu(); + } + }; + + const openMenu = () => { + navMenu.classList.add('active'); + navToggle.classList.add('active'); + navToggle.setAttribute('aria-expanded', 'true'); + document.addEventListener('keydown', handleFocusTrap); + // Focus first menu item + const firstFocusable = getFocusableElements()[0]; + if (firstFocusable) { + setTimeout(() => firstFocusable.focus(), 100); + } + }; + + const closeMenu = () => { + navMenu.classList.remove('active'); + navToggle.classList.remove('active'); + navToggle.setAttribute('aria-expanded', 'false'); + document.removeEventListener('keydown', handleFocusTrap); + navToggle.focus(); + }; + + navToggle.addEventListener('click', function() { + const isExpanded = navToggle.getAttribute('aria-expanded') === 'true'; + if (isExpanded) { + closeMenu(); + } else { + openMenu(); + } + }); + + // Close mobile menu when clicking on a link + const navLinks = document.querySelectorAll('.nav-link'); + navLinks.forEach(link => { + link.addEventListener('click', () => { + closeMenu(); + }); + }); + + // Close menu when clicking outside + document.addEventListener('click', (e) => { + if (navMenu.classList.contains('active') && + !navMenu.contains(e.target) && + !navToggle.contains(e.target)) { + closeMenu(); + } + }); + } + + // Navbar Scroll Effect + const navbar = document.getElementById('navbar'); + + function handleNavbarScroll() { + if (window.scrollY > 50) { + navbar.classList.add('scrolled'); + } else { + navbar.classList.remove('scrolled'); + } + } + + window.addEventListener('scroll', handleNavbarScroll); + + // Smooth Scrolling for Navigation Links + const smoothScrollLinks = document.querySelectorAll('a[href^="#"]'); + + smoothScrollLinks.forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + + const targetId = this.getAttribute('href'); + const targetSection = document.querySelector(targetId); + + if (targetSection) { + const headerOffset = 80; + const elementPosition = targetSection.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - headerOffset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + } + }); + }); + + // Enhanced Scroll Animations + const animatedElements = document.querySelectorAll('.animate-on-scroll, .service-card, .feature, .step'); + + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' + }; + + const observer = new IntersectionObserver(function(entries) { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('animated'); + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + observer.unobserve(entry.target); + } + }); + }, observerOptions); + + animatedElements.forEach((element, index) => { + // Set initial state + element.style.opacity = '0'; + element.style.transform = 'translateY(30px)'; + element.style.transition = `opacity 0.8s ease-out ${index * 0.1}s, transform 0.8s ease-out ${index * 0.1}s`; + observer.observe(element); + }); + + // Add hover animations to service cards + const serviceCards = document.querySelectorAll('.service-card'); + serviceCards.forEach(card => { + card.addEventListener('mouseenter', function() { + this.style.transform = 'translateY(-10px) scale(1.02)'; + this.style.boxShadow = '0 20px 40px rgba(0, 0, 0, 0.15)'; + }); + + card.addEventListener('mouseleave', function() { + this.style.transform = 'translateY(0) scale(1)'; + this.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.08)'; + }); + }); + + // Add pulse animation to CTA buttons + const ctaButtons = document.querySelectorAll('.btn-primary'); + ctaButtons.forEach(btn => { + btn.addEventListener('mouseenter', function() { + this.style.animation = 'pulse 0.5s ease-in-out'; + }); + + btn.addEventListener('mouseleave', function() { + this.style.animation = 'none'; + }); + }); + + console.log('Enhanced animations initialized'); + + // Initialize reCAPTCHA and form tracking + let interactionScore = 0; + let formStartTime = Date.now(); + + // Track user interactions for bot detection + document.addEventListener('mousemove', () => interactionScore += 1); + document.addEventListener('keydown', () => interactionScore += 2); + document.addEventListener('click', () => interactionScore += 3); + + // Set form timestamp + const timestampField = document.getElementById('form_timestamp'); + if (timestampField) { + timestampField.value = formStartTime; + } + + // Form Validation and Enhancement with ARIA support + const contactForm = document.querySelector('.contact-form form'); + + // Helper function to set field error state + function setFieldError(fieldId, errorMessage) { + const field = document.getElementById(fieldId); + const errorSpan = document.getElementById(fieldId + '-error'); + if (field && errorSpan) { + field.setAttribute('aria-invalid', 'true'); + errorSpan.textContent = errorMessage; + } + } + + // Helper function to clear field error state + function clearFieldError(fieldId) { + const field = document.getElementById(fieldId); + const errorSpan = document.getElementById(fieldId + '-error'); + if (field && errorSpan) { + field.setAttribute('aria-invalid', 'false'); + errorSpan.textContent = ''; + } + } + + // Clear all form errors + function clearAllErrors() { + ['name', 'email', 'company', 'message'].forEach(clearFieldError); + } + + if (contactForm) { + // Real-time validation on blur + const requiredFields = contactForm.querySelectorAll('[required]'); + requiredFields.forEach(field => { + field.addEventListener('blur', function() { + validateField(this); + }); + field.addEventListener('input', function() { + if (this.getAttribute('aria-invalid') === 'true') { + validateField(this); + } + }); + }); + + function validateField(field) { + const value = field.value.trim(); + const fieldId = field.id; + + if (fieldId === 'name' && value.length < 2) { + setFieldError(fieldId, 'Please enter a valid name (at least 2 characters)'); + return false; + } else if (fieldId === 'email' && !isValidEmail(value)) { + setFieldError(fieldId, 'Please enter a valid email address'); + return false; + } else if (fieldId === 'company' && value.length < 2) { + setFieldError(fieldId, 'Please enter your organisation name'); + return false; + } else if (fieldId === 'message' && value.length < 10) { + setFieldError(fieldId, 'Please provide more details (at least 10 characters)'); + return false; + } + + clearFieldError(fieldId); + return true; + } + + contactForm.addEventListener('submit', function(e) { + e.preventDefault(); + clearAllErrors(); + + // Basic form validation + const formData = new FormData(this); + const name = formData.get('name'); + const email = formData.get('email'); + const company = formData.get('company'); + const message = formData.get('message'); + + // Validation + let isValid = true; + let firstErrorField = null; + + if (!name || name.trim().length < 2) { + setFieldError('name', 'Please enter a valid name (at least 2 characters)'); + isValid = false; + if (!firstErrorField) firstErrorField = document.getElementById('name'); + } + + if (!email || !isValidEmail(email)) { + setFieldError('email', 'Please enter a valid email address'); + isValid = false; + if (!firstErrorField) firstErrorField = document.getElementById('email'); + } + + if (!company || company.trim().length < 2) { + setFieldError('company', 'Please enter your organisation name'); + isValid = false; + if (!firstErrorField) firstErrorField = document.getElementById('company'); + } + + if (!message || message.trim().length < 10) { + setFieldError('message', 'Please provide more details (at least 10 characters)'); + isValid = false; + if (!firstErrorField) firstErrorField = document.getElementById('message'); + } + + // Focus first error field for accessibility + if (!isValid && firstErrorField) { + firstErrorField.focus(); + return; + } + + if (isValid) { + // Show loading state + const submitButton = this.querySelector('button[type="submit"]'); + const originalText = submitButton.textContent; + submitButton.textContent = 'Sending...'; + submitButton.disabled = true; + + // Update form timestamp to current time (ensures it's fresh at submission) + const timestampField = document.getElementById('form_timestamp'); + if (timestampField) { + timestampField.value = Date.now(); + } + // Recreate formData after updating timestamp + const freshFormData = new FormData(this); + + // Execute reCAPTCHA and submit form + if (typeof grecaptcha !== 'undefined') { + grecaptcha.ready(() => { + grecaptcha.execute(window.recaptchaSiteKey, {action: 'contact_form'}).then((token) => { + // Add reCAPTCHA token and interaction data + freshFormData.set('recaptcha_response', token); + freshFormData.set('interaction_token', btoa(JSON.stringify({score: Math.min(interactionScore, 100), time: Date.now() - formStartTime}))); + + // Submit form + fetch('contact-handler.php', { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + body: freshFormData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showNotification('Message sent successfully! We\'ll get back to you soon.', 'success'); + this.reset(); + } else { + showNotification(data.message || 'There was an error sending your message. Please try again.', 'error'); + } + }) + .catch(error => { + console.error('Error:', error); + showNotification('There was an error sending your message. Please try again.', 'error'); + }) + .finally(() => { + submitButton.textContent = originalText; + submitButton.disabled = false; + }); + }); + }); + } else { + // Fallback if reCAPTCHA not loaded + showNotification('Security verification not available. Please refresh the page.', 'error'); + submitButton.textContent = originalText; + submitButton.disabled = false; + } + } else { + showNotification(errors.join('
'), 'error'); + } + }); + } + + // Email validation function + function isValidEmail(email) { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + } + + // Notification system + function showNotification(message, type = 'info') { + // Remove existing notifications + const existingNotification = document.querySelector('.notification'); + if (existingNotification) { + existingNotification.remove(); + } + + // Create notification element + const notification = document.createElement('div'); + notification.className = `notification notification-${type}`; + notification.innerHTML = ` +
+ ${message} + +
+ `; + + // Add styles + notification.style.cssText = ` + position: fixed; + top: 20px; + right: 20px; + z-index: 10000; + background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'}; + color: white; + padding: 16px 20px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + max-width: 400px; + font-family: 'Inter', sans-serif; + font-size: 14px; + opacity: 0; + transform: translateX(100%); + transition: all 0.3s ease; + `; + + notification.querySelector('.notification-content').style.cssText = ` + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + `; + + notification.querySelector('.notification-close').style.cssText = ` + background: none; + border: none; + color: white; + font-size: 18px; + cursor: pointer; + padding: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + `; + + // Add to page + document.body.appendChild(notification); + + // Animate in + setTimeout(() => { + notification.style.opacity = '1'; + notification.style.transform = 'translateX(0)'; + }, 100); + + // Handle close button + notification.querySelector('.notification-close').addEventListener('click', () => { + hideNotification(notification); + }); + + // Auto hide after 5 seconds + setTimeout(() => { + if (document.body.contains(notification)) { + hideNotification(notification); + } + }, 5000); + } + + function hideNotification(notification) { + notification.style.opacity = '0'; + notification.style.transform = 'translateX(100%)'; + setTimeout(() => { + if (document.body.contains(notification)) { + notification.remove(); + } + }, 300); + } + + // Stats Counter Animation + const stats = document.querySelectorAll('.stat-number'); + + function animateStats() { + stats.forEach(stat => { + const originalText = stat.textContent.trim(); + console.log('Animating stat:', originalText); + + // Handle different stat types + if (originalText.includes('£2.5M+')) { + return; // Keep as is, don't animate currency + } else if (originalText.includes('99.8%')) { + animateNumber(stat, 0, 99.8, '%'); + } + }); + } + + function animateNumber(element, start, end, suffix = '') { + let current = start; + const increment = (end - start) / 60; // 60 steps for smoother animation + + const timer = setInterval(() => { + current += increment; + if (current >= end) { + current = end; + clearInterval(timer); + } + + element.textContent = current.toFixed(1) + suffix; + }, 50); // Every 50ms + } + + // Trigger stats animation when section is visible + const statsSection = document.querySelector('.hero-stats'); + if (statsSection) { + const statsObserver = new IntersectionObserver(function(entries) { + entries.forEach(entry => { + if (entry.isIntersecting) { + console.log('Stats section is visible, starting animation'); + setTimeout(() => { + animateStats(); + }, 500); // Small delay + statsObserver.unobserve(entry.target); + } + }); + }, { threshold: 0.3 }); + + statsObserver.observe(statsSection); + } else { + console.log('Stats section not found'); + } + + // Enhanced Lazy Loading for Images with WebP support + const images = document.querySelectorAll('img[loading="lazy"]'); + + // WebP support detection + function supportsWebP() { + const canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + return canvas.toDataURL('image/webp').indexOf('webp') !== -1; + } + + if ('IntersectionObserver' in window) { + const imageObserver = new IntersectionObserver(function(entries) { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + + // Handle data-src for lazy loading + if (img.dataset.src) { + img.src = img.dataset.src; + } + + // Handle WebP support + if (img.dataset.webp && supportsWebP()) { + img.src = img.dataset.webp; + } + + img.classList.add('loaded'); + img.style.opacity = '1'; + imageObserver.unobserve(img); + } + }); + }, { + rootMargin: '50px 0px', + threshold: 0.1 + }); + + images.forEach(img => { + // Set initial opacity for lazy images + if (img.loading === 'lazy') { + img.style.opacity = '0'; + img.style.transition = 'opacity 0.3s ease'; + } + imageObserver.observe(img); + }); + } + + // Scroll to Top Button + const scrollTopBtn = document.createElement('button'); + scrollTopBtn.innerHTML = '↑'; + scrollTopBtn.className = 'scroll-top-btn'; + scrollTopBtn.style.cssText = ` + position: fixed; + bottom: 30px; + right: 30px; + width: 50px; + height: 50px; + border: none; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + font-size: 20px; + cursor: pointer; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + `; + + document.body.appendChild(scrollTopBtn); + + scrollTopBtn.addEventListener('click', () => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }); + + // Show/hide scroll to top button + function handleScrollTopButton() { + if (window.scrollY > 500) { + scrollTopBtn.style.opacity = '1'; + scrollTopBtn.style.visibility = 'visible'; + } else { + scrollTopBtn.style.opacity = '0'; + scrollTopBtn.style.visibility = 'hidden'; + } + } + + window.addEventListener('scroll', handleScrollTopButton); + + // Performance: Throttle scroll events + let scrollTimeout; + const originalHandlers = [handleNavbarScroll, handleScrollTopButton]; + + function throttledScrollHandler() { + if (!scrollTimeout) { + scrollTimeout = setTimeout(() => { + originalHandlers.forEach(handler => handler()); + scrollTimeout = null; + }, 16); // ~60fps + } + } + + window.removeEventListener('scroll', handleNavbarScroll); + window.removeEventListener('scroll', handleScrollTopButton); + window.addEventListener('scroll', throttledScrollHandler); + + // Preload critical resources with WebP support + function preloadResource(href, as = 'image', type = null) { + const link = document.createElement('link'); + link.rel = 'preload'; + link.href = href; + link.as = as; + if (type) { + link.type = type; + } + document.head.appendChild(link); + } + + // Preload critical images with WebP format preference + function preloadCriticalImages() { + const criticalImages = [ + 'assets/images/ukds-main-logo.png', + 'assets/images/hero-data-analytics.svg' + ]; + + criticalImages.forEach(imagePath => { + // Try WebP first if supported + if (supportsWebP()) { + const webpPath = imagePath.replace(/\.(jpg|jpeg|png)$/i, '.webp'); + preloadResource(webpPath, 'image', 'image/webp'); + } else { + preloadResource(imagePath, 'image'); + } + }); + } + + // Initialize critical image preloading + preloadCriticalImages(); + + // Initialize tooltips (if needed) + const tooltipElements = document.querySelectorAll('[data-tooltip]'); + + tooltipElements.forEach(element => { + element.addEventListener('mouseenter', function() { + const tooltipText = this.getAttribute('data-tooltip'); + const tooltip = document.createElement('div'); + tooltip.className = 'tooltip'; + tooltip.textContent = tooltipText; + tooltip.style.cssText = ` + position: absolute; + background: #1a1a1a; + color: white; + padding: 8px 12px; + border-radius: 6px; + font-size: 14px; + white-space: nowrap; + z-index: 10000; + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; + `; + + document.body.appendChild(tooltip); + + const rect = this.getBoundingClientRect(); + tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px'; + tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px'; + + setTimeout(() => { + tooltip.style.opacity = '1'; + }, 100); + + this.addEventListener('mouseleave', function() { + tooltip.style.opacity = '0'; + setTimeout(() => { + if (document.body.contains(tooltip)) { + tooltip.remove(); + } + }, 300); + }, { once: true }); + }); + }); + + // Security: Prevent XSS in dynamic content + function sanitizeHTML(str) { + const temp = document.createElement('div'); + temp.textContent = str; + return temp.innerHTML; + } + + // Service Worker Registration (for PWA capabilities) + if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('/sw.js') + .then(registration => { + console.log('SW registered: ', registration); + }) + .catch(registrationError => { + console.log('SW registration failed: ', registrationError); + }); + }); + } + + // Performance monitoring + if ('performance' in window) { + window.addEventListener('load', function() { + setTimeout(() => { + const perfData = performance.getEntriesByType('navigation')[0]; + if (perfData) { + console.log('Page Load Performance:', { + 'DNS Lookup': Math.round(perfData.domainLookupEnd - perfData.domainLookupStart), + 'TCP Connection': Math.round(perfData.connectEnd - perfData.connectStart), + 'Request/Response': Math.round(perfData.responseEnd - perfData.requestStart), + 'DOM Processing': Math.round(perfData.domComplete - perfData.domLoading), + 'Total Load Time': Math.round(perfData.loadEventEnd - perfData.navigationStart) + }); + } + }, 0); + }); + } + + console.log('UK Data Services website initialized successfully'); + console.log('Performance optimizations: Lazy loading, WebP support, and preloading enabled'); + + // Universal Blog Pagination System + initializeBlogPagination(); + + function initializeBlogPagination() { + const paginationContainer = document.querySelector('.blog-pagination'); + const articlesGrid = document.querySelector('.articles-grid'); + + if (!paginationContainer || !articlesGrid) { + return; // No pagination on this page + } + + const prevButton = paginationContainer.querySelector('button:first-child'); + const nextButton = paginationContainer.querySelector('button:last-child'); + const paginationInfo = paginationContainer.querySelector('.pagination-info'); + + if (!prevButton || !nextButton || !paginationInfo) { + return; // Invalid pagination structure + } + + // Get current page from URL or default to 1 + const urlParams = new URLSearchParams(window.location.search); + let currentPage = parseInt(urlParams.get('page')) || 1; + + // Get all articles on the page + const allArticles = Array.from(articlesGrid.querySelectorAll('.article-card')); + const articlesPerPage = 6; + const totalPages = Math.ceil(allArticles.length / articlesPerPage); + + // If we have actual multiple pages of content, use the original pagination logic + // Otherwise, implement client-side pagination + if (totalPages <= 1) { + // Hide pagination if not needed + paginationContainer.style.display = 'none'; + return; + } + + function renderPage(page, shouldScroll = false) { + // Hide all articles + allArticles.forEach(article => { + article.style.display = 'none'; + }); + + // Show articles for current page + const startIndex = (page - 1) * articlesPerPage; + const endIndex = startIndex + articlesPerPage; + + for (let i = startIndex; i < endIndex && i < allArticles.length; i++) { + allArticles[i].style.display = 'block'; + allArticles[i].style.animation = 'fadeInUp 0.6s ease forwards'; + } + + // Update pagination info + paginationInfo.textContent = `Page ${page} of ${totalPages}`; + + // Update button states + prevButton.disabled = (page <= 1); + nextButton.disabled = (page >= totalPages); + + // Update URL without page reload + const newUrl = new URL(window.location); + if (page > 1) { + newUrl.searchParams.set('page', page); + } else { + newUrl.searchParams.delete('page'); + } + window.history.replaceState({}, '', newUrl); + + // Only scroll to articles section when navigating between pages + if (shouldScroll) { + articlesGrid.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + } + + // Event listeners + prevButton.addEventListener('click', function(e) { + e.preventDefault(); + if (currentPage > 1) { + currentPage--; + renderPage(currentPage, true); + } + }); + + nextButton.addEventListener('click', function(e) { + e.preventDefault(); + if (currentPage < totalPages) { + currentPage++; + renderPage(currentPage, true); + } + }); + + // Initialize first page (don't scroll on initial load) + renderPage(currentPage, false); + + // Add CSS animation for article transitions + const style = document.createElement('style'); + style.textContent = ` + @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + .article-card { + transition: opacity 0.3s ease, transform 0.3s ease; + } + + .article-card[style*="display: none"] { + opacity: 0; + transform: translateY(20px); + } + `; + document.head.appendChild(style); + + console.log(`Blog pagination initialized: ${totalPages} pages, ${allArticles.length} articles`); + } + + // Viewport-based Image Loading Optimization + function initializeViewportImageLoading() { + // Intersection Observer for lazy loading optimization + const imageObserver = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const img = entry.target; + + // Load high-quality version when in viewport + if (img.dataset.src) { + img.src = img.dataset.src; + img.removeAttribute('data-src'); + } + + // Load WebP for supported browsers + if (img.dataset.webp && supportsWebP()) { + img.src = img.dataset.webp; + } + + observer.unobserve(img); + } + }); + }, { + rootMargin: '50px 0px', + threshold: 0.01 + }); + + // Observe all images with data-src + document.querySelectorAll('img[data-src]').forEach(img => { + imageObserver.observe(img); + }); + + // WebP support detection + function supportsWebP() { + return new Promise(resolve => { + const webP = new Image(); + webP.onload = webP.onerror = () => resolve(webP.height === 2); + webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA'; + }); + } + } + + // Touch Target Optimization for Mobile + function optimizeTouchTargets() { + const minTouchSize = 44; // 44px minimum touch target + + // Check and optimize button sizes + document.querySelectorAll('button, .btn, a[role="button"]').forEach(element => { + const rect = element.getBoundingClientRect(); + if (rect.width < minTouchSize || rect.height < minTouchSize) { + element.style.minWidth = minTouchSize + 'px'; + element.style.minHeight = minTouchSize + 'px'; + element.style.display = 'inline-flex'; + element.style.alignItems = 'center'; + element.style.justifyContent = 'center'; + } + }); + + // Add touch-friendly spacing + document.querySelectorAll('.nav-menu a, .social-links a').forEach(element => { + element.style.padding = '12px 16px'; + element.style.margin = '4px'; + }); + } + + // Initialize optimizations + initializeViewportImageLoading(); + if ('ontouchstart' in window) { + optimizeTouchTargets(); + } +}); \ No newline at end of file diff --git a/blog/articles/healthcare-research-data-collection.php b/blog/articles/healthcare-research-data-collection.php index 0c20895..67135cf 100644 --- a/blog/articles/healthcare-research-data-collection.php +++ b/blog/articles/healthcare-research-data-collection.php @@ -220,7 +220,6 @@ $breadcrumbs = [
  • GDPR Compliance: Full adherence to data protection regulations
  • NHS Data Governance: Approved by NHS Digital and local Caldicott Guardians
  • ICO Registration: Registered with Information Commissioner's Office
  • -
  • ISO 27001: Information security management certification
  • Good Clinical Practice: Compliance with clinical trial regulations
  • diff --git a/faq-enhanced.php b/faq-enhanced.php index 1b4a1a3..72f5d8f 100644 --- a/faq-enhanced.php +++ b/faq-enhanced.php @@ -45,7 +45,7 @@ $keywords = "UK data services FAQ, web scraping questions, data cleaning help, b "name": "How secure is my data with UK Data Services?", "acceptedAnswer": { "@type": "Answer", - "text": "We use enterprise-grade security measures including 256-bit encryption, secure transfer protocols, access controls, and staff NDAs. We're ISO 27001 certified and fully GDPR compliant." + "text": "We use enterprise-grade security measures including 256-bit encryption, secure transfer protocols, access controls, and staff NDAs. We're fully GDPR compliant." } }, { @@ -321,7 +321,7 @@ $keywords = "UK data services FAQ, web scraping questions, data cleaning help, b
    • Encryption: 256-bit AES encryption for data at rest and in transit
    • Access Controls: Multi-factor authentication and role-based access
    • -
    • Secure Facilities: ISO 27001 certified data centers in the UK
    • +
    • Secure Facilities: UK-based secure data centers
    • Staff Training: Regular security training and background checks
    • Network Security: Firewalls, intrusion detection, and monitoring
    • Backup & Recovery: Regular backups with tested recovery procedures
    • diff --git a/includes/schema/faq-schema.php b/includes/schema/faq-schema.php index 372afff..d6d75bf 100644 --- a/includes/schema/faq-schema.php +++ b/includes/schema/faq-schema.php @@ -108,7 +108,7 @@ $commonFAQs = [ ], [ 'question' => 'What security measures do you have in place?', - 'answer' => 'We implement enterprise-grade security including encrypted data transfer (SSL/TLS), secure data storage, access controls, regular security audits, and compliance with ISO 27001 principles.' + 'answer' => 'We implement enterprise-grade security including encrypted data transfer (SSL/TLS), secure data storage, access controls, and regular security audits.' ] ], 'property-data' => [ diff --git a/index.php b/index.php index 71754b0..1818509 100644 --- a/index.php +++ b/index.php @@ -1121,11 +1121,6 @@ $twitter_card_image = "https://ukdataservices.co.uk/assets/images/ukds-main-logo

      Trusted by 150+ UK Businesses | Fully Insured | Enterprise-Grade Security

      -
      -
      🔒
      - ISO 27001 - Security Certified -
      GDPR Compliant