1<!DOCTYPE html> 2<!-- 3 4 Powered by... 5 _ _ 6 _ __ (_)_ __ ___ | |__ _ _ 7 | '_ \| | '_ ` _ \| '_ \| | | | 8 | | | | | | | | | | |_) | |_| | 9 |_| |_|_|_| |_| |_|_.__/ \__,_| 10 11 Interested? Visit https://nimbu.io 12 13--> <html class='no-js' itemscope itemtype='http://schema.org/WebPage' lang='en'> <head> <meta charset='utf-8'> <title>About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control</title> <meta content='hammer-ims' name='author'> <meta content='width=device-width, initial-scale=1, shrink-to-fit=no' name='viewport'> <meta content='Hammer-IMS, quality control, process control, innovative solutions, sustainability, precision, industrial technology, customer-centric solutions, quality, innovation, collaboration, process optimization' name='keywords'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' name='description'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/apple-touch-icon.png?8xqb1ia' rel='apple-touch-icon' sizes='180x180'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon-32x32.png?8xqb1ia' rel='icon' sizes='32x32' type='image/png'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon-16x16.png?8xqb1ia' rel='icon' sizes='16x16' type='image/png'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/manifest.json?8xqb1ia' rel='manifest'> <link color='#000000' href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/safari-pinned-tab.svg?8xqb1ia' rel='mask-icon'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon.ico?8xqb1ia' rel='shortcut icon'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/browserconfig.xml?8xqb1ia' name='msapplication-config'> <meta content='#ffe431' name='theme-color'> <meta content='616924933' property='fb:admins'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' property='og:title'> <meta content='website' property='og:type'> <meta content='https://www.hammer-ims.com/about' property='og:url'> <meta content='hammer-ims.com' property='og:site_name'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' property='og:description'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' itemprop='name'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' itemprop='description'> <meta content='summary_large_image' name='twitter:card'> <meta content='@hammer-ims' name='twitter:site'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' name='twitter:title'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' name='twitter:description'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' name='twitter:image'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' property='og:image'> <meta content='1200' itemprop='og:image:width'> <meta content='630' itemprop='og:image:height'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' itemprop='image'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' rel='image_src' type='image/jpeg'> <link href="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/stylesheets/app_v2.css?8xqb1ia" media="screen,print" rel="stylesheet" type="text/css"/> <meta name="csrf-param" content="authenticity_token"/> <meta name="csrf-token" content="WBjM_xudgw17VUlGPBK2gnnGx8hlXqOEp7JgoVRkXV3D4FmjwyMN6_egXakzO8ilX4uQO_h9u7Lk3FbM8I0zpA"/> <link rel="dns-prefetch" href="https://cdn.nimbu.io"/> <link rel="dns-prefetch" href="https://static.nimbu.io"/> <meta http-equiv="x-dns-prefetch-control" content="on"/> <link rel="canonical" href="https://www.hammer-ims.com/about"/> <link rel="alternate" href="https://www.hammer-ims.com/de/uber-uns" hreflang="de"/>
13 <link rel="alternate" href="https://www.hammer-ims.com/about" hreflang="en"/> <link rel="alternate" href="https://www.hammer-ims.com/fr/about" hreflang="fr"/> <link rel="alternate" href="https://www.hammer-ims.com/nl/over-ons" hreflang="nl"/> <meta name="referrer" content="strict-origin-when-cross-origin"/>
13<script> 14 window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} 15 gtag('consent', 'default', { 'ad_personalization': 'denied', 'ad_user_data': 'denied' }); 16 </script>
16
16<script data-type="text/javascript" type="opt-in" data-name="google_ads"> 17 gtag('consent', 'update', {'ad_personalization': 'granted', 'ad_user_data': 'granted'}); 18 </script>
vendor: 64 bytes, line 18
18 <script async src="https://www.googletagmanager.com/gtag/js?id=
18AW-959597372
vendor: 12 bytes, line 18
18"></script>
18<script> 19
vendor: 146 bytes, lines 19-23
19window.dataLayer = window.dataLayer || []; 20 function gtag(){dataLayer.push(arguments);} 21 gtag('js', new Date()); 22 23 gtag('config', '
23AW-959597372
vendor: 8 bytes, lines 23-24
23'); 24
24</script>
24
24<script> 25 gtag('event', 'conversion', {'send_to': 'AW-959597372/LD5yCLPnieIBELyWyckD'}); 26 </script>
26
26<script data-type='text/javascript' type='opt-in' data-name='hotjar'>
vendor: 131 bytes, lines 26-29
26 27 (function(h,o,t,j,a,r){ 28 h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; 29 h._hjSettings={hjid:
294941049
vendor: 272 bytes, lines 29-35
29,hjsv:6}; 30 a=o.getElementsByTagName('head')[0]; 31 r=o.createElement('script');r.async=1; 32 r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; 33 a.appendChild(r); 34 })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); 35
35</script>
35
35<script type="application/json" id="nimbu-consent-config">{"privacyPolicy":"/privacy","purposes":{"functional":"Basic website operation (functional - necessary)","analytics":"Tracking statistics","content":"Content from external sources","advertising":"Personalised advertising"},"apps":[{"name":"cms","title":"CMS","description":"Cookies that ensure that this website works.","purposes":["functional"],"required":true,"optOut":false,"default":true,"cookies":[],"placeholderMessage":"","placeholderAction":""},{"name":"nimbu_analytics","title":"Nimbu Analytics","description":" Built-in visitor statistics in the nimbu CMS (platform on which the website is built).","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":["_nimbu_session","_nimbu_secure_session","__cfduid","_nimbu_id","_nimbu_visit_id"],"placeholderMessage":"","placeholderAction":""},{"name":"google_analytics","title":"Google Analytics","description":"Visitor statistics that are tracked by Google and that help us improve the content of the website.","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":[["__utma","/",".hammer-ims.com"],["__utmt","/",".hammer-ims.com"],["__utmb","/",".hammer-ims.com"],["__utmc","/",".hammer-ims.com"],["__utmz","/",".hammer-ims.com"],["__utmv","/",".hammer-ims.com"],["/^_ga(_.*)?/","/","."]],"placeholderMessage":"","placeholderAction":""},{"name":"youtube","title":"Youtube","description":"Embedded YouTube videos","purposes":["content"],"required":false,"optOut":false,"default":true,"cookies":[["GPS","/",".youtube.com"],["IDE","/",".doubleclick.net"],["VISITOR_INFO1_LIVE","/",".youtube.com"],["YSC","/",".youtube.com"]],"placeholderMessage":"Hier wordt inhoud van Youtube geblokkeerd.","placeholderAction":"Klik hier om uw instellingen te wijzigen"},{"name":"google_ads","title":"Google Ads","description":"Display of personalised advertisements","purposes":["advertising"],"required":false,"optOut":false,"default":true,"cookies":[],"placeholderMessage":"","placeholderAction":""},{"name":"hotjar","title":"Hotjar","description":"Track user behavior and improve user experience on the website","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":[["/^_hj(_.*)?/","/","."]],"placeholderMessage":"","placeholderAction":""},{"name":"zoho","title":"Zoho","description":"Tracking visitor statistics","purposes":["analytics"],"required":false,"optOut":false,"default":false,"cookies":[["663a60c55d","/","salesiq.zoho.com"],["LS_CSRF_TOKEN","/","salesiq.zoho.com"],["ZohoMarkRef","/",".zoho.com"],["ZohoMarkSrc","/",".zoho.com"],["cookie-uid","/",".zoho.com"],["hammer-_zldp","/",".hammer-ims.com"],["hammer-_zldt","/",".hammer-ims.com"],["zld113379000000002043float","/",".hammer-ims.com"]],"placeholderMessage":"","placeholderAction":""}],"message":"We use cookies for the following purposes: {purposes}.","changed":"We added new applications that use cookies since your last visit, please adjust your preferences.","learnMore":"Read more","ok":"OK","save":"Save","decline":"Reject","close":"Close","modal":{"title":"We use cookies","message":"We use cookies. Below you can choose which applications you wish to allow.","privacyPolicy":{"name":"Privacy policy","text":"To know more, read our {privacyPolicy}."}}}</script>
35
35<script defer type="application/javascript" src="https://cdn.nimbu.io/js/consent/nimbu-consent-manager-v1.1.4.js" data-style-prefix="nimbuCookie"></script>
35
35<script> 36 window.dataLayer = window.dataLayer || []; 37 function gtag(){dataLayer.push(arguments);} 38 gtag('consent', 'default', { 39 'ad_storage': 'denied', 40 'analytics_storage': 'denied', 41 'ad_personalization': 'denied', 42 'ad_user_data': 'denied' , 43 'wait_for_update': 500 44 }); 45 gtag('set', 'ads_data_redaction', true); 46</script>
46
46<script>
vendor: 327 bytes, lines 46-50
46(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 47new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 48j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 49'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 50})(window,document,'script','dataLayer','
50GTM-53XGBWK
vendor: 3 bytes, line 50
50');
50</script>
50
50<script type="opt-in" data-type="text/javascript" data-name="google_ads"> 51 gtag('consent', 'update', {'ad_storage': 'granted', 'ad_personalization': 'granted', 'ad_user_data': 'granted'}); 52</script>
52
52<script type="opt-in" data-type="text/javascript" data-name="google_analytics"> 53 gtag('consent', 'update', {'analytics_storage': 'granted'}); 54</script>
54</head> <body class='body-page_v2' id='body'> <div aria-describedby='unsupportedBrowserDesc' aria-labelledby='unsupportedBrowserTitle' aria-modal='true' data-nosnippet id='unsupported-browser' role='dialog' style='display: none'> <div> <img aria-hidden='true' loading='lazy' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/browser-unsupported.svg?8xqb1ia' alt="browser unsupported"> <div class='h2' id='unsupportedBrowserTitle'>Je browser wordt niet langer ondersteund</div> <div class='h3' id='unsupportedBrowserDesc'> Om onze website te gebruiken kan je één van de volgende browsers proberen: <a href='https://www.google.com/chrome/'>Google Chrome,</a> <a href='https://www.mozilla.org/nl/firefox/new/'>Mozilla Firefox</a> of <a href='https://www.microsoft.com/nl-nl/edge'>Microsoft Edge</a> </div> <button onclick='ignoreOldBrowser();'> Ga toch door </button> <div class='unsupported-browser-risk'> Door de website toch te gebruiken met deze browser kan de website er anders uitzien dan bedoelt en kunnen wij de veiligheid niet garanderen </div> </div> </div> <style> 55 @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { 56 #unsupported-browser { 57 display: block !important; 58 font-family: Helvetica, sans-serif; 59 } 60 } 61 #unsupported-browser { 62 position: fixed; 63 top: 0; 64 left: 0; 65 right: 0; 66 bottom: 0; 67 background-color: #f6f6f6; 68 z-index: 99999999999; 69 text-align: center; 70 } 71 #unsupported-browser .h3 { 72 font-size: 18px; 73 text-transform: initial; 74 line-height: 27px; 75 width: 400px; 76 max-width: 100%; 77 margin-left: auto; 78 margin-right: auto; 79 margin-top: 0; 80 } 81 #unsupported-browser .h2 { 82 font-size: 25px; 83 line-height: 30px; 84 font-weight: 600; 85 text-transform: initial; 86 margin-bottom: 10px; 87 margin-top: 30px 88 } 89 #unsupported-browser > div { 90 position: absolute; 91 top: 50%; 92 left: 50%; 93 transform: translate(-50%, -50%); 94 max-width: 100%; 95 padding: 30px; 96 } 97 #unsupported-browser img { 98 width: 500px; 99 height: 209px; 100 max-width: 100%; 101 } 102 #unsupported-browser button { 103 border: 1px solid #888 ; 104 background-color: transparent; 105 padding: 7px 15px; 106 color: #888; 107 margin-top: 60px; 108 } 109 .unsupported-browser-risk { 110 margin-left: auto; 111 margin-right: auto; 112 max-width: 80%; 113 width: 500px; 114 margin-top: 10px; 115 font-size: 14px; 116 line-height: 16px; 117 color: #888; 118 } 119</style>
119<script> 120 var getCookieValue = document.cookie.match('(^|;) ?' + "ignore_old_browser" + '=([^;]*)(;|$)'); 121 var cookieValue = getCookieValue ? getCookieValue[2] : null; 122 123 if (cookieValue != null) { 124 var oldBrowserMessage = document.getElementById("unsupported-browser") 125 oldBrowserMessage.parentNode.removeChild(oldBrowserMessage); 126 } 127 128 function ignoreOldBrowser() { 129 document.cookie="ignore_old_browser=true" 130 var oldBrowserMessage = document.getElementById("unsupported-browser") 131 oldBrowserMessage.parentNode.removeChild(oldBrowserMessage); 132 } 133</script>
133 <div class='page-container'> <header class='main-header'> <div class='top-nav'> <div class='container'> <nav id='top-nav'> <ul class='nav'> <li id="top-about-us-link" class="link current first"><a href="/about">About us</a></li> <li id="top-news-link" class="link"><a href="/news">News</a></li> <li id="top-careers-link" class="link"><a href="/careers">Careers</a></li> <li id="top-locations-link" class="link"><a href="/locations">Locations</a></li> <li id="top-exhibitions-link" class="link"><a href="/exhibitions">Exhibitions</a></li> <li id="top-customer-support-link" class="link"><a href="/login">Customer support</a></li> <li id="top-representatives-link" class="link last"><a href="/representatives">Representatives</a></li></ul> <div class='social-medias'> <div class='language-buttons nav-link d-flex justify-content-center align-items-center'> <a class='' href='/de/uber-uns'> de </a> <a class='active order-first' href='/en/about'> en </a> <a class='' href='/fr/about'> fr </a> <a class='' href='/nl/over-ons'> nl </a> </div> <div class='social-media nav-link'> <a href='https://www.linkedin.com/company/hammer-ims/' target='_blank'> <i class='fab fa-linkedin'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.youtube.com/@Hammer-IMS/videos' target='_blank'> <i class='fab fa-youtube'></i> </a> </div> <div class='search-btn nav-link'> <button type='button'> <i class='far fa-search'></i> </button> </div> </div> </nav> </div> </div> <div class='main-nav-container'> <div class='container'> <a class='logo' href='/'> <img alt='logo' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/logo_v2.png?8xqb1ia'> </a> <nav id='main-nav'> <ul class='nav'> <li id="main-div-style-text-align-center-line-height-1-5-measuring-br-technologies-div-link" class="link first"><a href="/measuring-technologies"><div style="text-align:center;line-height:1.5;">Measuring <br> technologies</div></a></li> <li id="main-div-style-text-align-center-line-height-1-5-camera-br-technologies-div-link" class="link"><a href="/camera-technologies"><div style="text-align:center;line-height:1.5;">Camera<br> technologies</div></a></li> <li id="main-industries-link" class="link"><a href="/industries">Industries</a></li> <li id="main-products-link" class="link"><a href="/products">Products</a></li> <li id="main-documentation-link" class="link"><a href="/documentation">Documentation</a></li> <li id="main-references-link" class="link"><a href="/our-customers">References</a></li> <li id="main-faq-link" class="link"><a href="/faq">FAQ</a></li> <li id="main-book-a-demo-link" class="link"><a href="/book-a-demo">Book a demo</a></li> <li id="main-contact-us-link" class="link last"><a href="/contact">Contact us</a></li></ul> </nav> </div> <a class='hamburger hamburger--collapse' href='javascript:void(0)'> <span class='hamburger-box'> <span class='hamburger-inner'></span> </span> </a> </div> <div class='search-bar with-topnav'> <form action='/search' data-leads-ignore="true"> <input name='q' placeholder='Search' type='text'> </form> <button class='search-close'> <i class='far fa-times'></i> </button> </div> </header> <div aria-atomic='true' aria-live='polite' class='position-relative' id='toast-live-container'> <div class='toast-container position-fixed top-0 end-0 p-3'> </div> </div> <div id='main'> <section class='heading-section overlay-black overlay-opacity-opaque ' style=''> <div class='overlay'></div> <div class='container'> <div class='row align-items-center'> <div class="col-md-9 col-lg-8 col-xxl-6"> <div class='title-wrapper'> <h1 class='title mb-0'>About us</h1> <ul class='breadcrumb'><li itemscope itemtype="http://data-vocabulary.org/Breadcrumb"><a href='/' itemprop="url"><span itemprop="title">Home</span></a></li><li class='current' itemscope itemtype="http://data-vocabulary.org/Breadcrumb"><a href='/about' itemprop="url"><span itemprop="title">About us</span></a></li></ul> </div> </div> </div> </div> </section> <section class='content-section text-and-image-section theme-auto default-image-size spacing-options-both '> <div class='container'> <div class='row align-items-center'> <div class='text-wrapper col-md-5 wysiwyg order-md-last offset-md-1'> <p><strong><span style="font-size: 24px;"><strong></strong><span style="color: rgb(0, 0, 0);">Shaping tomorrow's quality and process control</span></span></strong></p> <p>At Hammer-IMS, our mission is to provide innovative, high-tech, and reliable quality and process control solutions that help our customers optimize their production processes, improve efficiency, and promote sustainability. We strive to remain at the forefront of the industry by constantly innovating and anticipating our customers' needs.</p> <div class="btns-wrapper d-flex flex-wrap"> </div> </div> <div class='image-wrapper col-md-6 wysiwyg text-center mt-md-0 mt-5 order-md-first'> <img alt='' src='https://cdn.nimbu.io/s/4j0nxju/pages/hs0pbal/1720441834227/team.jpg?ba62c12'> </div> </div> </div> </section> <section class='content-section text-and-image-section theme-gray default-image-size spacing-options-both '> <div class='container'> <div class='row align-items-center'> <div class='text-wrapper col-md-5 wysiwyg '> <p><strong><span style="font-size: 24px;"><span style="color: rgb(0, 0, 0);">
133Revolutionizing the industry with precision and efficiency</span></span></strong></p> <p>Our vision is to lead in a world where advanced quality and process control technologies are seamlessly integrated into industrial processes, maximising production efficiency and quality. We aim to lead the industry into a future where accurate, non-destructive measurement methods are the norm, enabling companies to increase productivity and reduce waste.</p> <div class="btns-wrapper d-flex flex-wrap"> </div> </div> <div class='image-wrapper col-md-6 wysiwyg text-center mt-md-0 mt-5 offset-md-1'> <img alt='' src='https://cdn.nimbu.io/s/4j0nxju/pages/1pfm0mi/172044137235/spartanburg.jpg?x4b9ya8'> </div> </div> </div> </section> <section class='content-section text-section theme-auto spacing-options-both'> <div class='container'> <div class='row justify-content-center'> <div class='text-wrapper col-md-8 wysiwyg text-columns-1'> <h3 style="text-align: center;"><strong><strong><span style="font-size: 30px;">Our guiding values</span></strong></strong></h3> <p style="text-align: center;">Our values shape everything we do. They inspire our team, drive our actions, and ensure we deliver the best results.<br> </p> </div> </div> </div> </section> <section class='content-section cards-section theme-auto spacing-options-bottom'> <div class='container cards-container'> <div class='row'> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720431809897/artboard-22.png" data-image="tadbi4sg1ivw" width="104" height="104" style="width: 104px; max-width: 104px; height: 104px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Commitment to sustainability </strong></strong></span></p> <p>We are dedicated to sustainability in all its formsâeconomic, ecological, and social. Our machines do not contain nuclear components, prioritizing environmentally friendly and safe technologies.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720431998791/artboard-22_1.png" data-image="uongku8vv4ir" width="106" height="106" style="width: 106px; max-width: 106px; height: 106px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Focus on quality </strong></strong></span></p> <p>Quality is at the heart of everything we do. We ensure that our products and services meet the highest standards and consistently exceed customer expectations.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432099896/artboard-22_2.png" data-image="zb1b6i77fqvb" width="107" height="107" style="width: 107px; max-width: 107px; height: 107px;"></figure> <p><span style="font-size: 24px;"><strong><span style="font-size: 18px;"></span><br></strong></span></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Passion for innovation </strong></strong></span></p> <p>We embrace continuous innovation and strive to develop groundbreaking solutions that set new standards in industrial measurement te
133chnologies. We stay ahead of industry trends and anticipate our customers' evolving needs.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432138504/artboard-22_3.png" data-image="sxwuxs3rnhhf" width="105" height="105" style="width: 105px; max-width: 105px; height: 105px;"></figure> <p><br></p> <p><span style="font-size: 28px;"><strong><strong><span style="color: rgb(0, 0, 0);">Customer-centric solutions </span></strong></strong></span></p> <p>We listen to our customers' needs and are committed to delivering tailored solutions that meet their specific requirements and expectations. Our customer-centric approach drives us to provide exceptional service and support.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432201812/artboard-22_4.png" data-image="1dzn1u69eyjq" width="103" height="103" style="width: 103px; max-width: 103px; height: 103px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Stronger together </strong></strong></span></p> <p>We believe that success is achieved through collaboration. We value teamwork and foster a cooperative environment, both internally and with our external partners. Collective effort and shared success are key to our approach.</p></div> </div> </div> </div> </div> </div> </section> </div> <footer class="footer-v2"> <div class='container'> <div class='footer-content'> <div class="row justify-content-between footer-top-row"> <div class="col-md-6 col-lg-4 text-center text-md-start"> <a class='logo-footer-link' href='/'> <img alt='logo in footer' class='logo-footer' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/logo_white_v2.png?8xqb1ia'> </a> </div> <div class="col-md-6 d-flex justify-content-center justify-content-md-end mt-4 mt-md-0"> <div class="btns-wrapper d-flex"> <a href="/book-appointment" class="btn btn-outline-white first"> Book a demo </a> <a href="/contact" class="btn btn-primary last"> Contact us </a> </div> </div> </div> <div class='row footer-bottom-row'> <div class='col-md-3 col-lg-3'> <div class="tagline"> Hammer-IMS is your one-stop shop for advanced quality and process control solutions. We specialize in measurement systems for thickness and basis weight, as well as surface inspection systems for anomaly detection. Our cutting-edge M-Ray technology ensures non-nuclear, radiation-free solutions, delivering precision and safety in every application. </div> </div> <div class='col-6 col-md-2 col-lg-2'> <ul class='footernav mt-4 mt-md-0'> <li id="footer-measuring-technologies-link" class="link first"><a href="/measuring-technologies">Measuring technologies</a></li> <li id="footer-camera-technologies-link" class="link"><a href="/camera-technologies">Camera technologies</a></li> <li id="footer-industries-link" class="link"><a href="/industries">Industries</a></li> <li id="footer-products-link" class="link"><a href="/products">Products</a></li> <li id="footer-documentation-link" class="link"><a href="/documentation">Documentation</a></li> <li id="footer-references-link" class="link"><a href="/our-customers">References</a></li> <li id="footer-faq-link" class="link last"><a href="/faq">FAQ</a></li></ul> </div> <div class='col-6 col-md-2 col-lg-2'> <ul class='footernav mt-4 mt-md-0'>
133 <li id="footer-about-us-link" class="link current first"><a href="/about">About us</a></li> <li id="footer-news-link" class="link"><a href="/news">News</a></li> <li id="footer-careers-link" class="link"><a href="/careers">Careers</a></li> <li id="footer-locations-link" class="link"><a href="/locations">Locations</a></li> <li id="footer-exhibitions-link" class="link"><a href="/exhibitions">Exhibitions</a></li> <li id="footer-partners-link" class="link"><a href="/measuring-technologies/suppliers">Partners</a></li> <li id="footer-feeds-link" class="link last"><a href="https://feeds.hammer-ims.com/">Feeds</a></li></ul> <div class='social-medias-footer mt-3 mt-md-0'> <div class='social-media nav-link'> <a href='https://www.linkedin.com/company/hammer-ims/' target='_blank'> <i class='fab fa-linkedin'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.facebook.com/hammerims' target='_blank'> <i class='fab fa-facebook-f'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.instagram.com/hammer_ims/' target='_blank'> <i class='fab fa-instagram'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.youtube.com/@Hammer-IMS/videos' target='_blank'> <i class='fab fa-youtube'></i> </a> </div> </div> </div> <div class="col-md-5 col-lg-5"> <div class="row"> <div class="col-12"> <div class='footer-company mt-3 mt-md-0'> <div class="footer-subtitle">Contact us</div> </div> </div> <div class="col-12 col-lg-6"> <div class='footer-company'> <p><strong>Belgium</strong><br>Industrieweg 1401 <br>3540 Herk-de-Stad <br><a href="mailto:[email protected]">[email protected]</a><br>+32 11 36 55 01<br>VAT (BE) 0648.896.643 RPR<br>Antwerp, Hasselt branch</p> </div> </div> <div class="col-12 col-lg-6"> <div class='footer-company'> <p><strong>United States</strong> <br>7092 Howard St Unit J <br> Spartanburg, SC 29303<br><a href="mailto:[email protected]">[email protected]</a> <br>+1 864 310 6050</p> </div> </div> </div> </div> </div> </div> <div class='footer-credentials '> <div class="row d-flex justify-content-between"> <div class="col-md-3 text-center text-md-start mb-2 mb-md-0"> © 2026 Hammer-IMS </div> <div class="col-md-6 text-center d-flex justify-content-center mb-2 mb-md-0 flex-wrap"> <a href='/privacy'>Privacy</a> <a href='/terms-conditions'>Terms & Conditions</a> <a href='/cookie-policy' target='_blank'>Cookie Policy</a> <a href='http://www.nimbu.io' target='_blank'>Powered by Nimbu</a> </div> <div class="col-md-3 text-center text-md-end"> All rights reserved </div> </div> </div> </div> </footer> </div>
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/polyfills.js?8xqb1ia" type="text/javascript"></script>
133
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.swiper.js?8xqb1ia" type="text/javascript"></script>
133
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.core.js?8xqb1ia" type="text/javascript"></script>
133
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.js?8xqb1ia" type="text/javascript"></script>
133
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/app_v2.js?8xqb1ia" type="text/javascript"></script>
133
133<script data-environment="prod"> 134/** 135* @module LeadsScript 136*/ 137 138(function () { 139 "use strict"; 140 // ============================================================================ 141 // ATTRIBUTION TRACKER CLASS 142 // ============================================================================ 143 144 /** 145 * @class AttributionTracker 146 */ 147 class AttributionTracker { 148 /** 149 * @param {Object} options - Configuration options 150 * @param {number} options.trackingDays - Days to keep attribution data (default: 30) 151 * @param {string} options.sessionKey - sessionStorage key (default: "leads_attribution_session") 152 * @param {string} options.dataKey - localStorage key (default: "leads_attribution_data") 153 */ 154 constructor(options = {}) { 155 this.sessionKey = options.sessionKey || "leads_attribution_session"; 156 this.dataKey = options.dataKey || "leads_attribution_data"; 157 this.trackingDays = options.trackingDays || 30; 158 159 this.init(); 160 } 161 162 /** 163 * @private 164 */ 165 init() { 166 // Generate session ID if not exists 167 try { 168 if (!sessionStorage.getItem(this.sessionKey)) { 169 sessionStorage.setItem(this.sessionKey, this.generateSessionId()); 170 } 171 } catch (e) { 172 console.warn( 173 "[Leads Script] Session storage unavailable, attribution session disabled:", 174 e 175 ); 176 } 177 178 // Clean old data 179 this.cleanOldData(); 180 181 // Track this page visit 182 this.trackPageVisit(); 183 } 184 185 /** 186 * @returns {string} Unique session identifier 187 * @private 188 */ 189 generateSessionId() { 190 return ( 191 "sess_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9) 192 ); 193 } 194 195 /** 196 * @private 197 */ 198 trackPageVisit() { 199 const currentUrl = window.location.href; 200 const currentPage = window.location.pathname; 201 const referrer = document.referrer; 202 const urlParams = new URLSearchParams(window.location.search); 203 204 // Get existing attribution data 205 let attributionData = this.getAttributionData(); 206 207 // If this is the first visit (no landing page recorded) or data is expired 208 if ( 209 !attributionData.landing_page || 210 this.isDataExpired(attributionData) 211 ) { 212 attributionData = this.resetAttributionData(); 213 attributionData.landing_page = currentUrl; 214 attributionData.landing_timestamp = new Date().toISOString(); 215 216 // Determine lead source for new session 217 attributionData = this.determineLeadSource( 218 attributionData, 219 referrer, 220 urlParams 221 ); 222 } 223 224 // Always update current page info 225 attributionData.current_page = currentPage; 226 attributionData.current_url = currentUrl; 227 attributionData.last_visit = new Date().toISOString(); 228 229 // Track page views 230 attributionData.page_views = (attributionData.page_views || 0) + 1; 231 232 // Add current page to journey 233 if (!attributionData.page_journey) { 234 attributionData.page_journey = []; 235 } 236 attributionData.page_journey.push({ 237 page: currentPage, 238 timestamp: new Date().toISOString(), 239 title: document.title 240 }); 241 242 // Keep only last 20 pages to avoid storage bloat 243 if (attributionData.page_journey.length > 20) { 244 attributionData.page_journey = attributionData.page_journey.slice(-20); 245 } 246 247 // Store updated data 248 this.saveAttributionData(attributionData); 249 } 250 251 /** 252 * @param {Object} data - Attribution data object to update 253 * @param {string} referrer - Document referrer URL 254 * @param {URLSearchParams} urlParams - Current page URL parameters 255 * @returns {Object} Updated attribution data 256 * @private 257 */ 258 determineLeadSource(data, referrer, urlParams) { 259 // Check UTM parameters first (highest priority) 260 if (urlParams.get("utm_source")) { 261 data.utm_source = urlParams.get("utm_source") || ""; 262 data.utm_medium = urlParams.get("utm_medium") || ""; 263 data.utm_campaign = urlParams.get("utm_campaign") || ""; 264 data.utm_term = urlParams.get("utm_term") || ""; 265 data.utm_content = urlParams.get("utm_content") || ""; 266 267 // Determine lead source category based on UTM medium 268 const medium = data.utm_medium.toLowerCase(); 269 if (medium === "cpc" || medium === "paid" || medium === "ppc") { 270 data.lead_source = "Paid"; 271 } else if (medium === "social") { 272 data.lead_source = "Social"; 273 } else if (medium === "email") { 274 data.lead_source = "Email"; 275 } else if (medium === "referral") { 276 data.lead_source = "Referral"; 277 } else { 278 data.lead_source = "Campaign"; 279 } 280 281 // Use UTM term as search term if available 282 if (data.utm_term) { 283 data.search_term = data.utm_term; 284 } 285 } else if (referrer && referrer !== window.location.href) { 286 // Check referrer if no UTM parameters 287 data.referrer_url = referrer; 288 289 try { 290 const referrerUrl = new URL(referrer); 291 data.referrer_domain = referrerUrl.hostname; 292 293 const currentHost = window.location.hostname.toLowerCase(); 294 const refHost = referrerUrl.hostname.toLowerCase(); 295 296 // Treat same-domain traffic as Direct rather than Referral 297 if (currentHost === refHost) { 298 data.lead_source = "Direct"; 299 return data; 300 } 301 302 // Check if it's from a search engine 303 if (this.isSearchEngine(referrer)) { 304 data.lead_source = "Organic"; 305 data.search_term = this.extractSearchTerm(referrer);
306 data.search_engine = this.getSearchEngine(referrer); 307 } else if (this.isSocialMedia(referrer)) { 308 data.lead_source = "Social"; 309 data.social_network = this.getSocialNetwork(referrer); 310 } else { 311 // External referral 312 data.lead_source = "Referral"; 313 } 314 } catch (error) { 315 console.warn("Error parsing referrer URL:", error); 316 data.lead_source = "Referral"; 317 } 318 } else { 319 // Direct traffic (no referrer or same-site referrer) 320 data.lead_source = "Direct"; 321 } 322 323 return data; 324 } 325 326 /** 327 * @param {string} url - URL to check 328 * @returns {boolean} True if URL is from a search engine 329 * @private 330 */ 331 isSearchEngine(url) { 332 const searchEngines = [ 333 "google.com", 334 "google.co.uk", 335 "google.ca", 336 "google.com.au", 337 "bing.com", 338 "yahoo.com", 339 "duckduckgo.com", 340 "baidu.com", 341 "yandex.com", 342 "yandex.ru", 343 "ask.com", 344 "aol.com", 345 "ecosia.org", 346 "startpage.com", 347 "searx.org" 348 ]; 349 350 try { 351 const domain = new URL(url).hostname.toLowerCase(); 352 return searchEngines.some((engine) => domain.includes(engine)); 353 } catch (error) { 354 console.warn("Error checking if URL is from search engine:", error); 355 return false; 356 } 357 } 358 359 /** 360 * @param {string} referrer - Referrer URL 361 * @returns {string} Search term or "(not provided)" 362 * @private 363 */ 364 extractSearchTerm(referrer) { 365 try { 366 const url = new URL(referrer); 367 const params = new URLSearchParams(url.search); 368 369 // Common search parameter names 370 const searchParams = ["q", "query", "search", "p", "wd", "text"]; 371 372 for (const param of searchParams) { 373 const term = params.get(param); 374 if (term) { 375 return decodeURIComponent(term).trim(); 376 } 377 } 378 379 return "(not provided)"; 380 } catch (error) { 381 console.warn("Error extracting search term from referrer:", error); 382 return "(not provided)"; 383 } 384 } 385 386 /** 387 * @param {string} referrer - Referrer URL 388 * @returns {string} Search engine name or "Other"/"Unknown" 389 * @private 390 */ 391 getSearchEngine(referrer) { 392 try { 393 const domain = new URL(referrer).hostname.toLowerCase(); 394 if (domain.includes("google")) return "Google"; 395 if (domain.includes("bing")) return "Bing"; 396 if (domain.includes("yahoo")) return "Yahoo"; 397 if (domain.includes("duckduckgo")) return "DuckDuckGo"; 398 if (domain.includes("baidu")) return "Baidu"; 399 if (domain.includes("yandex")) return "Yandex"; 400 return "Other"; 401 } catch (error) { 402 console.warn("Error getting search engine from referrer:", error); 403 return "Unknown"; 404 } 405 } 406 407 /** 408 * @param {string} url - URL to check 409 * @returns {boolean} True if URL is from social media 410 * @private 411 */ 412 isSocialMedia(url) { 413 const socialPlatforms = [ 414 "facebook.com", 415 "twitter.com", 416 "x.com", 417 "linkedin.com", 418 "instagram.com", 419 "youtube.com", 420 "tiktok.com", 421 "pinterest.com", 422 "reddit.com", 423 "snapchat.com", 424 "whatsapp.com", 425 "telegram.org" 426 ]; 427 428 try { 429 const domain = new URL(url).hostname.toLowerCase(); 430 return socialPlatforms.some((platform) => domain.includes(platform)); 431 } catch (error) { 432 console.warn("Error checking if URL is from social media:", error); 433 return false; 434 } 435 } 436 437 /** 438 * @param {string} referrer - Referrer URL 439 * @returns {string} Social network name or "Other Social"/"Unknown" 440 * @private 441 */ 442 getSocialNetwork(referrer) { 443 try { 444 const domain = new URL(referrer).hostname.toLowerCase(); 445 if (domain.includes("facebook")) return "Facebook"; 446 if (domain.includes("twitter") || domain.includes("x.com")) 447 return "Twitter/X"; 448 if (domain.includes("linkedin")) return "LinkedIn"; 449 if (domain.includes("instagram")) return "Instagram"; 450 if (domain.includes("youtube")) return "YouTube"; 451 if (domain.includes("tiktok")) return "TikTok"; 452 if (domain.includes("pinterest")) return "Pinterest"; 453 if (domain.includes("reddit")) return "Reddit"; 454 return "Other Social"; 455 } catch (error) { 456 console.warn("Error getting social network from referrer:", error); 457 return "Unknown"; 458 } 459 } 460 461 /** 462 * @returns {Object} Attribution data object 463 * @private 464 */ 465 getAttributionData() { 466 try { 467 const stored = localStorage.getItem(this.dataKey); 468 if (stored) { 469 return JSON.parse(stored); 470 } 471 } catch (error) { 472 console.warn("Error parsing attribution data:", error); 473 } 474 475 return this.resetAttributionData(); 476 } 477 478 /** 479 * @param {Object} data - Attribution data to save 480 * @private 481 */ 482 saveAttributionData(data) { 483 try { 484 localStorage.setItem(this.dataKey, JSON.stringify(data)); 485 } catch (error) { 486 console.warn("Error saving attribution data:", error); 487 } 488 } 489 490 /** 491 * @returns {Object} Fresh attribution data object 492 * @private 493 */ 494 resetAttributionData() { 495 return { 496 lead_source: "Direct", 497 landing_page: "", 498 current_page: "", 499 search_term: "", 500 search_engine: "", 501 referrer_url: "", 502 referrer_domain: "", 503 social_network: "", 504 utm_source: "", 505 utm_medium: "", 506 utm_campaign: "", 507 utm_term: "",
508 utm_content: "", 509 landing_timestamp: "", 510 last_visit: "", 511 page_views: 0, 512 page_journey: [] 513 }; 514 } 515 516 /** 517 * @param {Object} data - Attribution data to check 518 * @returns {boolean} True if data is expired 519 * @private 520 */ 521 isDataExpired(data) { 522 if (!data.landing_timestamp) return true; 523 524 const landingDate = new Date(data.landing_timestamp); 525 const now = new Date(); 526 const daysDiff = (now - landingDate) / (1000 * 60 * 60 * 24); 527 528 return daysDiff > this.trackingDays; 529 } 530 531 /** 532 * @private 533 */ 534 cleanOldData() { 535 const data = this.getAttributionData(); 536 if (this.isDataExpired(data)) { 537 try { 538 localStorage.removeItem(this.dataKey); 539 } catch (e) { 540 console.warn("Error cleaning old attribution data:", e); 541 // Ignore storage failures; tracking degrades gracefully 542 } 543 } 544 } 545 546 /** 547 * @returns {string} Session ID or empty string if unavailable 548 * @private 549 */ 550 getSessionId() { 551 try { 552 return sessionStorage.getItem(this.sessionKey) || ""; 553 } catch (e) { 554 console.warn("Error getting session ID:", e); 555 return ""; 556 } 557 } 558 559 /** 560 * @returns {Object} Complete attribution data object 561 * @public 562 */ 563 getAllData() { 564 const data = this.getAttributionData(); 565 const sessionId = this.getSessionId(); 566 567 return { 568 ...data, 569 session_id: sessionId, 570 current_timestamp: new Date().toISOString() 571 }; 572 } 573 } 574 575 // ============================================================================ 576 // INITIALIZATION 577 // ============================================================================ 578 579 /** 580 * @private 581 */ 582 function initializeScript() { 583 if (document.readyState === "loading") { 584 document.addEventListener("DOMContentLoaded", initializeLeadsScript); 585 } else { 586 initializeLeadsScript(); 587 } 588 } 589 590 /** 591 * @private 592 */ 593 function initializeLeadsScript() { 594 const config = loadConfiguration(); 595 if (!config) { 596 return; // Early exit if configuration fails 597 } 598 599 createAttributionTracker(config); 600 // Sets up form handling for both static and dynamic forms 601 setupFormObserver(); 602 603 console.log("Leads script initialized successfully"); 604 } 605 606 /** 607 * @returns {Object|null} Configuration object or null if script tag not found 608 * @private 609 */ 610 function loadConfiguration() { 611 const scriptTag = document.currentScript || document.querySelector('script[data-environment]'); 612 613 if (!scriptTag) { 614 console.error("Leads script: No script tag found"); 615 return null; 616 } 617 618 // Only read data-environment, API path is derived from environment 619 const environment = scriptTag.getAttribute("data-environment") || "dev"; 620 const apiPath = getDefaultApiPath(environment); 621 622 return { 623 apiPath: apiPath, 624 environment: environment 625 }; 626 } 627 628 /** 629 * @param {string} environment - Environment name ('prod' or 'dev') 630 * @returns {string} API endpoint URL (defaults to 'dev' if invalid) 631 * @private 632 */ 633 function getDefaultApiPath(environment) { 634 const endpoints = { 635 prod: "https://api.gushwork.ai/seo-v2/lead", 636 dev: "https://api-dev.gushwork.ai/seo-v2/lead" 637 }; 638 639 return endpoints[environment] || endpoints.dev; 640 } 641 642 /** 643 * @param {Object} config - Configuration object with apiPath and environment 644 * @returns {AttributionTracker} Initialized tracker instance 645 * @private 646 */ 647 function createAttributionTracker(config) { 648 // Store config globally for form submission functions 649 // Form handlers need API path but aren't passed config directly 650 window.__leadsConfig = config; 651 652 // Create tracker instance - constructor calls init() synchronously 653 // This means tracker is fully initialized when this function returns 654 const tracker = new AttributionTracker({ 655 trackingDays: 30, // Attribution data expires after 30 days 656 sessionKey: "leads_attribution_session", // sessionStorage key 657 dataKey: "leads_attribution_data" // localStorage key 658 }); 659 660 // Store tracker globally for access by other parts of the system 661 window.attributionTracker = tracker; 662 return tracker; 663 } 664 665 // Start initialization 666 initializeScript(); 667 668 // ============================================================================ 669 // FORM SUBMISSION UTILITIES 670 // ============================================================================ 671 672 /** 673 * @param {HTMLFormElement} form - Form element to extract data from 674 * @returns {Object} Raw form data object with original field names 675 * @private 676 */ 677 function extractFormData(form) { 678 const data = {}; 679 680 const inputs = form.querySelectorAll("input, select, textarea");
681 inputs.forEach(function (input) { 682 // Skip submit buttons 683 if (input.type !== "submit") { 684 // Use name attribute, fall back to id if name doesn't exist 685 const fieldName = input.name || input.id; 686 687 if (fieldName) { 688 // Trim string values, preserve other types as-is 689 data[fieldName] = 690 typeof input.value === "string" ? input.value.trim() : input.value; 691 } 692 } 693 }); 694 695 return data; 696 } 697 698 /** 699 * @returns {string} Product name or empty string 700 * @private 701 */ 702 function getProductContext() { 703 let productName = ""; 704 705 if (window.location.pathname.includes("/product/")) { 706 productName = document.title.split(" | ")[0] || document.title; 707 } 708 709 return productName; 710 } 711 712 /** 713 * @param {Object} formData - Raw form data object (all fields as-is) 714 * @param {string} requestType - Type of request (e.g., 'contact', 'blog_newsletter') 715 * @param {Object} additionalInfo - Additional form information 716 * @param {string} additionalInfo.productContext - Product context string 717 * @param {string} additionalInfo.formId - Form ID 718 * @param {Object} config - Configuration object 719 * @param {string} config.apiPath - API endpoint URL 720 * @returns {Promise<Object>} Submission result 721 * @private 722 */ 723 async function submitToBackend( 724 formData, 725 requestType, 726 additionalInfo, 727 config 728 ) { 729 additionalInfo = additionalInfo || {}; 730 const productName = additionalInfo.productContext || getProductContext(); 731 732 let form_details = { 733 productName: productName, 734 requestType: requestType, 735 formId: additionalInfo.formId 736 }; 737 738 try { 739 if ( 740 window.attributionTracker && 741 typeof window.attributionTracker.getAllData === "function" 742 ) { 743 const attributionData = window.attributionTracker.getAllData(); 744 form_details = Object.assign({}, form_details, attributionData); 745 } 746 } catch (error) { 747 console.warn("Error getting attribution data for form submission:", error); 748 // No attribution data found, continue with form details 749 } 750 751 const apiPayload = { 752 request_origin: window.location.hostname.toLowerCase(), 753 leads_info: formData, 754 form_details: form_details 755 }; 756 757 try { 758 const response = await fetch(config.apiPath, { 759 method: "POST", 760 headers: { 761 "Content-Type": "application/json" 762 }, 763 body: JSON.stringify(apiPayload) 764 }); 765 766 if (!response.ok) { 767 throw new Error("HTTP error! status: " + response.status); 768 } 769 770 const result = await response.json(); 771 772 return { success: true, data: result }; 773 } catch (error) { 774 console.error("API Error:", error); 775 return { 776 success: false, 777 error: error instanceof Error ? error.message : "Unknown error occurred" 778 }; 779 } 780 } 781 782 /** 783 * @param {Event} event - Form submit event 784 * @param {string} requestType - Type of request (e.g., 'contact', 'blog_newsletter') 785 * @param {Object} options - Options object 786 * @param {Function} options.onSuccess - Callback on successful submission 787 * @param {Function} options.onError - Callback on error 788 * @param {string} options.productContext - Product context string 789 * @param {string} options.formId - Form ID 790 * @returns {Promise<Object>} Submission result 791 * @public 792 */ 793 async function handleFormSubmission(event, requestType, options) { 794 options = options || {}; 795 796 if (event && event.preventDefault) { 797 event.preventDefault(); 798 } 799 800 const form = event ? event.currentTarget || event.target : null; 801 if (!form) { 802 const errorMsg = "Form element not found"; 803 if (options.onError) { 804 options.onError(errorMsg); 805 } 806 return { success: false, error: errorMsg }; 807 } 808 809 const formData = extractFormData(form); 810 811 const additionalInfo = { 812 formId: options.formId || form.getAttribute("data-form-id") || form.id || "", 813 productContext: 814 options.productContext || 815 (form.dataset && form.dataset.productContext) || 816 "" 817 }; 818 819 // Get config from global window object (set during initialization) 820 const config = window.__leadsConfig || {}; 821 if (!config.apiPath) { 822 const errorMsg = 823 "Leads script not correctly configured. Missing apiPath."; 824 console.error(errorMsg); 825 if (options.onError) { 826 options.onError(errorMsg); 827 } 828 return { success: false, error: errorMsg }; 829 } 830 831 const result = await submitToBackend( 832 formData, 833 requestType, 834 additionalInfo, 835 config 836 ); 837 838 if (result.success) { 839 // Mark that a form has been successfully submitted 840 try { 841 localStorage.setItem("formSubmitted", "true"); 842 } catch (e) { 843 // localStorage might not be available, ignore silently 844 console.warn("Could not save form submission state:", e); 845 } 846 if (options.onSuccess) { 847 options.onSuccess(result.data); 848 } 849 } else { 850 if (options.onError) { 851 options.onError(result.error || "Submission failed"); 852 } 853 } 854 855 return result; 856 } 857 858 // ============================================================================ 859 // FORM HANDLING & OBSERVERS 860 // ============================================================================ 861 862 /** 863 * @private 864 */ 865 function setupFormObserver() { 866 // Process a newly added form
867 function processForm(form) { 868 // Attach auto-submission handler to all forms (defaults to "contact" type if not specified) 869 attachAutoSubmissionHandlerToForm(form); 870 } 871 872 // Process all existing forms on initialization 873 const existingForms = document.querySelectorAll("form"); 874 existingForms.forEach(processForm); 875 876 // Watch for dynamically added forms 877 const observer = new MutationObserver(function (mutations) { 878 mutations.forEach(function (mutation) { 879 mutation.addedNodes.forEach(function (node) { 880 if (node.nodeType === Node.ELEMENT_NODE) { 881 if (node.tagName === "FORM") { 882 // Direct form node 883 processForm(node); 884 } else { 885 // Container node - check for nested forms 886 const forms = 887 node.querySelectorAll && node.querySelectorAll("form"); 888 if (forms) { 889 forms.forEach(processForm); 890 } 891 } 892 } 893 }); 894 }); 895 }); 896 897 observer.observe(document.body, { 898 childList: true, 899 subtree: true 900 }); 901 } 902 903 /** 904 * @param {HTMLFormElement} form - Form element to attach handler to 905 * @private 906 */ 907 function attachAutoSubmissionHandlerToForm(form) { 908 // Skip if already attached 909 if (form.dataset.leadsHandlerAttached === "true") { 910 return; 911 } 912 913 // Skip if form has data-leads-ignore attribute 914 if (form.getAttribute("data-leads-ignore") === "true") { 915 return; 916 } 917 918 // Mark as attached 919 form.dataset.leadsHandlerAttached = "true"; 920 921 // Get configuration from form data attributes 922 const requestType = form.getAttribute("data-request-type") || "contact"; 923 const productContext = form.getAttribute("data-product-context") || ""; 924 const formId = form.getAttribute("data-form-id") || form.id || ""; 925 // Check if UI gratification (success/error messages) should be shown 926 const showGratification = form.getAttribute("data-gratification") === "true"; 927 928 // Attach submit handler 929 form.addEventListener("submit", async function (e) { 930 e.preventDefault(); 931 932 const submitButton = form.querySelector( 933 'button[type="submit"], input[type="submit"]' 934 ); 935 let errorContainer = null; 936 let successContainer = null; 937 938 // Store original button text BEFORE changing it 939 const originalButtonText = submitButton 940 ? submitButton.textContent || submitButton.value 941 : ""; 942 943 // Only create UI elements if gratification is enabled 944 if (showGratification) { 945 errorContainer = getOrCreateErrorContainer(form); 946 successContainer = getOrCreateSuccessContainer(form); 947 948 // Hide previous messages 949 hideElement(errorContainer); 950 hideElement(successContainer); 951 952 // Show loading state on button 953 if (submitButton) { 954 submitButton.disabled = true; 955 if (submitButton.tagName === "BUTTON") { 956 submitButton.textContent = "Submitting..."; 957 } else { 958 submitButton.value = "Submitting..."; 959 } 960 } 961 } 962 963 // Submit form 964 try { 965 const result = await handleFormSubmission(e, requestType, { 966 productContext: productContext, 967 formId: formId, 968 onSuccess: function (_data) { 969 // Show success message only if gratification is enabled 970 if (showGratification && successContainer) { 971 showSuccessMessage(form, successContainer); 972 } 973 // Reset form 974 form.reset(); 975 // Close modal if form is inside one 976 closeModalIfPresent(form); 977 }, 978 onError: function (errorMsg) { 979 // Show error message only if gratification is enabled 980 if (showGratification && errorContainer) { 981 showErrorMessage(form, errorContainer, errorMsg); 982 } 983 } 984 }); 985 986 if (!result.success && result.error) { 987 // Show error message only if gratification is enabled 988 if (showGratification && errorContainer) { 989 showErrorMessage(form, errorContainer, result.error); 990 } 991 } 992 } catch (error) { 993 const errorMsg = 994 error instanceof Error ? error.message : "Form submission failed"; 995 // Show error message only if gratification is enabled 996 if (showGratification && errorContainer) { 997 showErrorMessage(form, errorContainer, errorMsg); 998 } 999 } finally { 1000 // Restore button state only if gratification is enabled 1001 if (showGratification && submitButton) { 1002 submitButton.disabled = false;
1003 if (submitButton.tagName === "BUTTON") { 1004 submitButton.textContent = originalButtonText; 1005 } else { 1006 submitButton.value = originalButtonText; 1007 } 1008 } 1009 } 1010 }); 1011 } 1012 1013 // ============================================================================ 1014 // UI HELPER FUNCTIONS 1015 // ============================================================================ 1016 1017 /** 1018 * @param {HTMLFormElement} form - Form element 1019 * @returns {HTMLElement} Error container element 1020 * @private 1021 */ 1022 function getOrCreateErrorContainer(form) { 1023 let container = form.querySelector(".leads-error-message"); 1024 if (!container) { 1025 container = document.createElement("div"); 1026 container.className = "leads-error-message"; 1027 container.style.cssText = 1028 "padding: 12px; background-color: #fee; border: 1px solid #fcc; color: #c33; border-radius: 4px; margin-bottom: 16px; display: none;"; 1029 form.insertBefore(container, form.firstChild); 1030 } 1031 return container; 1032 } 1033 1034 /** 1035 * @param {HTMLFormElement} form - Form element 1036 * @returns {HTMLElement} Success container element 1037 * @private 1038 */ 1039 function getOrCreateSuccessContainer(form) { 1040 let container = form.querySelector(".leads-success-message"); 1041 if (!container) { 1042 container = document.createElement("div"); 1043 container.className = "leads-success-message"; 1044 container.style.cssText = 1045 "padding: 12px; background-color: #efe; border: 1px solid #cfc; color: #3c3; border-radius: 4px; margin-bottom: 16px; display: none;"; 1046 form.insertBefore(container, form.firstChild); 1047 } 1048 return container; 1049 } 1050 1051 /** 1052 * @param {HTMLFormElement} form - Form element 1053 * @param {HTMLElement} container - Error container element 1054 * @param {string} message - Error message to display 1055 * @private 1056 */ 1057 function showErrorMessage(form, container, message) { 1058 container.textContent = message; 1059 showElement(container); 1060 // Auto-hide after 5 seconds 1061 setTimeout(function () { 1062 hideElement(container); 1063 }, 5000); 1064 } 1065 1066 /** 1067 * @param {HTMLFormElement} form - Form element 1068 * @param {HTMLElement} container - Success container element 1069 * @private 1070 */ 1071 function showSuccessMessage(form, container) { 1072 container.textContent = "Thank you! We'll be in touch soon."; 1073 showElement(container); 1074 // Auto-hide after 5 seconds 1075 setTimeout(function () { 1076 hideElement(container); 1077 }, 5000); 1078 } 1079 1080 /** 1081 * @param {HTMLElement} element - Element to show 1082 * @private 1083 */ 1084 function showElement(element) { 1085 element.style.display = "block"; 1086 } 1087 1088 /** 1089 * @param {HTMLElement} element - Element to hide 1090 * @private 1091 */ 1092 function hideElement(element) { 1093 element.style.display = "none"; 1094 } 1095 1096 /** 1097 * @param {HTMLFormElement} form - Form element that was submitted 1098 * @private 1099 */ 1100 function closeModalIfPresent(form) { 1101 // Check if form is inside a bifrost modal overlay 1102 const bifrostModal = form.closest(".modal-overlay, .modal-overlay2"); 1103 if (bifrostModal) { 1104 // Close bifrost modal 1105 bifrostModal.classList.remove("active"); 1106 bifrostModal.style.display = "none"; 1107 document.body.style.overflow = ""; 1108 form.reset(); 1109 return; 1110 } 1111 1112 // Check if form is inside a React/Radix Dialog modal 1113 // Try multiple selectors for Radix Dialog 1114 const reactModal = form.closest( 1115 '[role="dialog"], [data-slot="dialog-content"], [data-radix-dialog-content]' 1116 ); 1117 if (reactModal) { 1118 // Try to find and trigger the close button (multiple selectors) 1119 const closeButtonSelectors = [ 1120 'button[aria-label*="close" i]', 1121 'button[aria-label*="Close" i]', 1122 "[data-radix-dialog-close]", 1123 '[data-slot="dialog-close"]', 1124 'button[type="button"]' // Last resort - might match wrong button 1125 ]; 1126 1127 let closeButton = null; 1128 for (const selector of closeButtonSelectors) { 1129 closeButton = reactModal.querySelector(selector); 1130 if (closeButton) { 1131 break; 1132 } 1133 } 1134 1135 if (closeButton) { 1136 closeButton.click(); 1137 } 1138 } 1139 } 1140 1141 if (typeof module !== "undefined" && module.exports) { 1142 module.exports = { 1143 AttributionTracker: AttributionTracker, 1144 handleFormSubmission: handleFormSubmission 1145 }; 1146 } 1147})(); 1148</script>
vendor: 68 bytes, line 1148
1148 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
1148GTM-53XGBWK
vendor: 83 bytes, line 1148
1148" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
1148</body> </html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.