Lead #3921
Best Contact
Emanuel Spence — Owner
Email
operations@esmsl.com (person)
Override
Captured Pages
http://esmsl.co.uk/staff
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/our-team
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/meet-the-team
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/team
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/about
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/contact
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };
http://esmsl.co.uk/
Status: 200
Emails: operations@esmsl.com
View text
Emanuel Spence Home Contact us Our work Services Testimonials Plumbing and Gas Experts in the North East Emanuel Spence are committed to offering reliable, high-quality services to meet the needs of our residential and commercial customers since 1895. Get in touch const header = document.querySelector(".header"); { if (window.IntersectionObserver) { // Transition the header type once we scroll 10% of the hero-section height. const scrollObserver = new IntersectionObserver( (event) => { for (const intersection of event) { if (intersection.intersectionRatio < 0.9) { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } } }, { rootMargin: "0px", threshold: [0.9, 1], } ); scrollObserver.observe(document.getElementById("hero-section")); } } There was an error sending your enquiry. Please try again later. // These callbacks need to be declared before recaptcha let formSubmissionButton; function enableButton() { formSubmissionButton.removeAttribute("disabled"); }; function disableButton() { formSubmissionButton.setAttribute("disabled", "disabled"); }; How can we help? Please fill in the form to help us understand what you need. We will get back to you within 24 hours. Name Please enter at least 2 characters Email address Wrong Phone Wrong Job address Wrong Job description Please include any information that may help us provide you with a more accurate quote Wrong Attachments - Optional Upload files Or drag files here to upload Max size: 20MB Send your enquiry Sending Loading... Enquiry sent Close // Recapcha submission formSubmissionButton = document .getElementById("form-submit-btn"); formSubmissionButton.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); submitEnquiry(e); }); let confirmationModal; const formElements = { name: { invalid: "Please enter at least 2 characters", empty: "Please enter your name", }, email: { invalid: "Invalid email address", empty: "Please enter your email", }, description: { invalid: "Description must contain between 2 and 1000 characters", empty: "Please enter the Description", }, location: { invalid: "Address must contain between 2 and 100 characters", empty: "Please enter your address", }, phone: { invalid: "Phone number must contain between 2 and 30 characters", empty: "Please enter your phone number", }, }; const validateInput = (event) => { const input = event.target; const rules = formElements[input.id]; const validity = input.validity; if (!validity.valid) { if (validity.valueMissing) { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.empty; } else { input.parentElement.querySelector( ".invalid-feedback" ).innerHTML = rules.invalid; } input.classList.add("is-invalid"); } else { input.classList.remove("is-invalid"); } input.parentElement.classList.add("was-validated"); }; const validateInputIfAlreadyLeftInput = (event) => event.target.parentElement.classList.contains("was-validated") && validateInput(event); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.addEventListener("blur", validateInput); input.addEventListener("input", validateInputIfAlreadyLeftInput); } const setInputValidationMessages = () => { for (const inputId of Object.keys(formElements)) { validateInput({ target: document.getElementById(inputId), }); } }; const form = document.getElementById("enquiry-form"); const dropZone = document.getElementById("dropzone"); const attachments = []; const maxFileSize = 1024 * 1024 * 20; let currentFileSize = 0; const attachmentsInput = document.getElementById("attachments"); const isDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.add("over"); }; const isNotDragging = (e) => { e.preventDefault(); e.stopImmediatePropagation(); dropZone.classList.remove("over"); }; const sizeUsed = document.getElementById("size-used"); const updateUsedSize = () => { const percentOfTotalAllowed = currentFileSize / maxFileSize; sizeUsed.innerText = `${(percentOfTotalAllowed * 100).toFixed( 1 )}% used`; }; const clearAttachments = (e) => { e && e.preventDefault(); e && e.stopImmediatePropagation(); while (attachments.length) { const a = attachments.pop(); currentFileSize -= a.size; } attachmentsInput.value = ""; const fileTable = document.getElementById("attachments-list"); fileTable && fileTable.parentElement.removeChild(fileTable); updateUsedSize(); }; const renderFileList = (fileTable) => { fileTable.innerHTML = ""; const attachmentsWrapper = document.createElement("div"); fileTable.appendChild(attachmentsWrapper); for (const attachment of attachments) { const c1 = document.createElement("div"); c1.classList.add( "col-12", "py-md-1", "py-3", "text-md-start", "text-center", "text-break" ); const paperclip = document.createElement("i"); paperclip.classList.add("bi", "bi-paperclip"); c1.appendChild(paperclip); const fileName = document.createElement("span"); c1.appendChild(fileName); attachmentsWrapper.appendChild(c1); fileName.innerText = attachment.name; } if (attachments.length) { const c1 = document.createElement("div"); c1.classList.add("align-self-md-end", "align-self-center"); fileTable.appendChild(c1); const clearButton = document.createElement("button"); clearButton.classList.add("btn", "btn-outline-dark"); clearButton.innerText = "Clear attachments"; clearButton.addEventListener("click", clearAttachments); c1.appendChild(clearButton); } }; const addFiles = (files) => { let allFilesAdded = true; let fileTable; if (attachments.length === 0) { // Init file table: fileTable = document.createElement("div"); fileTable.id = "attachments-list"; fileTable.classList.add( "flex-md-row", "flex-column", "d-flex", "pt-md-3", "pt-0", "justify-content-between" ); dropZone.appendChild(fileTable); } else { fileTable = document.getElementById("attachments-list"); } for (const file of files) { if (currentFileSize + file.size < maxFileSize) { currentFileSize += file.size; attachments.push(file); } else { // Maximum size exceeded, some files not added. allFilesAdded = false; } } if (!allFilesAdded) { // TODO: Show message about files not added? } renderFileList(fileTable); updateUsedSize(); }; dropZone.addEventListener("dragstart", isDragging); dropZone.addEventListener("dragover", isDragging); dropZone.addEventListener("dragenter", isDragging); dropZone.addEventListener("dragleave", isNotDragging); dropZone.addEventListener("dragend", isNotDragging); dropZone.addEventListener("drop", (event) => { isNotDragging(event); addFiles(event.dataTransfer.files); }); attachmentsInput.addEventListener("change", (event) => addFiles(event.target.files) ); const clearForm = () => { form.classList.remove("was-validated"); for (const inputId of Object.keys(formElements)) { const input = document.getElementById(inputId); input.parentElement.classList.remove("was-validated"); } form.reset(); }; let sendingEnquiry = false; let sendSuccess = false; const submitEnquiry = (event) => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); setInputValidationMessages(); } else { event.preventDefault(); event.stopPropagation(); sendingEnquiry = true; confirmationModal.show(); const modal = document.getElementById("confirmation-modal"); const sending = modal.querySelector(".sending"); const sent = modal.querySelector(".sent"); sending.classList.add("show"); sending.classList.remove("visually-hidden"); sent.classList.remove("show"); sent.classList.add("visually-hidden"); const finishedSendingEnquiry = () => { sendingEnquiry = false; sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); clearAttachments(); }; const failedSendingEnquiry = () => { const toast = new bootstrap.Toast(document.getElementById("enquire-toast")); toast.show(); sending.classList.remove("show"); sending.classList.add("visually-hidden"); sent.classList.add("show"); sent.classList.remove("visually-hidden"); }; // Gather the form data const formData = new FormData(form); for (const attachment of attachments) { formData.append("file", attachment); } // Sending // NOTE - add some artificial delays in here so that it doesn't seem too quick! const response = fetch("https://tradehq.co.uk/esmsl/enquire", { method: "POST", body: formData }); response.then((r) => { sendSuccess = true; setTimeout(finishedSendingEnquiry, 500); }).catch((e) => { sendingEnquiry = false; confirmationModal.hide(); setTimeout(failedSendingEnquiry, 300); }); } form.classList.add("was-validated"); }; function enquiryInit() { const modalEl = document.getElementById("confirmation-modal"); if (modalEl) { confirmationModal = new bootstrap.Modal(modalEl); modalEl.addEventListener("hide.bs.modal", (event) => { if (sendingEnquiry) { event.preventDefault(); } else { if (sendSuccess) { clearForm(); } } }); } } Our work Previous Next // Use media queries to init or destroy carousel as needed. // Initialise carousel: let carousel; const gallery = document.querySelector("#gallery"); const galleryItems = gallery.querySelectorAll( ".carousel-inner > div" ); const createCarousel = () => { gallery.classList.add("carousel"); galleryItems.forEach((item) => item.classList.add("carousel-item")); if (window.bootstrap) { carousel = new bootstrap.Carousel(gallery, { interval: 12000, }); } }; const destroyCarousel = () => { gallery.classList.remove("carousel"); galleryItems.forEach((item) => item.classList.remove("carousel-item") ); carousel && carousel.dispose(); carousel = null; }; const handleCarouselLifecycle = (useCarousel) => { if (useCarousel && !carousel) { createCarousel(); } else if (!useCarousel && carousel) { destroyCarousel(); } }; const mobileQuery = window.matchMedia("(max-width: 767px)"); const mobileBreakpointChange = (e) => { handleCarouselLifecycle(e.matches); }; if ("addEventListener" in mobileQuery) { mobileQuery.addEventListener("change", mobileBreakpointChange); } else if ("addListener" in mobileQuery) { mobileQuery.addListener(mobileBreakpointChange); } handleCarouselLifecycle(mobileQuery.matches); function galleryInit() { if (mobileQuery.matches && !carousel) { carousel = new bootstrap.Carousel(gallery, { interval: 2000, }); } }; let map; // Match media to see if we need to offset the map, if we're in theme-1 // We need to work out how many longitude radians the width of our map is. function initMap() { const mapEl = document.getElementById("map"); if (mapEl) { map = new google.maps.Map(mapEl, { center: { lat: 54.5023295, lng: -1.3330089 }, zoom: 8, disableDefaultUI: true, gestureHandling: "none", zoomControl: false, }); const mapIsFullWidth = () => mapEl.getBoundingClientRect().width === document.body.getBoundingClientRect().width; const handleMapLocationCentering = () => { if (map) { if (mapIsFullWidth()) { const bounds = map.getBounds(); if (bounds) { const w = bounds.getSouthWest().lng(); let e = bounds.getNorthEast().lng(); if (w < 180 && w > 0 && e < 0) { e = 180 + (180 - Math.abs(e)); } map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089 - (Math.abs((e - w)) * 0.25))); } } else { map.setCenter(new google.maps.LatLng(54.5023295, -1.3330089)); } } }; window.addEventListener("resize", handleMapLocationCentering); handleMapLocationCentering(); google.maps.event.addListenerOnce(map, 'idle', handleMapLocationCentering); } } Contact us 1 Valley Drive, Yarm, UK operations@esmsl.com 01642247938 Our services Domestic Plumbing and Heating Full range of plumbing services for homes, renovations, upgrades, repairs and maintenance by experienced and careful plumbers. From a dripping tap to installing and repairing bathrooms, showers and wet rooms. Gasfitting Installation, maintenance and repair services for gas cooking, hot water systems and heating by Gassafe certified gasfitters. www.esmsl.co.uk Servicecare Gas Heating Cover We offer a competitive gas central heating cover for domestic and landlord's cover. Included servicing and gas safety checks. Commercial Plumbing Identification of leaks and carrying out the necessary repairs for both gas and water systems. We offer a full range of services to Schools, public houses and retail premises to keep your services running. Commercial Heating Repairs and Installations. We offer a full range of heating services to commercial and business clients. With full breakdown and repairs service. Gas servicing and Gas safety checks We can carry out Domestic, Landlords and Commercial servicing for gas servicing and safety checks. Testimonials Take a look at what customers say about Emanuel Spence Maintenance Services Emma Wilson Residential customer The team at Emanuel Spence Maintenance Services were extremely professional and easy to work with. They helped us make good, informed decisions during our renovation. Dawn From Newcastle Rated People Customer Replied with hours of contacting, booked in and completed the next day to a high standard, efficient, and work completed without issue. Would highly recommend emanuel spence. Steve form NE25 Rated People Customer Prompt response, and excellent customer service. Samantha from TS8 Rated People Customer Highly recommend, Jill on the phone was professional and friendly. The plumber came when booked and sorted the issue. The whole experience =was painless. Ken Husband Google Customer The faulty boiler has been repaired. The engineer was excellent, very easy to talk to and very knowledgable a credit to Emanuel Spence. Previous Next { const testimonialsCarousel = document.getElementById( "testimonials-carousel" ); if (testimonialsCarousel) { // Cycle through each item, and get the height. let maxHeight = 0; testimonialsCarousel.addEventListener("slide.bs.carousel", () => { // Set the min height for all carousel items to at least the height // of the current item. const currentSlide = document.querySelector( ".testimonial-item.active" ); if (currentSlide) { const oldHeight = maxHeight; maxHeight = Math.max( currentSlide.getBoundingClientRect().height, maxHeight ); if (maxHeight > oldHeight) { for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `${maxHeight}px`; } } } }); window.addEventListener("resize", () => { // Reset max height if window gets resized maxHeight = 0; for (const slide of document.querySelectorAll( ".testimonial-item" )) { slide.style.minHeight = `auto`; } }); } } operations@esmsl.com 01642247938 1 Valley Drive, Yarm, UK © Powered by Tradify Plumbing and Gas Management Software { // Section headers need to be dynamically offset based on the height of the navbar, once all sections // are loaded. const navHeight = document .getElementById("header") .getBoundingClientRect().height; document .querySelectorAll("main > section .section-anchor") .forEach((item) => { item.style.top = `-${navHeight}px`; }); } const bootstrapInit = () => { { // Initialise scrollspy window.addEventListener("activate.bs.scrollspy", () => { const section = document.querySelector(".nav-link.active"); if (section.innerText !== "Home") { header.classList.add("scrolled"); } else { header.classList.remove("scrolled"); } }); } { // Initialise the mobile menu const navLinks = document.querySelectorAll(".nav-item"); const menuToggle = document.getElementById( "navbar-supported-content" ); const bsCollapse = new bootstrap.Collapse(menuToggle, { toggle: false, }); const mobileQuery = window.matchMedia("(max-width: 767px)"); navLinks.forEach((link) => { link.addEventListener( "click", () => mobileQuery.matches && bsCollapse.toggle() ); }); } // Init optional components that rely on bootstrap js window.galleryInit && galleryInit(); window.enquiryInit && enquiryInit(); };