Help
RSS
API
Feed
Maltego
Contact
Domain > accounts.dependableliftrentals.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-11-24
18.165.83.79
(
ClassC
)
2026-02-12
3.163.24.50
(
ClassC
)
Port 80
HTTP/1.1 301 Moved PermanentlyServer: CloudFrontDate: Thu, 12 Feb 2026 13:35:11 GMTContent-Type: text/htmlContent-Length: 167Connection: keep-aliveLocation: https://accounts.dependableliftrentals.com/X-Cache: Redirect from cloudfrontVia: 1.1 f94332f6513a4c7b3f2217e6b2ddc08c.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: ZBd_-4JBjSReyM-rV_xlfL1ZGN6Bh_NobTgcoMLJFFckUNwskAZZ-w html>head>title>301 Moved Permanently/title>/head>body>center>h1>301 Moved Permanently/h1>/center>hr>center>CloudFront/center>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/htmlContent-Length: 197138Connection: keep-aliveDate: Thu, 12 Feb 2026 13:35:12 GMTLast-Modified: Mon, 08 Dec 2025 09:39:01 GMTETag: 762e72b1929b1a7cb8e3efb48ea300e8Server: AmazonS3X-Cache: Miss from cloudfrontVia: 1.1 2f64e28be83a9bbc21e5afb1a93fec2c.cloudfront.net (CloudFront)X-Amz-Cf-Pop: HIO52-P2X-Amz-Cf-Id: pB7F0uAGZuUBHNczGzxsofPS9nmesWjPWprmRa5wouyLuLqga6Pujg !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0> title>New Account Setup - Dependable Lift Rentals/title> link hrefhttps://fonts.googleapis.com/css2?familyPoppins:wght@300;400;500;600;700&displayswap relstylesheet> style> :root { --primary-color: #2B4C6F; --secondary-color: #F5C842; --text-main: #2B2B2B; --text-secondary: #666666; --border-color: #E5E5E5; --success-color: #28a745; --error-color: #dc3545; --background-color: #FFFFFF; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: Poppins, sans-serif; background-color: var(--background-color); color: var(--text-main); line-height: 1.6; } /* Header Styles */ .header-top { background-color: var(--primary-color); color: white; padding: 12px 0; } .header-top .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; display: flex; justify-content: space-between; align-items: center; } .phone-section { display: flex; align-items: center; gap: 8px; font-weight: 500; } .tagline { font-weight: 400; display: none; } @media (min-width: 640px) { .tagline { display: block; } } .navbar { background-color: white; padding: 15px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .navbar .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; } .logo { height: 60px; width: auto; } .nav-title { color: var(--primary-color); font-size: 24px; font-weight: 600; } /* Main Content */ .main-content { max-width: 1000px; margin: 0 auto; padding: 40px 20px; } .upload-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin: 20px 0; } .upload-section { border: 2px dashed #ccc; padding: 20px; text-align: center; border-radius: 8px; } .upload-section.dragover { border-color: #007bff; background-color: #f8f9fa; } .upload-section.uploaded { border-color: #28a745; background-color: #d4edda; } .preview { max-width: 200px; max-height: 150px; margin: 10px; border-radius: 4px; } .result { margin: 20px 0; padding: 15px; border-radius: 5px; } .success { background-color: #d4edda; border: 1px solid #c3e6cb; color: #155724; } .warning { background-color: #fff3cd; border: 1px solid #ffeaa7; color: #856404; } .error { background-color: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; } button { background-color: var(--primary-color); color: white; padding: 12px 24px; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 500; font-family: Poppins, sans-serif; transition: background-color 0.3s ease; } button:hover { background-color: #1e3a52; } button:disabled { background-color: #6c757d; cursor: not-allowed; } .secondary-btn { background-color: var(--secondary-color); color: var(--text-main); } .secondary-btn:hover { background-color: #e6b63a; } /* Form Styles */ input, select, textarea { width: 100%; padding: 12px; border: 2px solid var(--border-color); border-radius: 8px; font-family: Poppins, sans-serif; font-size: 14px; transition: border-color 0.3s ease; } input:focus, select:focus, textarea:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(43, 76, 111, 0.1); } label { display: block; margin-bottom: 8px; font-weight: 500; color: var(--text-main); } /* Upload Section Styles */ .upload-section { border: 2px dashed var(--border-color); padding: 30px; text-align: center; border-radius: 12px; background-color: #fafafa; transition: border-color 0.3s ease; } .upload-section:hover { border-color: var(--primary-color); } .upload-section.uploaded { border-color: var(--success-color); background-color: #f8fff8; } .loading { display: none; text-align: center; margin: 20px 0; } .text-results { background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin: 10px 0; } .text-item { margin: 5px 0; padding: 8px; background-color: white; border-radius: 3px; } .confidence { color: #6c757d; font-size: 0.9em; } .tabs { display: flex; margin: 20px 0; } .tab { padding: 10px 20px; background-color: #f8f9fa; border: 1px solid #ddd; cursor: pointer; } .tab.active { background-color: #007bff; color: white; } .tab-content { display: none; } .tab-content.active { display: block; } /* Hide desktop camera buttons on mobile devices */ @media (max-width: 768px) { .desktop-camera-btn { display: none !important; } } /style>/head>body> !-- Header --> div classheader-top> div classcontainer> div classphone-section> span>📞/span> a hreftel:1-800-531-7600 stylecolor: white; text-decoration: none;>1-800-531-7600/a> /div> div classtagline>LOW RENTAL RATES, FAST DELIVERY!/div> /div> /div> div classnavbar> div classcontainer> a hrefhttps://dependableliftrentals.com/ target_blank> img srcLogo-navbar.svg altDependable Lift Rentals classlogo> /a> div classnav-title>New Account Setup/div> /div> /div> div classmain-content> !-- New Account Checklist --> div idaccount-checklist stylebackground-color: #f8f9fa; border: 2px solid #007bff; border-radius: 10px; padding: 20px; margin: 20px 0;> !-- Always Visible Buttons --> div styledisplay: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 15px;> div idchecklist-item-1 classchecklist-item styledisplay: flex; flex-direction: column; align-items: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #e9ecef; cursor: pointer; min-width: 180px; max-width: 220px; onclickswitchTab(new-account)> h4 stylemargin: 0; color: #333; text-align: center; font-size: 0.95em;>1. New Account Information/h4> /div> div idchecklist-item-2 classchecklist-item styledisplay: flex; flex-direction: column; align-items: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #e9ecef; cursor: pointer; min-width: 180px; max-width: 220px; onclickswitchTab(id-verification)> h4 stylemargin: 0; color: #333; text-align: center; font-size: 0.95em;>2. Identity Verification/h4> /div> div idchecklist-item-3 classchecklist-item styledisplay: flex; flex-direction: column; align-items: center; padding: 15px; background: white; border-radius: 8px; border: 2px solid #e9ecef; cursor: pointer; min-width: 180px; max-width: 220px; onclickswitchTab(rental-agreement)> h4 stylemargin: 0; color: #333; text-align: center; font-size: 0.95em;>3. Rental Agreement/h4> /div> /div> !-- Collapsible Header with Help Button --> div styledisplay: flex; align-items: center; justify-content: center; gap: 15px; padding: 10px; border-top: 1px solid #dee2e6;> button onclickshowHelpModal(); event.stopPropagation(); stylebackground: #2196f3; color: white; border: none; padding: 6px 12px; border-radius: 6px; font-size: 13px; cursor: pointer; font-weight: bold;>📞 Need Help?/button> div onclicktoggleChecklist() stylecursor: pointer; display: flex; align-items: center; gap: 10px;> span stylecolor: #007bff; font-weight: bold;>Setup Progress (Expand)/span> span idchecklist-toggle stylefont-size: 20px; color: #007bff;>▶/span> /div> /div> !-- Collapsible Content --> div idchecklist-content stylemargin-top: 15px; display: none;> p styletext-align: center; margin-bottom: 20px; color: #666;>Complete all steps below to set up your Dependable Lift Rentals account/p> div styletext-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid #dee2e6;> div idoverall-progress stylemargin-bottom: 10px;> strong>Overall Progress: span idprogress-text>0 of 3 completed/span>/strong> /div> div stylebackground-color: #e9ecef; height: 10px; border-radius: 5px; overflow: hidden;> div idprogress-bar stylebackground-color: #28a745; height: 100%; width: 0%; transition: width 0.3s ease;>/div> /div> /div> /div> /div> div classtabs> div classtab active onclickswitchTab(new-account)>New Account Info/div> div classtab onclickswitchTab(id-verification)>ID Verification/div> div classtab onclickswitchTab(rental-agreement)>Rental Agreement/div> /div> !-- ID Verification Tab --> div idid-verification classtab-content> !-- AWS Rekognition Compliance Notice --> div stylebackground-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 8px; margin: 20px 0;> h4 stylecolor: #856404; margin-top: 0;>⚠️ Identity Verification Notice/h4> p stylemargin: 5px 0; font-size: 0.9em;>strong>Biometric Data Processing:/strong> This service uses AWS Rekognition to analyze facial features for identity verification. Your biometric data is processed securely and is not stored permanently./p> p stylemargin: 5px 0; font-size: 0.9em;>strong>Data Usage:/strong> Facial analysis is used solely for verifying your identity against provided identification documents. No facial recognition database is created or maintained./p> p stylemargin: 5px 0; font-size: 0.9em;>strong>Your Rights:/strong> You may request deletion of any processed biometric data. Processing is voluntary and required only for account verification./p> p stylemargin: 5px 0; font-size: 0.9em;>strong>Data Security:/strong> All data is encrypted in transit and at rest. AWS security standards are maintained throughout the verification process./p> div styletext-align: center; margin-top: 15px;> button onclickshowVerificationChecksModal() stylebackground: #17a2b8; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 0.9em;>🔍 What We Check/button> /div> /div> !-- User Information Form --> div stylebackground-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;> h3>📝 User Information/h3> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;> div> label>First Name: input typetext idfirstName placeholderEnter first name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Last Name: input typetext idlastName placeholderEnter last name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Company Name: input typetext idcompanyName placeholderEnter company name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Phone Number: input typetel idphoneNumber placeholderEnter phone number stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Email Address: input typeemail idemailAddress placeholderEnter email address stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> /div> div stylebackground-color: #e7f3ff; border: 1px solid #b3d9ff; padding: 15px; border-radius: 8px; margin: 20px 0;> p stylemargin: 0; font-size: 0.9em; color: #495057;> strong>📸 Photo Upload Instructions:/strong> If you need to re-add or re-upload a photo or document, simply click the button again to load a new photo or document. If a document is rejected, simply click the button for that section to upload a new one. /p> /div> !-- Section 1: ID Photo Uploads --> div stylemargin: 30px 0;> h3 stylecolor: #007bff; border-bottom: 2px solid #007bff; padding-bottom: 10px; margin-bottom: 20px;>📄 Section 1: ID Photo Uploads/h3> div classupload-grid> div classupload-section ididFrontUpload> h3>📄 Front of ID/h3> input typefile ididFrontFile acceptimage/* styledisplay: none;> input typefile ididFrontMobile acceptimage/* captureenvironment styledisplay: none;> div styledisplay: flex; gap: 10px; flex-wrap: wrap; justify-content: center;> button onclickdocument.getElementById(idFrontFile).click() stylepadding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px;>📁 Choose File/button> button onclickgenerateQRCode(idFront) stylepadding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px;>📱 Mobile Camera/button> button onclickopenCamera(idFront) ididFrontDesktopBtn classdesktop-camera-btn stylepadding: 8px 16px; background-color: #6f42c1; color: white; border: none; border-radius: 4px; display: none;>💻 Desktop/Laptop Camera/button> /div> p>Should contain ID photo or Passport ID page/p> button onclickshowPhotoTips(idFront) stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em; margin-top: 5px;>📸 Photo Tips/button> img ididFrontPreview classpreview styledisplay: none;> /div> div classupload-section ididBackUpload> h3>📄 Back of ID span stylecolor: #6c757d; font-size: 0.8em;>(Optional)/span>/h3> input typefile ididBackFile acceptimage/* styledisplay: none;> input typefile ididBackMobile acceptimage/* captureenvironment styledisplay: none;> div styledisplay: flex; gap: 10px; flex-wrap: wrap; justify-content: center;> button onclickdocument.getElementById(idBackFile).click() stylepadding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px;>📁 Choose File/button> button onclickgenerateQRCode(idBack) stylepadding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px;>📱 Mobile Camera/button> button onclickopenCamera(idBack) ididBackDesktopBtn classdesktop-camera-btn stylepadding: 8px 16px; background-color: #6f42c1; color: white; border: none; border-radius: 4px; display: none;>💻 Desktop/Laptop Camera/button> /div> p>Passport - Not neededbr>ID or DL card - back of card (optional)/p> button onclickshowPhotoTips(idBack) stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em; margin-top: 5px;>📸 Photo Tips/button> img ididBackPreview classpreview styledisplay: none;> /div> /div> /div> !-- Section 2: Selfie Uploads --> div stylemargin: 30px 0;> h3 stylecolor: #28a745; border-bottom: 2px solid #28a745; padding-bottom: 10px; margin-bottom: 20px;>🤳 Section 2: Selfie Uploads/h3> div classupload-grid> div classupload-section idselfieUpload> h3>🤳 Selfie/h3> input typefile idselfieFile acceptimage/* styledisplay: none;> input typefile idselfieMobile acceptimage/* captureuser styledisplay: none;> div styledisplay: flex; gap: 10px; flex-wrap: wrap; justify-content: center;> button onclickdocument.getElementById(selfieFile).click() stylepadding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px;>📁 Choose File/button> button onclickgenerateQRCode(selfie) stylepadding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px;>📱 Mobile Camera/button> button onclickopenCamera(selfie) classdesktop-camera-btn stylepadding: 8px 16px; background-color: #6f42c1; color: white; border: none; border-radius: 4px;>💻 Desktop/Laptop Camera/button> /div> p>For face comparison/p> button onclickshowPhotoTips(selfie) stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em; margin-top: 5px;>📸 Photo Tips/button> img idselfiePreview classpreview styledisplay: none;> /div> div classupload-section idlivenessUpload> h3>🎥 Liveness Detection span stylecolor: #dc3545; font-size: 0.8em;>(Required)/span>/h3> div styledisplay: flex; gap: 10px; flex-wrap: wrap; justify-content: center;> button onclickstartLivenessDetection() stylepadding: 8px 16px; background-color: #e74c3c; color: white; border: none; border-radius: 4px;>Laptop/Desktop Camera/button> button onclickgenerateQRCode(liveness) stylepadding: 8px 16px; background-color: #28a745; color: white; border: none; border-radius: 4px;>Mobile Camera (QR)/button> button onclickshowLivenessTips() stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em;>How it Works/button> /div> p>Required Video Verificationbr>Choose your preferred camera option/p> input typefile idlivenessMobile acceptvideo/* capturecamcorder styledisplay: none;> div idlivenessResult styledisplay: none; margin-top: 10px;>/div> video idlivenessVideo styledisplay: none; width: 100%; max-width: 300px; margin-top: 10px; autoplay muted>/video> canvas idlivenessCanvas styledisplay: none;>/canvas> /div> /div> /div> !-- Hidden threshold input (not visible to clients) --> input typehidden ididThreshold value80> !-- Requirements Summary --> div stylebackground-color: #e7f3ff; border: 1px solid #b3d9ff; padding: 15px; border-radius: 8px; margin: 20px 0;> h4 stylecolor: #007bff; margin-top: 0;>📋 ID Verification Requirements/h4> div styledisplay: grid; grid-template-columns: 1fr 1fr; gap: 20px;> div> h5 stylecolor: #28a745; margin: 0 0 10px 0;>✅ Required/h5> ul stylemargin: 0; padding-left: 20px;> li>Front of ID (photo page)/li> li>Selfie photo/li> li>Liveness Detection/li> li>Biometric consent/li> /ul> /div> div> h5 stylecolor: #6c757d; margin: 0 0 10px 0;>⚪ Optional/h5> ul stylemargin: 0; padding-left: 20px;> li>Back of ID/li> /ul> /div> /div> /div> !-- Biometric Consent --> div stylebackground-color: #e7f3ff; border: 1px solid #b3d9ff; padding: 15px; border-radius: 8px; margin: 20px 0;> div stylefont-weight: bold; text-align: center;> div stylemargin-bottom: 15px;>I consent to the processing of my biometric data (facial features) using AWS Rekognition for identity verification purposes. I understand this data will not be stored permanently and is used solely for account verification./div> button idbiometricConsentBtn onclicktoggleBiometricConsent() stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 120px;> Click to AGREE /button> input typehidden idbiometricConsent valuefalse> /div> /div> div styletext-align: center; margin: 20px 0;> button idverifyBtn onclickverifyID() disabled stylebackground-color: #28a745; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;>🔍 Verify ID/button> div classloading ididLoading>🔄 Processing ID verification.../div> /div> div ididResult>/div> /div> !-- New Account Info Tab --> div idnew-account classtab-content active> div stylebackground-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;> h3>🏢 New Account Information/h3> !-- Customer Information --> div stylemargin-bottom: 20px;> h4>Customer Information/h4> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px;> div> label>Company Name: input typetext idcustomerName placeholderEnter company name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; onblurcheckTestData() oninputcheckTestData()>/label> /div> div> label>Doing Business As (Optional): input typetext iddoingBusinessAs placeholderEnter DBA name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> /div> !-- Address Information --> div stylemargin-bottom: 20px;> h4>Address Information/h4> div styledisplay: grid; grid-template-columns: 1fr; gap: 15px; margin-bottom: 15px;> div> label>Street Address: input typetext idstreetAddress placeholderEnter street address stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> div styledisplay: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px;> div> label>City: input typetext idcity placeholderEnter city stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>State: input typetext idstate placeholderEnter state stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Zip Code & 4: input typetext idzipCode placeholder12345-6789 stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> /div> !-- Accounts Payable Information --> div stylemargin-bottom: 20px;> h4>Accounts Payable Information/h4> p stylefont-size: 0.9em; color: #666; margin-bottom: 15px;>Please add the responsible person details for Accounts Payable/p> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px;> div> label>Accounts Payable Contact: input typetext idprimaryContact placeholderEnter primary contact name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Accounts Payable Email: input typeemail idapEmail placeholderEnter AP email address stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Telephone (Include Area Code): input typetel idaccountTelephone placeholder(555) 123-4567 stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> /div> !-- Additional Contacts --> div stylemargin-bottom: 20px;> div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;> h4 stylemargin: 0;>Additional Contacts/h4> button onclickaddMoreContacts() stylebackground: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 0.9em;>+ Add More Contacts/button> /div> div idadditionalContactsContainer>/div> /div> !-- Rental Protection Program --> div stylemargin-bottom: 20px;> h4>Rentals Protection Program Declaration/h4> p stylefont-size: 0.9em; color: #666; margin-bottom: 15px;>(Certificate of Insurance with Dependable Lift Rentals, Inc named is REQUIRED if Declined)/p> div styletext-align: center; margin-bottom: 15px;> button idacceptBtn onclicksetRentalProtection(YES) stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 140px; margin-right: 10px;> Click to Accept /button> button iddeclineBtn onclicksetRentalProtection(NO) stylebackground-color: #dc3545; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 140px;> Click to Decline /button> input typehidden namerentalProtection idrentalProtection value> /div> /div> !-- Signature Section --> div stylemargin-bottom: 20px;> h4>Signature Section/h4> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;> div> label>Name Printed: input typetext idnamePrinted placeholderEnter printed name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Date: input typedate idsignatureDate stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> !-- Signature Canvas --> div stylemargin-top: 15px;> label styledisplay: block; margin-bottom: 10px; font-weight: bold;>Digital Signature:/label> div styleborder: 2px solid #ddd; border-radius: 4px; padding: 10px; background-color: white;> canvas idsignatureCanvas width400 height150 styleborder: 1px solid #ccc; cursor: crosshair; display: block; margin: 0 auto;>/canvas> div styletext-align: center; margin-top: 10px;> button typebutton onclickclearSignature() stylebackground-color: #dc3545; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px;>Clear Signature/button> span stylefont-size: 0.9em; color: #666;>Sign above using your mouse or finger/span> /div> /div> /div> /div> !-- Notes Section --> div stylemargin-bottom: 20px;> h4>Notes/h4> textarea idaccountNotes placeholderEnter any additional notes... stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; min-height: 100px; resize: vertical;>/textarea> /div> !-- Account Agreement and Terms --> div stylemargin-bottom: 20px; border: 2px solid #007bff; padding: 20px; border-radius: 8px; background-color: #f8f9fa;> h3 stylecolor: #007bff; margin-top: 0;>Account Agreement and Terms/h3> div stylefont-size: 0.9em; line-height: 1.4; max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; background-color: white; border-radius: 4px;> p>The submitting this application (the Undersigned) acknowledges and agrees to the following:/p> p>strong>1./strong> This Account Agreement and Terms (Agreement) supersedes, with respect to rentals made subsequent to the acceptance of this application by Dependable Lift Rentals, Inc. (Dependable), any prior Account Agreement and Terms governing the extension of credit by Dependable to the Undersigned. Rentals by Dependable to the Undersigned made subsequent to the acceptance of this application by Dependable shall be governed by the Rental Contract pertaining to such rental and by this Agreement. In the event that any provisions of the Rental Contract shall conflict with any provision of this Agreement; the Rental Contract shall control./p> p>strong>2./strong> The receipt of an application shall not create any obligation on the part of Dependable to rent or sell equipment to the undersigned or to extend credit to the undersigned in connection with any such rental or sale./p> p>strong>3./strong> ALL RENTALS REQUIRE CREDIT CARD AUTHORIZATION AT THE TIME OF RENTAL./p> p>strong>4./strong> This form does not validate any tax. All tax exemption documentation must be submitted to Dependable rentals for validation prior to any sale or rental./p> p>strong>5./strong> Any and all obligations arising under a Rental Contract, including without limitation, the obligation to pay for all Charges associated with or arising from a rental transaction shall be governed by the terms of the Rental Contract and the Choice of Law provision set forth therein. To the extent any rights or obligations hereunder are not addressed by the Rental Contract, this Agreement shall be governed and construed by the laws of the STATE of WYOMING, without regard to any conflict or choice of laws principals. if any provision or pan of any provision of this Agreement conflicts with any applicable law then that provision will be deemed to be modified to be consistent with such law, or to be deleted if modification is impossible, and will not affect the remainder of this Agreement, which will continue in full force and effect. If any provision of this Agreement is held to be so broad as to be unenforceable, then that provision will be interpreted to be only as broad as is necessary for it to be enforceable./p> p>strong>6./strong> The Undersigned recognizes that it may from time to time be owed money by Dependable due to contracts or transactions between the Undersigned and Dependable which are separate and distinct for the rentals contemplated by this Credit Application and Agreement. Dependable shall have the right to withhold from the Undersigned any monies owed by Dependable to the Undersigned in connection with any such other contracts or transactions and to offset the same against any sums owed by the Undersigned to Dependable in such amounts as may be deemed by Dependable to be reasonably necessary to cover such indebtedness of the Undersigned. So long as this right of withhold and offset is carried out in good faith, the Undersigned hereby waives any claims against Dependable for any consequential damages coming from such withhold and offset even if it is later determined that the withhold and offset was improper./p> p>strong>7./strong> THE UNDERSIGNED HEREBY WAIVES THE RIGHT TO A JURY TRIAL OF ANY OR ALL CLAIMS OR DISPUTES WHICH MAY ARISE AFTER ACCEPTANCE OF THIS APPLICATION BY Dependable IN CONNECTION WITH THIS AGREEMENT OR ANY RENTAL CONTRACT BETWEEN THE UNDERSIGNED AND Dependable./p> p>strong>8./strong> The terms of this Agreement may be revised or supplemented from time to time by Dependable sending the Undersigned notice of such changes. It will be presumed that the Undersigned has received any such notice mailed to the Undersigned at the address shown on this Application or otherwise provided to Dependable./p> p>strong>9./strong> Dependable Rentals, Inc. reserves the right to file preliminary notices for work done in the states where those notices are required by state law in order to protect our Lien Rights./p> p>strong>10./strong> THE UNDERSINGED ATTESTS TO HAVING VALID AND CURRENT INSURANCE AT THE TIME OF RENTAL AND INTENDS TO EXTEND COVERAGE TO ALL RENTALS. INSURANCE COVERAGE MUST BE APPROPRIATE AND ADEQUATE FOR THE TYPE OF VEHICLE OR EQUIPMENT RENTED IN COMPLIANCE FOR ADIQUATE COVERAGE NEEDED BY LOW FOR THE ASSOCIATED VALID DRIVERS LICENSE TO THIS RENTAL AND IN ACCORDANCE WITH THE LAWS OF THE STATE WHICH THE VEHICLE TRAVELS./p> p>strong>11./strong> At the time of rental, the renter or authorized driver may be subject to an electronic DMV check and additional consumer or commercial investigation for the purposes of preventing fraud./p> /div> div stylemargin-top: 15px;> div stylefont-weight: bold; text-align: center;> div stylemargin-bottom: 15px;>I have read and agree to the Account Agreement and Terms/div> button idagreeTermsBtn onclicktoggleAgreeTerms() stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 120px;> Click to AGREE /button> input typehidden idagreeTerms valuefalse> /div> /div> /div> !-- Document Upload Section --> div stylemargin-bottom: 20px;> h4>Required Documents/h4> p stylefont-size: 0.9em; color: #666; margin-bottom: 15px;>Please Upload and Attach the following documents: (if applicable)/p> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 15px;> div classupload-section idtaxCertUpload> h5>📄 State Tax Certbr>span stylefont-size: 0.8em; color: #666;>(Required for No Sales Tax Validation)/span>/h5> input typefile idtaxCertFile accept.pdf,.jpg,.jpeg,.png styledisplay: none;> button onclickdocument.getElementById(taxCertFile).click()>Choose Tax Certificate/button> p stylefont-size: 0.8em; margin-top: 5px;>PDF, JPG, or PNG format/p> button onclickshowDocumentTips(taxCert) stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em; margin-top: 5px;>💡 Tips/button> div idtaxCertPreview styledisplay: none; margin-top: 10px; font-size: 0.9em; color: #28a745;>/div> /div> div classupload-section idcoiUpload> h5>📄 COI Attachedbr>span stylefont-size: 0.8em; color: #666;>(Typically for Commercial Accounts)/span>/h5> input typefile idcoiFile accept.pdf,.jpg,.jpeg,.png styledisplay: none;> button onclickdocument.getElementById(coiFile).click()>Choose COI Document/button> p stylefont-size: 0.8em; margin-top: 5px;>PDF, JPG, or PNG format/p> button onclickshowDocumentTips(coi) stylebackground: #17a2b8; color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8em; margin-top: 5px;>💡 Tips/button> div idcoiPreview styledisplay: none; margin-top: 10px; font-size: 0.9em; color: #28a745;>/div> /div> /div> /div> !-- Submit Button --> div styletext-align: center; margin: 20px 0;> button idsubmitAccountBtn onclicksubmitAccountInfo() stylebackground-color: #28a745; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;>📋 Submit Account Information/button> div classloading idaccountLoading>🔄 Processing account information.../div> /div> div idaccountResult>/div> /div> /div> !-- Rental Agreement Tab --> div idrental-agreement classtab-content> div stylebackground-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;> h3>📄 Rental Agreement/h3> !-- Agreement Document Display --> div stylemargin-bottom: 20px; border: 2px solid var(--primary-color); padding: 20px; border-radius: 8px; background-color: #f8f9fa;> div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;> div> h4 stylecolor: var(--primary-color); margin: 0;>Rental Agreement Document/h4> p stylefont-size: 0.8em; color: #666; margin: 5px 0 0 0;>Scroll to bottom to Agree (REQUIRED)/p> /div> button onclickdownloadRentalAgreementPDF() classsecondary-btn stylepadding: 8px 16px; font-size: 14px;> 📄 Download PDF /button> /div> iframe srcrental-agreement-accordion.html stylewidth: 100%; height: 600px; border: none;>/iframe> /div> !-- Agreement Acceptance (Outside Scroll Area) --> div stylemargin-bottom: 20px; border: 2px solid var(--primary-color); padding: 20px; border-radius: 8px; background-color: #fff3cd;> div styletext-align: center;> div stylefont-weight: bold; margin-bottom: 15px; font-size: 16px;>I have read and agree to the terms and conditions of this Rental Agreement/div> button idagreeRentalTermsBtn onclicktoggleAgreeRentalTerms() stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 120px;> Click to AGREE /button> input typehidden idagreeRentalTerms valuefalse> /div> /div> !-- Signature Section --> div stylemargin-bottom: 20px;> h4>Digital Signature Required/h4> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-bottom: 15px;> div> label>Signed Name: input typetext idrentalSignedName placeholderEnter your full name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Company Name (Optional): input typetext idrentalCompanyName placeholderEnter company name (optional) stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Date: input typedate idrentalSignatureDate stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> !-- Additional Signature Confirmation Fields --> div stylebackground-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 15px;> h5 stylecolor: #495057; margin-top: 0;>Signature Authentication/h5> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px;> div> label>Email Address: input typeemail idrentalSignerEmail placeholderEnter your email address stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Phone Number: input typetel idrentalSignerPhone placeholderEnter your phone number stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>IP Address: input typetext idrentalSignerIP readonly stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; background-color: #e9ecef; placeholderAuto-detected>/label> /div> /div> /div> !-- Legal Acknowledgment --> div stylebackground-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 8px; margin-bottom: 15px;> h5 stylecolor: #856404; margin-top: 0;>Signature Legal Acknowledgement/h5> div stylemargin-bottom: 15px; text-align: center;> div stylefont-size: 0.9em; color: #495057; margin-bottom: 10px;>I acknowledge that my digital signature has the same legal effect as a handwritten signature and I intend to be legally bound by this agreement./div> button idlegalAcknowledgmentBtn onclicktoggleLegalAcknowledgment() stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 140px;> Click to AGREE /button> input typehidden idlegalAcknowledgment valuefalse> /div> div stylemargin-bottom: 10px; text-align: center;> div stylefont-size: 0.9em; color: #495057; margin-bottom: 10px;>I confirm that I am the person named above and have the authority to enter into this agreement./div> button ididentityConfirmationBtn onclicktoggleIdentityConfirmation() stylebackground-color: #28a745; color: white; border: none; padding: 12px 24px; border-radius: 25px; font-size: 16px; font-weight: bold; cursor: pointer; min-width: 140px;> Click to CONFIRM /button> input typehidden ididentityConfirmation valuefalse> /div> /div> !-- Signature Canvas --> div stylemargin-top: 15px;> label styledisplay: block; margin-bottom: 10px; font-weight: bold;>Digital Signature:/label> div styleborder: 2px solid #ddd; border-radius: 4px; padding: 10px; background-color: white;> canvas idrentalSignatureCanvas width400 height150 styleborder: 1px solid #ccc; cursor: crosshair; display: block; margin: 0 auto;>/canvas> div styletext-align: center; margin-top: 10px;> button typebutton onclickclearRentalSignature() stylebackground-color: #dc3545; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px;>Clear Signature/button> span stylefont-size: 0.9em; color: #666;>Sign above using your mouse or finger/span> /div> /div> /div> /div> !-- Submit Button --> div styletext-align: center; margin: 20px 0;> button idsubmitRentalBtn onclicksubmitRentalAgreement() stylebackground-color: #28a745; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;>📋 Submit Rental Agreement/button> div classloading idrentalLoading styledisplay: none;>🔄 Processing rental agreement.../div> /div> div idrentalResult>/div> /div> /div> !-- Verification Checks Modal --> div idverificationChecksModal styledisplay: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000;> div styleposition: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 30px; border-radius: 8px; max-width: 600px; max-height: 80vh; overflow-y: auto; box-shadow: 0 4px 20px rgba(0,0,0,0.3);> h3 stylecolor: #2196f3; margin-top: 0;>🔍 What We Check During ID Verification/h3> div styletext-align: left; line-height: 1.6;> h4 stylecolor: #28a745; margin-top: 20px;>✅ PASSES ALL CHECKS:/h4> ul stylemargin: 10px 0; padding-left: 20px;> li>strong>Document type:/strong> USA Drivers License, Passport, or State ID/li> li>strong>Document origin:/strong> Must be USA-issued/li> li>strong>Expiration:/strong> Must be current (not expired)/li> li>strong>Face match:/strong> Must meet similarity threshold/li> li>strong>Image quality:/strong> Must be clear enough for processing/li> /ul> h4 stylecolor: #dc3545; margin-top: 20px;>❌ REJECTION REASONS:/h4> ul stylemargin: 10px 0; padding-left: 20px;> li>Wrong document type (foreign license, etc.)/li> li>Non-USA document/li> li>Expired document/li> li>Face similarity below threshold/li> li>Poor image quality preventing processing/li> li>Missing required images/li> li>Invalid image format or size/li> /ul> div stylebackground-color: #f8f9fa; padding: 15px; border-radius: 4px; margin: 20px 0;> p stylemargin: 0; font-size: 0.9em; color: #495057;> strong>Note:/strong> Our system uses advanced AWS technology to ensure accurate and secure verification. All attempts are logged for security and compliance purposes. This allows us to help you complete the process. /p> /div> /div> div styletext-align: center; margin-top: 20px;> button onclickcloseVerificationChecksModal() stylebackground-color: #007bff; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; font-size: 16px;>Got It!/button> /div> /div> /div> !-- Cookie Agreement Banner --> div idcookieBanner styleposition: fixed; bottom: 20px; left: 20px; background: #333; color: white; padding: 15px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 1000; max-width: 350px; font-size: 14px; display: block;> div stylemargin-bottom: 10px;> 🍪 We use cookies to improve your experience and analyze site usage. /div> div styledisplay: flex; gap: 10px;> button onclickacceptCookies() stylebackground: #28a745; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 12px;>Accept/button> button onclickdeclineCookies() stylebackground: #6c757d; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 12px;>Decline/button> /div> /div> script srchttps://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js>/script> script srcrental-agreement-data.js>/script> script> // Cookie Agreement Functions function acceptCookies() { localStorage.setItem(cookiesAccepted, true); document.getElementById(cookieBanner).style.display none; } function declineCookies() { localStorage.setItem(cookiesAccepted, false); document.getElementById(cookieBanner).style.display none; } // Check if cookies already accepted/declined function checkCookieConsent() { const cookieConsent localStorage.getItem(cookiesAccepted); if (cookieConsent ! null) { document.getElementById(cookieBanner).style.display none; } } // Initialize cookie check on page load document.addEventListener(DOMContentLoaded, function() { checkCookieConsent(); // Initialize first tab highlighting switchTab(new-account); // Detect mobile browser and adjust UI (OS-based only, not touch capability) const isMobile /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); if (isMobile) { console.log(Mobile browser detected - adjusting UI); // Hide desktop camera buttons const desktopButtons document.querySelectorAll(.desktop-camera-btn); desktopButtons.forEach(btn > btn.style.display none); // Hide QR code buttons (mobile will use direct camera) const qrButtons document.querySelectorAll(buttononclick*generateQRCode); qrButtons.forEach(btn > { if (btn.textContent.includes(Mobile Camera)) { // For liveness, hide the QR button completely if (btn.getAttribute(onclick).includes(liveness)) { btn.style.display none; } else { // For other uploads, convert to direct camera btn.textContent 📱 Camera; const onclickStr btn.getAttribute(onclick); const match onclickStr.match(/generateQRCode\((\w+)\)/); if (match) { const uploadType match1; btn.setAttribute(onclick, `openMobileCamera(${uploadType})`); } } } }); // Update liveness buttons for mobile const livenessDesktopBtn document.querySelector(buttononclickstartLivenessDetection()); if (livenessDesktopBtn) { livenessDesktopBtn.textContent 📹 Camera; // Keep original function - it was working correctly } // Hide the QR button on mobile (it doesnt work properly) const livenessQRBtn document.querySelector(buttononclick*generateQRCode(\liveness\)); if (livenessQRBtn) { livenessQRBtn.style.display none; } } }); // Mobile camera functions function openMobileCamera(uploadType) { const fileInputId uploadType + Mobile; const fileInput document.getElementById(fileInputId); if (fileInput) { fileInput.click(); } else { // Fallback to regular file input document.getElementById(uploadType + File).click(); } } function startMobileLiveness() { // Start mobile liveness video recording const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); navigator.mediaDevices.getUserMedia({ video: { facingMode: user }, audio: false }) .then(stream > { video.srcObject stream; video.style.display block; result.style.display block; result.innerHTML ` div stylebackground: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #856404;>strong>📹 Recording Liveness Video/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Look directly at camera - auto-completing in span idcountdown>3/span> seconds/p> button onclickcompleteMobileLiveness() stylebackground: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 4px; margin-top: 8px;>Complete Now/button> /div> `; // Start countdown let countdown 3; const countdownEl document.getElementById(countdown); const countdownInterval setInterval(() > { countdown--; if (countdownEl) countdownEl.textContent countdown; if (countdown 0) { clearInterval(countdownInterval); completeMobileLiveness(); } }, 1000); // Store stream for later cleanup window.currentLivenessStream stream; }) .catch(error > { console.error(Mobile camera error:, error); result.style.display block; result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>strong>❌ Camera Error/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Please allow camera access and try again/p> /div> `; }); } function completeMobileLiveness() { const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); if (!video.videoWidth || !video.videoHeight) { console.error(Video not ready); return; } // Set canvas size to video size canvas.width video.videoWidth; canvas.height video.videoHeight; // Draw current video frame to canvas const ctx canvas.getContext(2d); ctx.drawImage(video, 0, 0); // Extract frame as base64 const frameData canvas.toDataURL(image/jpeg, 0.8).split(,)1; // Store liveness data livenessData { image: frameData, timestamp: new Date().toISOString(), source: mobile_camera }; // Stop camera if (window.currentLivenessStream) { window.currentLivenessStream.getTracks().forEach(track > track.stop()); window.currentLivenessStream null; } video.style.display none; // Show success result.innerHTML ` div stylebackground: #d4edda; border: 1px solid #c3e6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #155724;>strong>✅ Mobile Liveness Completed/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Video frame captured successfully/p> /div> `; updateButtons(); } // Initialize field synchronization listeners addFieldSyncListeners(); // Ensure page starts at the top window.scrollTo(0, 0); // API endpoint configuration - detect environment and use appropriate endpoint let API_ENDPOINT; if (window.location.hostname.includes(d1i4nhozl8hdc5.cloudfront.net)) { // Staging environment - using Lambda function URL API_ENDPOINT https://ivlayrvudt7esg4rjnzsk5o6xe0uqqal.lambda-url.us-east-1.on.aws/; } else { // Production environment - using Lambda function URL API_ENDPOINT https://ivlayrvudt7esg4rjnzsk5o6xe0uqqal.lambda-url.us-east-1.on.aws/; } console.log(Current hostname:, window.location.hostname); console.log(Using API endpoint:, API_ENDPOINT); // ID Verification variables let idFrontImage null; let idBackImage null; let selfieImage null; // Client Identification System (matches production) let clientId null; function generateClientId() { const timestamp Date.now(); const random Math.random().toString(36).substring(2, 8); return `DLR-${timestamp}-${random}`.toUpperCase(); } function initializeClient() { try { // Check if client ID already exists in local storage clientId localStorage.getItem(clientId); if (!clientId) { // Generate new client ID for new session clientId generateClientId(); localStorage.setItem(clientId, clientId); console.log(New client ID generated:, clientId); } else { console.log(Existing client ID loaded:, clientId); } } catch (error) { // Fallback if localStorage is blocked console.warn(localStorage blocked, using session-only client ID); clientId generateClientId(); } // Display client ID in the interface updateClientIdDisplay(); } function updateClientIdDisplay() { // Add client ID display to checklist const checklist document.getElementById(account-checklist); let clientDisplay document.getElementById(client-id-display); if (!clientDisplay) { clientDisplay document.createElement(div); clientDisplay.id client-id-display; clientDisplay.style.cssText text-align: center; margin-bottom: 15px; padding: 10px; background-color: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 5px; font-size: 0.9em;; clientDisplay.innerHTML `strong>Your Account ID:/strong> span stylefont-family: monospace; color: #007bff;>${clientId}/span>`; // Insert after the checklist title const title checklist.querySelector(h2); title.parentNode.insertBefore(clientDisplay, title.nextSibling); } } function setupFormChangeDetection() { // Get all form inputs, selects, and textareas const formElements document.querySelectorAll(input, select, textarea); formElements.forEach(element > { element.addEventListener(change, handleFormChange); element.addEventListener(input, handleFormChange); }); } function handleFormChange(event) { const element event.target; const tabContainer element.closest(.tab-content); if (!tabContainer) return; const tabId tabContainer.id; // Check if this tab has been submitted if ((tabId new-account && accountSubmitted) || (tabId rental-agreement && rentalSubmitted)) { formChanged true; showChangeAlert(element); updateSubmitButtons(tabId); } } function showChangeAlert(element) { // Remove existing alerts const existingAlert element.parentNode.querySelector(.change-alert); if (existingAlert) return; // Create alert message const alert document.createElement(div); alert.className change-alert; alert.style.cssText color: #dc3545; font-size: 12px; margin-top: 5px; font-weight: bold;; alert.textContent ⚠️ Data Changes After Submission will REQUIRE a New Submit; // Insert after the element element.parentNode.insertBefore(alert, element.nextSibling); } function updateSubmitButtons(tabId) { if (formChanged) { // Update account submit button only if account tab changed if (tabId new-account) { const accountBtn document.getElementById(submitAccountBtn); if (accountBtn && accountSubmitted) { accountBtn.style.backgroundColor #dc3545; accountBtn.textContent 🔄 New Submit Required; } } // Update rental submit button only if rental tab changed if (tabId rental-agreement) { const rentalBtn document.getElementById(submitRentalBtn); if (rentalBtn && rentalSubmitted) { rentalBtn.style.backgroundColor #dc3545; rentalBtn.textContent 🔄 New Submit Required; } } } } function resetFormChangeState() { formChanged false; // Remove all change alerts document.querySelectorAll(.change-alert).forEach(alert > alert.remove()); // Reset button styles const accountBtn document.getElementById(submitAccountBtn); if (accountBtn) { accountBtn.style.backgroundColor #28a745; accountBtn.textContent 📋 Submit Account Information; } const rentalBtn document.getElementById(submitRentalBtn); if (rentalBtn) { rentalBtn.style.backgroundColor #28a745; rentalBtn.textContent 📋 Submit Rental Agreement; } } // Toggle checklist visibility function toggleChecklist() { const content document.getElementById(checklist-content); const toggle document.getElementById(checklist-toggle); if (content.style.display none) { content.style.display block; toggle.textContent ▼; } else { content.style.display none; toggle.textContent ▶; } } // Tab switching function switchTab(tabName) { // Remove active class from all tabs and content document.querySelectorAll(.tab).forEach(tab > tab.classList.remove(active)); document.querySelectorAll(.tab-content).forEach(content > content.classList.remove(active)); // Remove active styling from all checklist items (but preserve completed status) document.querySelectorAll(.checklist-item).forEach((item, index) > { const tabKey new-account, id-verification, rental-agreementindex; if (!checklistProgresstabKey) { // Only reset if not completed item.style.border 2px solid #e9ecef; item.style.backgroundColor white; } }); // Add active class to the correct tab (find by onclick attribute or text content) const tabs document.querySelectorAll(.tab); tabs.forEach(tab > { if (tab.onclick && tab.onclick.toString().includes(`${tabName}`)) { tab.classList.add(active); } }); // Show the correct content document.getElementById(tabName).classList.add(active); // Sync fields when switching tabs setTimeout(syncFields, 100); // Highlight the corresponding checklist item let checklistItemId ; switch(tabName) { case new-account: checklistItemId checklist-item-1; break; case id-verification: checklistItemId checklist-item-2; break; case rental-agreement: checklistItemId checklist-item-3; // Initialize rental signature canvas when tab opens setTimeout(() > { if (!rentalSignatureCanvas) { rentalSignatureCanvas document.getElementById(rentalSignatureCanvas); if (rentalSignatureCanvas) { rentalSignatureCtx rentalSignatureCanvas.getContext(2d); // Mouse events rentalSignatureCanvas.addEventListener(mousedown, startRentalSigning); rentalSignatureCanvas.addEventListener(mousemove, drawRentalSignature); rentalSignatureCanvas.addEventListener(mouseup, stopRentalSigning); // Touch events rentalSignatureCanvas.addEventListener(touchstart, handleRentalTouch); rentalSignatureCanvas.addEventListener(touchmove, handleRentalTouch); rentalSignatureCanvas.addEventListener(touchend, stopRentalSigning); } } }, 100); break; } if (checklistItemId) { const checklistItem document.getElementById(checklistItemId); if (checklistItem) { checklistItem.style.border 2px solid #007bff; checklistItem.style.backgroundColor #f8f9ff; } } // Scroll to tabs area on mobile for better UX if (window.innerWidth 768) { document.querySelector(.tabs).scrollIntoView({ behavior: smooth, block: start }); } } // File input handlers for ID verification document.getElementById(idFrontFile).addEventListener(change, (e) > handleFile(e.target.files0, idFront)); document.getElementById(idFrontMobile).addEventListener(change, (e) > handleFile(e.target.files0, idFront)); document.getElementById(idBackFile).addEventListener(change, (e) > handleFile(e.target.files0, idBack)); document.getElementById(idBackMobile).addEventListener(change, (e) > handleFile(e.target.files0, idBack)); document.getElementById(selfieFile).addEventListener(change, (e) > handleFile(e.target.files0, selfie)); document.getElementById(selfieMobile).addEventListener(change, (e) > handleFile(e.target.files0, selfie)); document.getElementById(livenessMobile).addEventListener(change, (e) > handleLivenessVideo(e.target.files0)); // Drag and drop setup idFrontUpload, idBackUpload, selfieUpload.forEach(id > { const element document.getElementById(id); element.addEventListener(dragover, (e) > { e.preventDefault(); element.classList.add(dragover); }); element.addEventListener(dragleave, () > element.classList.remove(dragover)); element.addEventListener(drop, (e) > { e.preventDefault(); element.classList.remove(dragover); const file e.dataTransfer.files0; const type id.replace(Upload, ); handleFile(file, type); }); }); // File input handlers for document uploads document.getElementById(taxCertFile).addEventListener(change, (e) > handleDocumentFile(e.target.files0, taxCert)); document.getElementById(coiFile).addEventListener(change, (e) > handleDocumentFile(e.target.files0, coi)); // Signature Canvas Setup const canvas document.getElementById(signatureCanvas); const ctx canvas.getContext(2d); let isDrawing false; let hasSignature false; // Set canvas drawing properties ctx.strokeStyle #000; ctx.lineWidth 2; ctx.lineCap round; // Mouse events canvas.addEventListener(mousedown, startDrawing); canvas.addEventListener(mousemove, draw); canvas.addEventListener(mouseup, stopDrawing); canvas.addEventListener(mouseout, stopDrawing); // Touch events for mobile canvas.addEventListener(touchstart, handleTouch); canvas.addEventListener(touchmove, handleTouch); canvas.addEventListener(touchend, stopDrawing); function startDrawing(e) { isDrawing true; const rect canvas.getBoundingClientRect(); ctx.beginPath(); ctx.moveTo(e.clientX - rect.left, e.clientY - rect.top); hasSignature true; } function draw(e) { if (!isDrawing) return; const rect canvas.getBoundingClientRect(); ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top); ctx.stroke(); } function stopDrawing() { isDrawing false; } function handleTouch(e) { e.preventDefault(); const touch e.touches0; const mouseEvent new MouseEvent(e.type touchstart ? mousedown : e.type touchmove ? mousemove : mouseup, { clientX: touch.clientX, clientY: touch.clientY }); canvas.dispatchEvent(mouseEvent); } function clearSignature() { ctx.clearRect(0, 0, canvas.width, canvas.height); hasSignature false; } function getSignatureData() { return hasSignature ? canvas.toDataURL(image/png) : null; } function handleDocumentFile(file, type) { if (!file) return; const preview document.getElementById(type + Preview); preview.innerHTML `✅ ${file.name} (${(file.size / 1024).toFixed(1)} KB)`; preview.style.display block; } // Show verification checks modal function showVerificationChecksModal() { document.getElementById(verificationChecksModal).style.display block; } // Close verification checks modal function closeVerificationChecksModal() { document.getElementById(verificationChecksModal).style.display none; } // Field synchronization between tabs function syncFields() { // Person Name synchronization const firstName document.getElementById(firstName)?.value || ; const lastName document.getElementById(lastName)?.value || ; const fullName `${firstName} ${lastName}`.trim(); const primaryContact document.getElementById(primaryContact)?.value || ; const rentalSignedName document.getElementById(rentalSignedName)?.value || ; // Sync person names if (fullName && !primaryContact) { document.getElementById(primaryContact).value fullName; } if (fullName && !rentalSignedName) { document.getElementById(rentalSignedName).value fullName; } if (primaryContact && !fullName && primaryContact.includes( )) { const nameParts primaryContact.split( ); document.getElementById(firstName).value nameParts0; document.getElementById(lastName).value nameParts.slice(1).join( ); } if (rentalSignedName && !fullName && rentalSignedName.includes( )) { const nameParts rentalSignedName.split( ); document.getElementById(firstName).value nameParts0; document.getElementById(lastName).value nameParts.slice(1).join( ); } // Company Name synchronization const companyName document.getElementById(companyName)?.value || ; const customerName document.getElementById(customerName)?.value || ; const rentalCompanyName document.getElementById(rentalCompanyName)?.value || ; if (companyName && !customerName) { document.getElementById(customerName).value companyName; } if (companyName && !rentalCompanyName) { document.getElementById(rentalCompanyName).value companyName; } if (customerName && !companyName) { document.getElementById(companyName).value customerName; } if (customerName && !rentalCompanyName) { document.getElementById(rentalCompanyName).value customerName; } if (rentalCompanyName && !companyName) { document.getElementById(companyName).value rentalCompanyName; } if (rentalCompanyName && !customerName) { document.getElementById(customerName).value rentalCompanyName; } // Email synchronization const emailAddress document.getElementById(emailAddress)?.value || ; const apEmail document.getElementById(apEmail)?.value || ; const rentalSignerEmail document.getElementById(rentalSignerEmail)?.value || ; if (emailAddress && !apEmail) { document.getElementById(apEmail).value emailAddress; } if (emailAddress && !rentalSignerEmail) { document.getElementById(rentalSignerEmail).value emailAddress; } if (apEmail && !emailAddress) { document.getElementById(emailAddress).value apEmail; } if (apEmail && !rentalSignerEmail) { document.getElementById(rentalSignerEmail).value apEmail; } if (rentalSignerEmail && !emailAddress) { document.getElementById(emailAddress).value rentalSignerEmail; } if (rentalSignerEmail && !apEmail) { document.getElementById(apEmail).value rentalSignerEmail; } // Phone synchronization const phoneNumber document.getElementById(phoneNumber)?.value || ; const accountTelephone document.getElementById(accountTelephone)?.value || ; const rentalSignerPhone document.getElementById(rentalSignerPhone)?.value || ; if (phoneNumber && !accountTelephone) { document.getElementById(accountTelephone).value phoneNumber; } if (phoneNumber && !rentalSignerPhone) { document.getElementById(rentalSignerPhone).value phoneNumber; } if (accountTelephone && !phoneNumber) { document.getElementById(phoneNumber).value accountTelephone; } if (accountTelephone && !rentalSignerPhone) { document.getElementById(rentalSignerPhone).value accountTelephone; } if (rentalSignerPhone && !phoneNumber) { document.getElementById(phoneNumber).value rentalSignerPhone; } if (rentalSignerPhone && !accountTelephone) { document.getElementById(accountTelephone).value rentalSignerPhone; } } // Additional contacts functionality let contactCounter 0; function addMoreContacts() { contactCounter++; const container document.getElementById(additionalContactsContainer); const contactDiv document.createElement(div); contactDiv.id `contact-${contactCounter}`; contactDiv.style.cssText border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 15px; background-color: #f9f9f9;; contactDiv.innerHTML ` div styledisplay: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;> h5 stylemargin: 0; color: #007bff;>Contact #${contactCounter}/h5> button onclickremoveContact(${contactCounter}) stylebackground: #dc3545; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 0.8em;>Remove/button> /div> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px;> div> label>Contact Name: input typetext idcontactName${contactCounter} placeholderEnter contact name stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Email: input typeemail idcontactEmail${contactCounter} placeholderEnter email address stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Phone: input typetel idcontactPhone${contactCounter} placeholder(555) 123-4567 stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> div> label>Position: input typetext idcontactPosition${contactCounter} placeholderEnter position/title stylewidth: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;>/label> /div> /div> `; container.appendChild(contactDiv); } function removeContact(contactId) { const contactDiv document.getElementById(`contact-${contactId}`); if (contactDiv) { contactDiv.remove(); } } // Auto-fill test data function function checkTestData() { const companyName document.getElementById(customerName).value.toLowerCase().trim(); console.log(Checking test data, company name:, companyName); if (companyName dlrtest) { console.log(DLRTEST detected, filling test data...); fillAccountTestData(); } } function fillAccountTestData() { console.log(Starting fillAccountTestData...); try { // Basic company info document.getElementById(customerName).value Dependable Lift Rentals Test; document.getElementById(doingBusinessAs).value DLR Testing; document.getElementById(streetAddress).value 123 Test Street; document.getElementById(city).value Test City; document.getElementById(state).value CA; document.getElementById(zipCode).value 90210-1234; document.getElementById(primaryContact).value John Test; document.getElementById(apEmail).value test@dlr.com; document.getElementById(accountTelephone).value (555) 123-4567; document.getElementById(namePrinted).value John Test; document.getElementById(signatureDate).value new Date().toISOString().split(T)0; document.getElementById(accountNotes).value Test account for development purposes; console.log(Basic fields filled); console.log(Document fields processed (now handled by Textract)); // Set rental protection if (typeof setRentalProtection function) { setRentalProtection(YES); console.log(Rental protection set); } console.log(Account test data filled successfully); } catch (error) { console.error(Error in fillAccountTestData:, error); } } // Add event listeners for field synchronization function addFieldSyncListeners() { // Person name fields firstName, lastName, primaryContact, rentalSignedName.forEach(id > { const element document.getElementById(id); if (element) { element.addEventListener(blur, syncFields); } }); // Company name fields companyName, customerName, rentalCompanyName.forEach(id > { const element document.getElementById(id); if (element) { element.addEventListener(blur, syncFields); } }); // Email fields emailAddress, apEmail, rentalSignerEmail.forEach(id > { const element document.getElementById(id); if (element) { element.addEventListener(blur, syncFields); } }); // Phone fields phoneNumber, accountTelephone, rentalSignerPhone.forEach(id > { const element document.getElementById(id); if (element) { element.addEventListener(blur, syncFields); } }); } // Helper function to convert file to base64 function fileToBase64(file) { return new Promise((resolve, reject) > { const reader new FileReader(); reader.readAsDataURL(file); reader.onload () > resolve(reader.result); reader.onerror error > reject(error); }); } async function submitAccountInfo() { const submitBtn document.getElementById(submitAccountBtn); const loading document.getElementById(accountLoading); const result document.getElementById(accountResult); // Collect all form data const signatureData getSignatureData(); // Process uploaded files const taxCertFile document.getElementById(taxCertFile).files0; const coiFile document.getElementById(coiFile).files0; const accountData { client_id: clientId, customer_name: document.getElementById(customerName).value.trim(), doing_business_as: document.getElementById(doingBusinessAs).value.trim(), street_address: document.getElementById(streetAddress).value.trim(), city: document.getElementById(city).value.trim(), state: document.getElementById(state).value.trim(), zip_code: document.getElementById(zipCode).value.trim(), primary_contact: document.getElementById(primaryContact).value.trim(), ap_email: document.getElementById(apEmail).value.trim(), telephone: document.getElementById(accountTelephone).value.trim(), rental_protection: document.getElementById(rentalProtection).value, name_printed: document.getElementById(namePrinted).value.trim(), signature_data: signatureData, signature_date: document.getElementById(signatureDate).value, notes: document.getElementById(accountNotes).value.trim(), browser_time: new Date().toISOString(), terms_agreed: document.getElementById(agreeTerms).value true }; // Collect additional contacts const additionalContacts ; for (let i 1; i contactCounter; i++) { const contactDiv document.getElementById(`contact-${i}`); if (contactDiv) { const contactName document.getElementById(`contactName${i}`)?.value.trim(); const contactEmail document.getElementById(`contactEmail${i}`)?.value.trim(); const contactPhone document.getElementById(`contactPhone${i}`)?.value.trim(); const contactPosition document.getElementById(`contactPosition${i}`)?.value.trim(); if (contactName || contactEmail || contactPhone || contactPosition) { additionalContacts.push({ name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }); } } } accountData.additional_contacts additionalContacts; // Add uploaded files as base64 if (taxCertFile) { const taxCertBase64 await fileToBase64(taxCertFile); accountData.tax_certificate { filename: taxCertFile.name, content_type: taxCertFile.type, data: taxCertBase64 }; } if (coiFile) { const coiBase64 await fileToBase64(coiFile); accountData.coi_document { filename: coiFile.name, content_type: coiFile.type, data: coiBase64 }; } // Validate required fields const requiredFields customer_name, street_address, city, state, zip_code, primary_contact, ap_email, telephone, name_printed, signature_date; const missingFields requiredFields.filter(field > !accountDatafield); // Check rental protection separately const rentalProtectionValue document.getElementById(rentalProtection).value; console.log(Rental Protection Value:, rentalProtectionValue); // Debug log if (missingFields.length > 0) { alert(Please fill in all required fields: + missingFields.join(, )); return; } if (!rentalProtectionValue || (rentalProtectionValue ! YES && rentalProtectionValue ! NO)) { alert(Please select either Accept or Decline for Rental Protection Program); return; } // Check COI requirement if rental protection is declined if (rentalProtectionValue NO) { const coiFile document.getElementById(coiFile).files0; if (!coiFile) { alert(Certificate of Insurance (COI) is required when Rental Protection Program is declined); return; } } // Check signature if (!signatureData) { alert(Please provide your digital signature); return; } // Check terms agreement if (document.getElementById(agreeTerms).value ! true) { alert(Please read and agree to the Account Agreement and Terms); return; } submitBtn.disabled true; loading.style.display block; result.innerHTML ; // Add terms agreement to payload accountData.terms_agreed document.getElementById(agreeTerms).value true; try { const response await fetch(API_ENDPOINT, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(accountData) }); const data await response.json(); if (response.ok) { accountSubmitted true; resetFormChangeState(); result.innerHTML `div classresult success> h3>✅ Account Information Submitted/h3> p>strong>Session ID:/strong> ${data.session_id}/p> p>strong>Customer:/strong> ${data.customer_name}/p> p>strong>Status:/strong> ${data.status}/p> p>em>Account information has been saved and email notification sent./em>/p> /div>`; // Update checklist progress updateChecklistProgress(new-account, true); // Scroll to top of checklist to show progress document.getElementById(account-checklist).scrollIntoView({ behavior: smooth, block: start }); } else { result.innerHTML `div classresult error> h3>❌ Error/h3> p>${data.error || Unknown error occurred}/p> ${getSupportMessage()} /div>`; } } catch (error) { result.innerHTML `div classresult error> h3>❌ Network Error/h3> p>Failed to connect to API: ${error.message}/p> ${getSupportMessage()} /div>`; } finally { submitBtn.disabled false; loading.style.display none; } } function handleFile(file, type) { console.log(handleFile called:, type, file); // Liveness should use handleLivenessVideo instead if (type liveness) { handleLivenessVideo(file); return; } // Clear any previous error messages when new file is uploaded const resultDiv document.getElementById(result); if (resultDiv && resultDiv.innerHTML.includes(error)) { resultDiv.innerHTML ; } if (!file || !file.type.startsWith(image/)) { alert(Please select a valid image file); return; } // Check for HEIC files (not supported by browsers) if (file.name.toLowerCase().includes(.heic) || file.name.toLowerCase().includes(.heif)) { alert(HEIC/HEIF files are not supported. Please convert to JPG/PNG first.); return; } if (file.size > 5 * 1024 * 1024) { alert(Image size must be less than 5MB); return; } console.log(Processing file:, file.name, file.size); const reader new FileReader(); reader.onload (e) > { console.log(FileReader loaded for:, type); // Create image element to compress const img new Image(); img.onload () > { console.log(Image loaded for compression:, type); const canvas document.createElement(canvas); const ctx canvas.getContext(2d); // Calculate new dimensions (max 1200px width/height) let { width, height } img; const maxSize 1200; if (width > maxSize || height > maxSize) { if (width > height) { height (height * maxSize) / width; width maxSize; } else { width (width * maxSize) / height; height maxSize; } } canvas.width width; canvas.height height; // Draw and compress ctx.drawImage(img, 0, 0, width, height); const compressedDataUrl canvas.toDataURL(image/jpeg, 0.8); const base64 compressedDataUrl.split(,)1; const preview document.getElementById(type + Preview); const section document.getElementById(type + Upload); console.log(Storing base64 for:, type); // Store base64 data switch(type) { case idFront: idFrontImage base64; break; case idBack: idBackImage base64; break; case selfie: selfieImage base64; break; case source: sourceImage base64; break; case target: targetImage base64; break; } // Update UI preview.src compressedDataUrl; preview.style.display block; section.classList.add(uploaded); // Reset verification state if ID document is re-uploaded if (type idFront || type idBack) { resetVerificationState(); } console.log(Updated UI for:, type); updateButtons(); }; img.src e.target.result; }; reader.readAsDataURL(file); } function updateButtons() { // Call the main verify button check function checkVerifyButton(); } function resetVerificationState() { // Clear previous verification results const resultDiv document.getElementById(result); if (resultDiv) { resultDiv.innerHTML ; } // Re-enable verify button if conditions are met updateButtons(); } function toggleBiometricConsent() { const btn document.getElementById(biometricConsentBtn); const input document.getElementById(biometricConsent); if (input.value false) { btn.style.backgroundColor #6c757d; btn.innerHTML ✅ I AGREE; input.value true; } else { btn.style.backgroundColor #28a745; btn.innerHTML Click to AGREE; input.value false; } checkVerifyButton(); } function toggleAgreeTerms() { const btn document.getElementById(agreeTermsBtn); const input document.getElementById(agreeTerms); if (input.value false) { btn.style.backgroundColor #6c757d; btn.innerHTML ✅ I AGREE; input.value true; } else { btn.style.backgroundColor #28a745; btn.innerHTML Click to AGREE; input.value false; } } function toggleLegalAcknowledgment() { const btn document.getElementById(legalAcknowledgmentBtn); const input document.getElementById(legalAcknowledgment); if (input.value false) { btn.style.backgroundColor #6c757d; btn.innerHTML ✅ I AGREE; input.value true; } else { btn.style.backgroundColor #28a745; btn.innerHTML Click to AGREE; input.value false; } } function toggleIdentityConfirmation() { const btn document.getElementById(identityConfirmationBtn); const input document.getElementById(identityConfirmation); if (input.value false) { btn.style.backgroundColor #6c757d; btn.innerHTML ✅ I CONFIRM; input.value true; } else { btn.style.backgroundColor #28a745; btn.innerHTML Click to CONFIRM; input.value false; } } function toggleAgreeRentalTerms() { const btn document.getElementById(agreeRentalTermsBtn); const input document.getElementById(agreeRentalTerms); if (input.value false) { btn.style.backgroundColor #6c757d; btn.innerHTML ✅ I AGREE; input.value true; } else { btn.style.backgroundColor #28a745; btn.innerHTML Click to AGREE; input.value false; } } function setRentalProtection(choice) { const acceptBtn document.getElementById(acceptBtn); const declineBtn document.getElementById(declineBtn); const input document.getElementById(rentalProtection); const coiUpload document.getElementById(coiUpload); // Reset both buttons to default green/red acceptBtn.style.backgroundColor #28a745; acceptBtn.innerHTML Click to Accept; declineBtn.style.backgroundColor #dc3545; declineBtn.innerHTML Click to Decline; // Reset COI highlighting coiUpload.style.border 2px dashed #ccc; coiUpload.style.backgroundColor #fafafa; // Set selected button to grey if (choice YES) { acceptBtn.style.backgroundColor #6c757d; acceptBtn.innerHTML ✅ Accepted; input.value YES; } else if (choice NO) { declineBtn.style.backgroundColor #6c757d; declineBtn.innerHTML ❌ Declined; input.value NO; // Highlight COI as required coiUpload.style.border 2px solid #dc3545; coiUpload.style.backgroundColor #fff5f5; // Add required indicator const coiTitle coiUpload.querySelector(h5); if (!coiTitle.innerHTML.includes(REQUIRED)) { coiTitle.innerHTML 📄 COI Attached (REQUIRED - Commercial Accounts); } } } function handleLivenessVideo(file) { if (!file) return; // Validate file type if (!file.type.startsWith(video/)) { alert(Please select a video file for liveness detection.); return; } const result document.getElementById(livenessResult); result.style.display block; result.innerHTML ` div stylebackground: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #856404;>strong>🎥 Processing Video.../strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Extracting frame from video for verification/p> /div> `; // Create video element to extract frame const video document.createElement(video); const canvas document.createElement(canvas); const ctx canvas.getContext(2d); video.onloadedmetadata function() { canvas.width video.videoWidth; canvas.height video.videoHeight; // Seek to middle of video for best frame video.currentTime video.duration / 2; }; video.onseeked function() { try { // Draw video frame to canvas ctx.drawImage(video, 0, 0); // Store liveness data livenessData { image: canvas.toDataURL(image/jpeg, 0.8).split(,)1, timestamp: new Date().toISOString(), source: mobile_video }; console.log(Mobile liveness data captured:, livenessData ? success : failed); // Update UI with success message result.innerHTML ` div stylebackground: #d4edda; border: 1px solid #c3e6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #155724;>strong>✅ Liveness Video Processed/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Frame extracted from mobile video successfully/p> /div> `; // Update buttons to enable verify if all requirements met updateButtons(); } catch (error) { console.error(Error processing liveness video:, error); result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>strong>❌ Video Processing Error/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Unable to extract frame from video. Please try again./p> /div> `; } }; video.onerror function() { result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>strong>❌ Video Load Error/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Unable to load video file. Please check the file format./p> /div> `; }; video.src URL.createObjectURL(file); } function generateQRCode(uploadType) { // Check if user is on mobile device (OS-based only, not touch capability) const isMobile /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); // If on mobile, directly open camera instead of QR code if (isMobile) { if (uploadType liveness) { startMobileLiveness(); } else { openMobileCamera(uploadType); } return; } // For liveness, create QR code to dedicated mobile page if (uploadType liveness) { const currentUrl window.location.href.split(?)0; const baseUrl currentUrl.substring(0, currentUrl.lastIndexOf(/)); const uniqueSessionId `${clientId}-${Date.now()}`; const mobileUrl `${baseUrl}/mobile-liveness.html?session${uniqueSessionId}`; // Create QR code modal const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 8px; text-align: center; max-width: 400px; margin: 20px;; const title document.createElement(h3); title.textContent Mobile Liveness Detection; title.style.color #e74c3c; title.style.marginBottom 20px; const instructions document.createElement(p); instructions.innerHTML 1. Scan this QR code with your mobile devicebr>2. Record a 3-second liveness videobr>3. Frame will be automatically processed and returned; instructions.style.marginBottom 20px; instructions.style.lineHeight 1.6; const qrContainer document.createElement(div); qrContainer.id qrcode-liveness; qrContainer.style.margin 20px auto; const status document.createElement(div); status.id liveness-status; status.textContent Waiting for mobile liveness capture...; status.style.cssText margin: 15px 0; color: #666; font-style: italic;; const closeBtn document.createElement(button); closeBtn.textContent Close; closeBtn.style.cssText background: #dc3545; color: white; border: none; padding: 10px 20px; border-radius: 4px; margin-top: 20px; cursor: pointer;; closeBtn.onclick () > document.body.removeChild(modal); container.appendChild(title); container.appendChild(instructions); container.appendChild(qrContainer); container.appendChild(status); container.appendChild(closeBtn); modal.appendChild(container); document.body.appendChild(modal); // Generate QR code const qrCode document.createElement(div); qrCode.innerHTML generateQRCodeSVG(mobileUrl); qrContainer.appendChild(qrCode); // Start polling S3 for liveness frame startLivenessPolling(uniqueSessionId, status, modal); return; } // Desktop: Show QR code for mobile upload const uploadSession `${clientId}-${uploadType}-${Date.now()}`; const uploadUrl `https://un52v6d8q9.execute-api.us-east-1.amazonaws.com/prod/mobile-upload?session${uploadSession}&type${uploadType}`; // Create QR code modal const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 12px; text-align: center; max-width: 400px;; const title document.createElement(h3); title.textContent `Upload ${uploadType} from Mobile`; title.style.marginBottom 20px; const instructions document.createElement(p); if (uploadType liveness) { instructions.innerHTML 1. Scan this QR code with your mobile devicebr>2. Record a 3-second video with your mobile camerabr>3. Video will be processed automatically; } else { instructions.innerHTML 1. Scan this QR code with your mobile devicebr>2. Take photo with your mobile camerabr>3. Photo will appear here automatically; } instructions.style.marginBottom 20px; const qrContainer document.createElement(div); qrContainer.id qr-container; qrContainer.style.cssText margin: 20px 0; display: flex; justify-content: center;; // Generate QR code using QR.js library const qrCode document.createElement(div); qrCode.innerHTML generateQRCodeSVG(uploadUrl); qrContainer.appendChild(qrCode); const status document.createElement(div); status.id upload-status; status.textContent Waiting for mobile upload...; status.style.cssText margin: 15px 0; color: #666; font-style: italic;; const closeBtn document.createElement(button); closeBtn.textContent Cancel; closeBtn.style.cssText padding: 10px 20px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer; margin-top: 15px;; closeBtn.onclick () > { clearInterval(pollInterval); document.body.removeChild(modal); }; container.appendChild(title); container.appendChild(instructions); container.appendChild(qrContainer); container.appendChild(status); container.appendChild(closeBtn); modal.appendChild(container); document.body.appendChild(modal); // Poll for upload completion const pollInterval setInterval(async () > { try { const response await fetch(`https://un52v6d8q9.execute-api.us-east-1.amazonaws.com/prod/api/check-upload?session${uploadSession}`); const data await response.json(); if (data.completed && data.imageData) { // Create file from uploaded data const blob dataURLtoBlob(data.imageData); const file new File(blob, `mobile-${uploadType}-${Date.now()}.jpg`, { type: image/jpeg }); // Process the uploaded file handleFile(file, uploadType); // Close modal clearInterval(pollInterval); document.body.removeChild(modal); // Show success message status.textContent Upload successful!; status.style.color #28a745; } } catch (error) { console.log(Polling for upload..., error); } }, 2000); } function generateQRCodeSVG(text) { // Use QR Server API which doesnt have CORS issues const encodedText encodeURIComponent(text); return `div styletext-align: center;> img srchttps://api.qrserver.com/v1/create-qr-code/?size200x200&data${encodedText} altQR Code stylewidth: 200px; height: 200px; border: 1px solid #ddd; margin-bottom: 10px;> div stylefont-size: 12px; color: #666; margin-top: 10px;>Scan with mobile camera/div> div stylefont-size: 10px; word-break: break-all; margin-top: 5px; padding: 5px; background: #f5f5f5;>${text}/div> /div>`; } function dataURLtoBlob(dataURL) { const arr dataURL.split(,); const mime arr0.match(/:(.*?);/)1; const bstr atob(arr1); let n bstr.length; const u8arr new Uint8Array(n); while (n--) { u8arrn bstr.charCodeAt(n); } return new Blob(u8arr, { type: mime }); } async function openCamera(type) { try { const facingMode (type selfie) ? user : environment; const stream await navigator.mediaDevices.getUserMedia({ video: { facingMode: facingMode } }); // Create camera modal const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 20px; border-radius: 8px; text-align: center; max-width: 90%; max-height: 90%;; const video document.createElement(video); video.style.cssText width: 100%; max-width: 400px; height: auto; transform: scaleX(-1);; video.srcObject stream; video.autoplay true; const buttonContainer document.createElement(div); buttonContainer.style.cssText margin-top: 15px; display: flex; gap: 10px; justify-content: center;; const captureBtn document.createElement(button); captureBtn.textContent 📷 Capture; captureBtn.style.cssText padding: 10px 20px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer;; const cancelBtn document.createElement(button); cancelBtn.textContent ❌ Cancel; cancelBtn.style.cssText padding: 10px 20px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer;; captureBtn.onclick () > { const canvas document.createElement(canvas); canvas.width video.videoWidth; canvas.height video.videoHeight; const ctx canvas.getContext(2d); // Flip the image back to normal orientation ctx.scale(-1, 1); ctx.drawImage(video, -canvas.width, 0); // For ID cards, flip horizontally again after capture if (type idFront || type idBack) { const tempCanvas document.createElement(canvas); tempCanvas.width canvas.width; tempCanvas.height canvas.height; const tempCtx tempCanvas.getContext(2d); tempCtx.scale(-1, 1); tempCtx.drawImage(canvas, -tempCanvas.width, 0); tempCanvas.toBlob(blob > { const file new File(blob, `${type}-${Date.now()}.jpg`, { type: image/jpeg }); handleFile(file, type); stream.getTracks().forEach(track > track.stop()); document.body.removeChild(modal); }, image/jpeg, 0.8); } else { canvas.toBlob(blob > { const file new File(blob, `${type}-${Date.now()}.jpg`, { type: image/jpeg }); handleFile(file, type); stream.getTracks().forEach(track > track.stop()); document.body.removeChild(modal); }, image/jpeg, 0.8); } }; cancelBtn.onclick () > { stream.getTracks().forEach(track > track.stop()); document.body.removeChild(modal); }; buttonContainer.appendChild(captureBtn); buttonContainer.appendChild(cancelBtn); container.appendChild(video); container.appendChild(buttonContainer); modal.appendChild(container); document.body.appendChild(modal); } catch (error) { alert(Camera access denied or not available. Please use file upload instead.); } } function checkVerifyButton() { const verifyBtn document.getElementById(verifyBtn); const biometricConsent document.getElementById(biometricConsent).value true; // Check if we have the actual image data (works for both file upload and camera) const hasIdFront !!idFrontImage; const hasIdBack !!idBackImage; const hasSelfie !!selfieImage; const hasLiveness !!livenessData; console.log(checkVerifyButton debug:, { biometricConsent, hasIdFront, hasIdBack, hasSelfie, hasLiveness }); const hasAllRequiredImages hasIdFront && hasSelfie && hasLiveness; // Back of ID is optional, Selfie with ID deprecated const shouldEnable biometricConsent && hasAllRequiredImages; console.log(Should enable verify button:, shouldEnable); verifyBtn.disabled !shouldEnable; } async function verifyID() { const verifyBtn document.getElementById(verifyBtn); const loading document.getElementById(idLoading); const result document.getElementById(idResult); // Validate user data const firstName document.getElementById(firstName).value.trim(); const lastName document.getElementById(lastName).value.trim(); const companyName document.getElementById(companyName).value.trim(); const phoneNumber document.getElementById(phoneNumber).value.trim(); const emailAddress document.getElementById(emailAddress).value.trim(); if (!firstName || !lastName || !companyName || !phoneNumber || !emailAddress) { alert(Please fill in all user information fields); return; } verifyBtn.disabled true; loading.style.display block; result.innerHTML ; const payload { client_id: clientId, id_front: idFrontImage, selfie: selfieImage, similarity_threshold: parseInt(document.getElementById(idThreshold).value), first_name: firstName, last_name: lastName, company_name: companyName, phone_number: phoneNumber, email_address: emailAddress, browser_time: new Date().toISOString() }; if (idBackImage) { payload.id_back idBackImage; } if (livenessData && livenessData.image) { // Send actual liveness frame data for verification payload.liveness_detection livenessData.image; console.log(Sending liveness detection data, source:, livenessData.source); } console.log(Sending payload to API:, { ...payload, id_front: payload.id_front ? base64_data_present : missing, selfie: payload.selfie ? base64_data_present : missing, id_back: payload.id_back ? base64_data_present : missing, liveness_detection: payload.liveness_detection ? base64_data_present : missing }); try { const response await fetch(API_ENDPOINT, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }); console.log(API Response status:, response.status); const data await response.json(); console.log(API Response data:, data); if (response.ok) { displayIDResults(data); } else { result.innerHTML `div classresult error> h3>❌ Error/h3> p>${data.error || Unknown error occurred}/p> p>strong>Status:/strong> ${response.status}/p> ${getSupportMessage()} /div>`; } } catch (error) { console.error(API Error:, error); result.innerHTML `div classresult error> h3>❌ Network Error/h3> p>Failed to connect to API: ${error.message}/p> ${getSupportMessage()} /div>`; } finally { verifyBtn.disabled false; loading.style.display none; updateButtons(); } } function displayIDResults(data) { const result document.getElementById(idResult); const faceComp data.face_comparison; const docVal data.document_validation; // Determine if verification was successful const isApproved faceComp.matches_found && (!docVal || docVal.valid); let html ; if (isApproved) { // Extract detailed results let faceMatchResult Not Performed; let livenessResult Not Performed; let minScore N/A; // Face comparison results if (faceComp && faceComp.matches_found && faceComp.matches && faceComp.matches.length > 0) { const topMatch faceComp.matches0; faceMatchResult `✅ ${topMatch.similarity.toFixed(1)}%`; minScore topMatch.similarity.toFixed(1) + %; } // Liveness results if (data.liveness_verification) { if (data.liveness_verification.status PASSED) { livenessResult `✅ PASSED`; if (data.liveness_verification.similarity) { const livenessScore data.liveness_verification.similarity; if (minScore N/A || livenessScore parseFloat(minScore)) { minScore livenessScore.toFixed(1) + %; } } } else { livenessResult `❌ FAILED`; } } else if (data.liveness_detection) { livenessResult ✅ PERFORMED; } // Success Message with detailed results html `div classresult success> h3>🎉 Verification Complete!/h3> div stylebackground: white; padding: 15px; border-radius: 6px; margin: 15px 0; border: 1px solid #c3e6cb;> p stylemargin: 5px 0; font-size: 16px;>strong>Face Match:/strong> ${faceMatchResult}/p> p stylemargin: 5px 0; font-size: 16px;>strong>Liveness Result:/strong> ${livenessResult}/p> p stylemargin: 5px 0; font-size: 16px;>strong>Minimum Score:/strong> ${minScore}/p> /div> p>strong>Your ID Verification documents have been accepted./strong>/p> p>We will review your documents and reply within 1 business day./p> p>If you have any questions or concerns or need immediate help, call from 8 AM CST - 5 PM CST at strong>1-800-531-7600/strong> or email us at strong>rentals@dependableliftrentals.com/strong>/p> p>strong>We are here to help!/strong>/p> /div>`; // Update checklist progress for successful verification updateChecklistProgress(id-verification, true); // Scroll to top of checklist to show progress document.getElementById(account-checklist).scrollIntoView({ behavior: smooth, block: start }); } else { // Decline Message html `div classresult error> h3>❌ Documents Not Accepted/h3> p>strong>Your ID Verification documents have NOT been accepted./strong>/p> p>Please click the upload buttons again to load new documents./p>`; // Add specific failure reasons html + p>strong>The document that failed is listed below:/strong>/p>ul>; if (!faceComp.matches_found) { html + li>Face comparison failed - selfie does not match ID photo/li>; } if (docVal && !docVal.valid) { html + `li>Document validation failed - ${docVal.document_type || Invalid document type}/li>`; } if (docVal && docVal.error) { html + `li>${docVal.error}/li>`; } html + `/ul> p>If you have any questions or concerns or need immediate help, call from 8 AM CST - 5 PM CST at strong>1-800-531-7600/strong> or email us at strong>rentals@dependableliftrentals.com/strong>/p> p>strong>We are here to help!/strong>/p> /div>`; } result.innerHTML html; } // Rental Agreement Functions let rentalSignatureCanvas, rentalSignatureCtx; let isRentalSigning false; function generateClientId() { const timestamp Date.now(); const random Math.random().toString(36).substring(2, 8); return `DLR-${timestamp}-${random}`.toUpperCase(); } function initializeClient() { try { // Check if client ID already exists in local storage clientId localStorage.getItem(clientId); if (!clientId) { // Generate new client ID for new session clientId generateClientId(); localStorage.setItem(clientId, clientId); console.log(New client ID generated:, clientId); } else { console.log(Existing client ID loaded:, clientId); } } catch (error) { // Fallback if localStorage is blocked console.warn(localStorage blocked, using session-only client ID); clientId generateClientId(); } // Display client ID in the interface updateClientIdDisplay(); } function updateClientIdDisplay() { // Add client ID display to checklist const checklist document.getElementById(account-checklist); let clientDisplay document.getElementById(client-id-display); if (!clientDisplay) { clientDisplay document.createElement(div); clientDisplay.id client-id-display; clientDisplay.style.cssText text-align: center; margin-bottom: 15px; padding: 10px; background-color: #e7f3ff; border: 1px solid #b3d9ff; border-radius: 5px; font-size: 0.9em;; clientDisplay.innerHTML `strong>Your Account ID:/strong> span stylefont-family: monospace; color: #007bff;>${clientId}/span>`; // Insert after the checklist title const title checklist.querySelector(h2); title.parentNode.insertBefore(clientDisplay, title.nextSibling); } } // Checklist Progress Tracking let checklistProgress { new-account: false, id-verification: false, rental-agreement: false }; function updateChecklistProgress(tabName, completed) { checklistProgresstabName completed; // Update visual indicators const itemMap { new-account: 1, id-verification: 2, rental-agreement: 3 }; const itemNumber itemMaptabName; const item document.getElementById(`checklist-item-${itemNumber}`); console.log(updateChecklistProgress:, tabName, completed, item found:, !!item); if (item) { if (completed) { item.style.border 2px solid #28a745; item.style.backgroundColor #d4edda; console.log(Applied green styling to item, itemNumber); } else { item.style.border 2px solid #e9ecef; item.style.backgroundColor white; } } updateOverallProgress(); } function updateOverallProgress() { const completed Object.values(checklistProgress).filter(Boolean).length; const total Object.keys(checklistProgress).length; const percentage (completed / total) * 100; document.getElementById(progress-text).textContent `${completed} of ${total} completed`; document.getElementById(progress-bar).style.width `${percentage}%`; // Show completion message if (completed total) { const checklist document.getElementById(account-checklist); checklist.style.border 2px solid #28a745; checklist.style.backgroundColor #f8fff9; // Add completion banner if (!document.getElementById(completion-banner)) { const banner document.createElement(div); banner.id completion-banner; banner.style.cssText background: #28a745; color: white; padding: 15px; border-radius: 8px; margin-top: 20px; text-align: center; font-weight: bold;; banner.innerHTML 🎉 Account Setup Complete! Your application has been submitted for review.; checklist.appendChild(banner); } } } // Track submission states let accountSubmitted false; let rentalSubmitted false; let formChanged false; // Initialize checklist on page load document.addEventListener(DOMContentLoaded, function() { // Initialize client identification system initializeClient(); // Add change listeners to all form fields setupFormChangeDetection(); // Prefill signature dates with todays date const today new Date().toISOString().split(T)0; document.getElementById(signatureDate).value today; document.getElementById(rentalSignatureDate).value today; initializeClient(); // Get users IP address for signature authentication fetch(https://api.ipify.org?formatjson) .then(response > response.json()) .then(data > { document.getElementById(rentalSignerIP).value data.ip; }) .catch(error > { console.log(Could not get IP address:, error); document.getElementById(rentalSignerIP).value Unable to detect; }); // Add consent checkbox listener const consentCheckbox document.getElementById(biometricConsent); if (consentCheckbox) { consentCheckbox.addEventListener(change, updateButtons); } rentalSignatureCanvas document.getElementById(rentalSignatureCanvas); if (rentalSignatureCanvas) { rentalSignatureCtx rentalSignatureCanvas.getContext(2d); // Set todays date for rental agreement document.getElementById(rentalSignatureDate).value new Date().toISOString().split(T)0; // Mouse events for rental signature rentalSignatureCanvas.addEventListener(mousedown, startRentalSigning); rentalSignatureCanvas.addEventListener(mousemove, drawRentalSignature); rentalSignatureCanvas.addEventListener(mouseup, stopRentalSigning); // Touch events for rental signature rentalSignatureCanvas.addEventListener(touchstart, handleRentalTouch); rentalSignatureCanvas.addEventListener(touchmove, handleRentalTouch); rentalSignatureCanvas.addEventListener(touchend, stopRentalSigning); } }); function startRentalSigning(e) { isRentalSigning true; const rect rentalSignatureCanvas.getBoundingClientRect(); const x e.clientX - rect.left; const y e.clientY - rect.top; rentalSignatureCtx.beginPath(); rentalSignatureCtx.moveTo(x, y); } function drawRentalSignature(e) { if (!isRentalSigning) return; const rect rentalSignatureCanvas.getBoundingClientRect(); const x e.clientX - rect.left; const y e.clientY - rect.top; rentalSignatureCtx.lineTo(x, y); rentalSignatureCtx.stroke(); } function stopRentalSigning() { isRentalSigning false; } function handleRentalTouch(e) { e.preventDefault(); const touch e.touches0; const mouseEvent new MouseEvent(e.type touchstart ? mousedown : e.type touchmove ? mousemove : mouseup, { clientX: touch.clientX, clientY: touch.clientY }); rentalSignatureCanvas.dispatchEvent(mouseEvent); } function clearRentalSignature() { rentalSignatureCtx.clearRect(0, 0, rentalSignatureCanvas.width, rentalSignatureCanvas.height); } async function submitRentalAgreement() { const submitBtn document.getElementById(submitRentalBtn); const loading document.getElementById(rentalLoading); const signedName document.getElementById(rentalSignedName).value.trim(); const companyName document.getElementById(rentalCompanyName).value.trim(); const signatureDate document.getElementById(rentalSignatureDate).value; const signerEmail document.getElementById(rentalSignerEmail).value.trim(); const signerPhone document.getElementById(rentalSignerPhone).value.trim(); const signerIP document.getElementById(rentalSignerIP).value; const agreeTerms document.getElementById(agreeRentalTerms).value true; const legalAcknowledgment document.getElementById(legalAcknowledgment).value true; const identityConfirmation document.getElementById(identityConfirmation).value true; // Validation if (!signedName) { alert(Please enter your signed name); return; } if (!signerEmail) { alert(Please enter your email address); return; } if (!signerPhone) { alert(Please enter your phone number); return; } if (!signatureDate) { alert(Please select a signature date); return; } if (!agreeTerms) { alert(Please agree to the rental agreement terms); return; } if (!legalAcknowledgment) { alert(Please acknowledge the legal effect of your digital signature); return; } if (!identityConfirmation) { alert(Please confirm your identity and authority to sign); return; } // Check if signature canvas has content const signatureData rentalSignatureCanvas.toDataURL(); const emptyCanvas document.createElement(canvas); emptyCanvas.width rentalSignatureCanvas.width; emptyCanvas.height rentalSignatureCanvas.height; if (signatureData emptyCanvas.toDataURL()) { alert(Please provide your digital signature); return; } // Generate complete signed PDF with all 41 sections const signedPdfDoc generateRentalAgreementPDF(true); const signedPdfBase64 signedPdfDoc.output(datauristring).split(,)1; // Create PDF filename first const pdfFilename `Rental_Agreement_SIGNED_${signedName.replace(/\s+/g, _)}_${signatureDate}.pdf`; // Create rental agreement payload with full client data for email const rentalData { client_id: clientId, rental_agreement_signed: true, signed_name: signedName, company_name: companyName, signature_date: signatureDate, signer_email: signerEmail, signer_phone: signerPhone, signer_ip: signerIP, signature_data: signatureData, legal_acknowledgment: legalAcknowledgment, identity_confirmation: identityConfirmation, browser_time: new Date().toISOString(), user_agent: navigator.userAgent, agreement_version: 1.0, signed_pdf: signedPdfBase64, // Complete signed PDF with all 41 sections pdf_filename: pdfFilename, // Full client data payload for email email_payload: { digital_signature_data: { signed_name: signedName, company_name: companyName || Individual, signature_date: signatureDate, email_address: signerEmail, phone_number: signerPhone, ip_address: signerIP, signature_captured: true, agreement_accepted: true }, pdf_s3_link: `https://face-scan-uploads.s3.amazonaws.com/rental-agreements/${clientId}/${pdfFilename}`, send_email_notification: true, email_subject: `Rental Agreement Signed - ${signedName}`, include_pdf_download_link: true } }; try { submitBtn.disabled true; loading.style.display block; // Debug: Log the payload being sent console.log(Sending rental agreement payload:, rentalData); console.log(Email payload object:, rentalData.email_payload); const response await fetch(API_ENDPOINT, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(rentalData) }); const data await response.json(); if (response.ok) { rentalSubmitted true; resetFormChangeState(); document.getElementById(rentalResult).innerHTML `div classresult success> h3>✅ Rental Agreement Submitted/h3> p>strong>Session ID:/strong> ${data.session_id}/p> p>strong>Signed by:/strong> ${signedName}/p> p>strong>Company:/strong> ${companyName || Individual}/p> p>strong>Email:/strong> ${signerEmail}/p> p>strong>Date:/strong> ${signatureDate}/p> p>em>Rental agreement has been saved with full signature authentication./em>/p> p>strong>✉️ Email notification sent with full client data and PDF download link./strong>/p> div styletext-align: center; margin-top: 15px;> button onclickdownloadSignedRentalAgreementPDF() stylebackground-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 14px;> 📄 Download Signed Agreement /button> /div> /div>`; // Update checklist progress updateChecklistProgress(rental-agreement, true); // Scroll to top of checklist to show progress document.getElementById(account-checklist).scrollIntoView({ behavior: smooth, block: start }); } else { throw new Error(data.error || Failed to submit rental agreement); } } catch (error) { console.error(Error:, error); document.getElementById(rentalResult).innerHTML `div classresult error> h3>❌ Submission Error/h3> p>Failed to submit rental agreement: ${error.message}/p> ${getSupportMessage()} /div>`; } finally { submitBtn.disabled false; loading.style.display none; } } function getSupportMessage() { return `div stylemargin-top: 15px; padding: 10px; background-color: #f8f9fa; border-left: 4px solid #007bff; border-radius: 4px;> p stylemargin: 0; font-size: 14px; color: #495057;> strong>Need Help?/strong>br> If you are having trouble with the software, verification, or new account creation, call us toll free at strong>1-800-531-7600/strong> or email us at a hrefmailto:rentals@dependableliftrentals.com stylecolor: #007bff;>rentals@dependableliftrentals.com/a>. We are here to help! /p> /div>`; } function generateRentalAgreementPDF(includeSignature false) { const { jsPDF } window.jspdf; const doc new jsPDF(); // Header doc.setFontSize(16); doc.setFont(undefined, bold); doc.text(DEPENDABLE LIFT RENTALS, INC., 105, 20, { align: center }); doc.text(RENTAL AGREEMENT, 105, 30, { align: center }); // Complete agreement content - ALL sections from single source doc.setFontSize(9); doc.setFont(undefined, normal); let yPos 50; // Use generated data from rental-agreement-data.js const agreementSections window.RENTAL_AGREEMENT_SECTIONS || 1. Contract Term: The term of the Contract shall begin on the Effective Date and shall continue until terminated by either party (Term) by providing written notice to the other party of such intent; termination to be effective fifteen (15) days from the date notice is received except that this Contract shall remain effective through the completion of all rentals outstanding as of the date this Contract would otherwise terminate. ; // Add each section with proper page breaks agreementSections.forEach((section, index) > { const lines doc.splitTextToSize(section, 170); lines.forEach(line > { if (yPos > 280) { doc.addPage(); yPos 20; } doc.text(line, 20, yPos); yPos + 4; }); yPos + 6; // Extra space between sections }); // Final legal statement yPos + 10; if (yPos > 250) { doc.addPage(); yPos 20; } doc.setFont(undefined, bold); doc.text(The undersigned represents and warrants s/he is of legal age and has the authority and power to sign this Contract and understands that this Contract is valid and enforceable once executed by Customer., 20, yPos, { maxWidth: 170 }); // Add signature section if requested if (includeSignature) { const signedName document.getElementById(rentalSignedName).value.trim(); const companyName document.getElementById(rentalCompanyName).value.trim(); const signatureDate document.getElementById(rentalSignatureDate).value; const signerEmail document.getElementById(rentalSignerEmail).value.trim(); const signerPhone document.getElementById(rentalSignerPhone).value.trim(); const signatureData rentalSignatureCanvas.toDataURL(); yPos + 20; if (yPos > 220) { doc.addPage(); yPos 20; } doc.setFontSize(12); doc.setFont(undefined, bold); doc.text(SIGNATURE SECTION, 20, yPos); yPos + 15; doc.setFontSize(10); doc.setFont(undefined, normal); doc.text(`Signed Name: ${signedName}`, 20, yPos); yPos + 8; doc.text(`Company: ${companyName || Individual}`, 20, yPos); yPos + 8; doc.text(`Email: ${signerEmail}`, 20, yPos); yPos + 8; doc.text(`Phone: ${signerPhone}`, 20, yPos); yPos + 8; doc.text(`Date: ${signatureDate}`, 20, yPos); yPos + 8; doc.text(`Session ID: ${clientId}`, 20, yPos); yPos + 15; // Add signature image if (signatureData && signatureData ! document.createElement(canvas).toDataURL()) { doc.text(Digital Signature:, 20, yPos); yPos + 5; doc.addImage(signatureData, PNG, 20, yPos, 100, 25); yPos + 30; } // Legal acknowledgments doc.setFontSize(8); doc.text(Legal Acknowledgments:, 20, yPos); yPos + 8; doc.text(I acknowledge that my digital signature has the same legal effect as a handwritten signature and I intend to be legally bound by this agreement., 20, yPos, { maxWidth: 170 }); yPos + 12; doc.text(I confirm that I am the person named above and have the authority to enter into this agreement., 20, yPos, { maxWidth: 170 }); yPos + 10; doc.text(`Document Generated: ${new Date().toLocaleString()}`, 20, yPos); } return doc; } function downloadRentalAgreementPDF() { // Generate unsigned PDF for review const doc generateRentalAgreementPDF(false); doc.save(Rental_Agreement_Review.pdf); } function downloadSignedRentalAgreementPDF() { // Generate signed PDF with all data const signedName document.getElementById(rentalSignedName).value.trim(); const signatureDate document.getElementById(rentalSignatureDate).value; const doc generateRentalAgreementPDF(true); doc.save(`Rental_Agreement_SIGNED_${signedName.replace(/\s+/g, _)}_${signatureDate}.pdf`); } function showDocumentTips(docType) { const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 12px; max-width: 500px; max-height: 80%; overflow-y: auto;; let content ; if (docType taxCert) { content ` h3 stylecolor: #007bff; margin-top: 0;>📄 Sales Tax Certificate Information/h3> div styletext-align: left; line-height: 1.6;> p stylemargin-bottom: 15px;> strong>Purpose:/strong> If you wish to have local sales taxes removed from your invoices, please provide your Sales Tax Certificates for each state. /p> p stylemargin-bottom: 15px;> strong>Optional:/strong> If you do not have sales tax certificates, no need to upload anything in this section. /p> p stylemargin-bottom: 15px;> strong>Multiple States:/strong> You can upload certificates for multiple states if your business operates across state lines. /p> /div> `; } else if (docType coi) { content ` h3 stylecolor: #007bff; margin-top: 0;>📄 Certificate of Insurance (COI) Information/h3> div styletext-align: left; line-height: 1.6;> p stylemargin-bottom: 15px;> strong>When Required:/strong> If you decline our Rental Protection Program you are required to name Dependable Lift Rentals, Inc. and issue a Certificate of Insurance (COI) to us. /p> p stylemargin-bottom: 15px;> strong>Upload Location:/strong> Upload your COI document here. /p> p stylemargin-bottom: 15px;> strong>Full Details:/strong> Refer to rental contract section 8 for full details of the Rental Protection Plan. /p> p stylemargin-bottom: 15px;> strong>Commercial Accounts:/strong> This is typically required for commercial rental accounts. /p> /div> `; } container.innerHTML content + ` div styletext-align: center; margin-top: 20px;> button onclickthis.closest(.modal-overlay).remove() stylebackground: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;>Got it!/button> /div> `; modal.className modal-overlay; modal.appendChild(container); document.body.appendChild(modal); // Close on background click modal.addEventListener(click, (e) > { if (e.target modal) { modal.remove(); } }); } function showPhotoTips(photoType) { const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 12px; max-width: 500px; max-height: 80%; overflow-y: auto;; let content ; switch(photoType) { case idFront: content ` h3 stylecolor: #007bff; margin-top: 0;>📄 Front of ID Photo Tips/h3> div styletext-align: left; line-height: 1.6;> h4 stylecolor: #28a745; margin-bottom: 10px;>✅ Best Practices:/h4> ul stylemargin-bottom: 20px;> li>strong>Lay ID flat/strong> - Place on a dark, contrasting surface/li> li>strong>Good lighting/strong> - Use bright, even lighting to avoid shadows/li> li>strong>Fill the frame/strong> - ID should take up most of the photo/li> li>strong>Keep it straight/strong> - Align ID parallel to camera/li> li>strong>Focus clearly/strong> - Ensure all text and photo are sharp/li> li>strong>Avoid glare/strong> - No reflections on the ID surface/li> /ul> h4 stylecolor: #dc3545; margin-bottom: 10px;>❌ Avoid:/h4> ul stylemargin-bottom: 20px;> li>Blurry or out-of-focus images/li> li>Shadows covering parts of the ID/li> li>Glare or reflections/li> li>Tilted or angled shots/li> li>ID too small in frame/li> /ul> /div>`; break; case idBack: content ` h3 stylecolor: #007bff; margin-top: 0;>📄 Back of ID Photo Tips/h3> div styletext-align: left; line-height: 1.6;> h4 stylecolor: #28a745; margin-bottom: 10px;>✅ Best Practices:/h4> ul stylemargin-bottom: 20px;> li>strong>Same as front/strong> - Use identical lighting and positioning/li> li>strong>Capture all text/strong> - Ensure barcodes and text are visible/li> li>strong>Flat surface/strong> - Keep ID completely flat/li> li>strong>Sharp focus/strong> - All details must be readable/li> /ul> h4 stylecolor: #dc3545; margin-bottom: 10px;>❌ Avoid:/h4> ul stylemargin-bottom: 20px;> li>Cutting off edges or corners/li> li>Blurry barcodes or text/li> li>Different lighting than front photo/li> /ul> p stylecolor: #666; font-style: italic;>Note: Passport holders dont need back photo/p> /div>`; break; case selfie: content ` h3 stylecolor: #007bff; margin-top: 0;>🤳 Selfie Photo Tips/h3> div styletext-align: left; line-height: 1.6;> h4 stylecolor: #28a745; margin-bottom: 10px;>✅ Best Practices:/h4> ul stylemargin-bottom: 20px;> li>strong>Face the camera directly/strong> - Look straight at the lens/li> li>strong>Ensure good lighting/strong> - Use natural light or bright indoor lighting/li> li>strong>Remove sunglasses and hats/strong> - Face must be clearly visible/li> li>strong>Keep face centered/strong> - Position your face in the middle of the frame/li> li>strong>Avoid shadows/strong> - Make sure lighting is even across your face/li> li>strong>Hold camera steady/strong> - Avoid blurry photos/li> li>strong>Use neutral expression/strong> - Similar to your ID photo/li> /ul> h4 stylecolor: #dc3545; margin-bottom: 10px;>❌ Avoid:/h4> ul stylemargin-bottom: 20px;> li>Dark or dim lighting/li> li>Wearing sunglasses, hats, or face coverings/li> li>Tilting your head or looking away/li> li>Blurry or out-of-focus photos/li> li>Strong backlighting or shadows/li> /ul> /div>`; break; case selfieWithId: content ` h3 stylecolor: #007bff; margin-top: 0;>📸 Selfie with ID Photo Tips/h3> div styletext-align: left; line-height: 1.6;> h4 stylecolor: #28a745; margin-bottom: 10px;>✅ Best Practices:/h4> ul stylemargin-bottom: 20px;> li>strong>Hold ID next to face/strong> - Position ID close to your cheek/li> li>strong>Both face and ID visible/strong> - Ensure both are in frame and clear/li> li>strong>Good lighting on both/strong> - Light your face and ID evenly/li> li>strong>Keep ID flat/strong> - Dont bend or curve the ID/li> li>strong>Face forward/strong> - Look directly at camera/li> li>strong>Steady shot/strong> - Keep both face and ID in focus/li> /ul> h4 stylecolor: #dc3545; margin-bottom: 10px;>❌ Avoid:/h4> ul stylemargin-bottom: 20px;> li>ID covering part of your face/li> li>Blurry ID or face/li> li>ID too far from face/li> li>Shadows on face or ID/li> li>Tilted or angled positioning/li> /ul> /div>`; break; } container.innerHTML content + ` p stylefont-size: 0.9em; color: #666; font-style: italic;> These tips help ensure successful processing with AWS Rekognition technology. /p> div styletext-align: center; margin-top: 20px;> button onclickthis.closest(.modal-overlay).remove() stylebackground: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;>Got it!/button> /div> `; modal.className modal-overlay; modal.appendChild(container); document.body.appendChild(modal); // Close on background click modal.addEventListener(click, (e) > { if (e.target modal) { modal.remove(); } }); } function showHelpModal() { const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 8px; max-width: 500px; margin: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);; container.innerHTML ` h3 stylecolor: #2196f3; margin-top: 0;>📞 Need Help?/h3> div styletext-align: left; line-height: 1.6;> p stylemargin-bottom: 15px;>If you encounter any issues using these pages, uploading documents, etc. Please contact us immediately by phone or email./p> p stylemargin: 10px 0; font-weight: bold; color: #1976d2;>📞 Phone: a hreftel:1-800-531-7600 stylecolor: #1976d2; text-decoration: none;>1-800-531-7600/a>/p> p stylemargin: 10px 0; font-weight: bold; color: #1976d2;>✉️ Email: a hrefmailto:rentals@dependableliftrentals.com stylecolor: #1976d2; text-decoration: none;>rentals@dependableliftrentals.com/a>/p> /div> div styletext-align: center; margin-top: 20px;> button onclickthis.closest(.modal-overlay).remove() stylebackground: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;>Got it!/button> /div> `; modal.className modal-overlay; modal.appendChild(container); document.body.appendChild(modal); // Close on background click modal.addEventListener(click, (e) > { if (e.target modal) { modal.remove(); } }); } // Liveness Detection Functions let livenessStream null; let livenessDetectionActive false; let livenessData null; // Store liveness detection result async function startLivenessDetection() { const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); try { // Get camera access livenessStream await navigator.mediaDevices.getUserMedia({ video: { facingMode: user } }); video.srcObject livenessStream; video.style.display block; livenessDetectionActive true; result.innerHTML ` div stylebackground: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #856404;>strong>Liveness Test Active/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Look directly at camera - auto-completing in span idcountdown>3/span> seconds/p> button onclickcompleteLivenessTest() stylebackground: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 4px; margin-top: 8px;>Complete Now/button> button onclickcancelLivenessTest() stylebackground: #dc3545; color: white; border: none; padding: 6px 12px; border-radius: 4px; margin-top: 8px; margin-left: 8px;>Cancel/button> /div> `; // Auto-complete after 3 seconds with countdown let countdown 3; const countdownElement document.getElementById(countdown); const countdownInterval setInterval(() > { countdown--; if (countdownElement) { countdownElement.textContent countdown; } if (countdown 0) { clearInterval(countdownInterval); if (livenessDetectionActive) { completeLivenessTest(); } } }, 1000); } catch (error) { result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>Camera access denied or unavailable/p> /div> `; } } function completeLivenessTest() { const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); try { // Check if video is ready if (!video || video.videoWidth 0 || video.videoHeight 0) { console.log(Video not ready, dimensions:, video ? `${video.videoWidth}x${video.videoHeight}` : video element not found); result.innerHTML ` div stylebackground: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #856404;>strong>⚠️ Video Not Ready/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Please wait for camera to initialize, then click Complete Now/p> /div> `; return; } // Capture frame from video canvas.width video.videoWidth; canvas.height video.videoHeight; const ctx canvas.getContext(2d); ctx.drawImage(video, 0, 0); // Store liveness data with proper base64 extraction livenessData { image: canvas.toDataURL(image/jpeg, 0.8).split(,)1, timestamp: new Date().toISOString(), source: desktop_camera }; console.log(Liveness data captured:, livenessData ? success : failed); // Stop camera if (livenessStream) { livenessStream.getTracks().forEach(track > track.stop()); livenessStream null; } video.style.display none; livenessDetectionActive false; const successMessage ` div stylebackground: #d4edda; border: 1px solid #c3e6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #155724;>strong>✅ Liveness Test Completed/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Video verification captured successfully/p> img srcdata:image/jpeg;base64,${livenessData.image} stylemax-width: 200px; max-height: 150px; margin: 10px 0; border-radius: 4px; border: 1px solid #c3e6cb;> /div> `; result.style.display block; result.innerHTML successMessage; console.log(Success message set:, successMessage.length > 0 ? yes : no); // Update buttons to enable verify if all requirements met updateButtons(); console.log(updateButtons called after liveness completion); } catch (error) { console.error(Error in completeLivenessTest:, error); result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>strong>❌ Capture Error/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Please try clicking Complete Now manually/p> /div> `; } } function cancelLivenessTest() { const video document.getElementById(livenessVideo); const result document.getElementById(livenessResult); // Stop camera if (livenessStream) { livenessStream.getTracks().forEach(track > track.stop()); livenessStream null; } video.style.display none; livenessDetectionActive false; result.innerHTML ; } function showLivenessTips() { const modal document.createElement(div); modal.style.cssText position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; display: flex; align-items: center; justify-content: center;; const container document.createElement(div); container.style.cssText background: white; padding: 30px; border-radius: 8px; max-width: 500px; margin: 20px; box-shadow: 0 4px 20px rgba(0,0,0,0.3);; container.innerHTML ` h3 stylecolor: #e74c3c; margin-top: 0;>🎥 Liveness Detection Tips/h3> div styletext-align: left; line-height: 1.6;> p>strong>What is Liveness Detection?/strong>/p> p>A short video test to verify youre a real person, not a photo or video./p> p>strong>How it works:/strong>/p> ul> li>Click Start Liveness Test to begin/li> li>Allow camera access when prompted/li> li>Look directly at the camera/li> li>Click Complete Test when ready/li> /ul> p>strong>Tips for success:/strong>/p> ul> li>Ensure good lighting on your face/li> li>Look directly at camera/li> li>Keep your face centered in frame/li> li>This is optional - you can skip if needed/li> /ul> /div> div styletext-align: center; margin-top: 20px;> button onclickthis.closest(.modal-overlay).remove() stylebackground: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;>Got it!/button> /div> `; modal.className modal-overlay; modal.appendChild(container); document.body.appendChild(modal); // Close on background click modal.addEventListener(click, (e) > { if (e.target modal) { modal.remove(); } }); } // Mobile camera functions function openMobileCamera(uploadType) { const fileInputId uploadType + Mobile; const fileInput document.getElementById(fileInputId); if (fileInput) { fileInput.click(); } else { // Fallback to regular file input document.getElementById(uploadType + File).click(); } } function startMobileLiveness() { // Start mobile liveness video recording const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); navigator.mediaDevices.getUserMedia({ video: { facingMode: user }, audio: false }) .then(stream > { video.srcObject stream; video.style.display block; result.style.display block; result.innerHTML ` div stylebackground: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #856404;>strong>📹 Recording Liveness Video/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Look directly at camera - auto-completing in span idcountdown>3/span> seconds/p> button onclickcompleteMobileLiveness() stylebackground: #28a745; color: white; border: none; padding: 6px 12px; border-radius: 4px; margin-top: 8px;>Complete Now/button> /div> `; // Start countdown let countdown 3; const countdownEl document.getElementById(countdown); const countdownInterval setInterval(() > { countdown--; if (countdownEl) countdownEl.textContent countdown; if (countdown 0) { clearInterval(countdownInterval); completeMobileLiveness(); } }, 1000); // Store stream for later cleanup window.currentLivenessStream stream; }) .catch(error > { console.error(Mobile camera error:, error); result.style.display block; result.innerHTML ` div stylebackground: #f8d7da; border: 1px solid #f5c6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #721c24;>strong>❌ Camera Error/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Please allow camera access and try again/p> /div> `; }); } function completeMobileLiveness() { const video document.getElementById(livenessVideo); const canvas document.getElementById(livenessCanvas); const result document.getElementById(livenessResult); if (!video.videoWidth || !video.videoHeight) { console.error(Video not ready); return; } // Set canvas size to video size canvas.width video.videoWidth; canvas.height video.videoHeight; // Draw current video frame to canvas const ctx canvas.getContext(2d); ctx.drawImage(video, 0, 0); // Extract frame as base64 const frameData canvas.toDataURL(image/jpeg, 0.8).split(,)1; // Store liveness data livenessData { image: frameData, timestamp: new Date().toISOString(), source: mobile_camera }; // Stop camera if (window.currentLivenessStream) { window.currentLivenessStream.getTracks().forEach(track > track.stop()); window.currentLivenessStream null; } video.style.display none; // Show success result.innerHTML ` div stylebackground: #d4edda; border: 1px solid #c3e6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #155724;>strong>✅ Mobile Liveness Completed/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Video frame captured successfully/p> /div> `; updateButtons(); } function startLivenessPolling(sessionId, statusElement, modal) { console.log(Starting S3 polling for liveness frame:, sessionId); let pollCount 0; const maxPolls 30; // 60 seconds total (2 second intervals) const pollInterval setInterval(() > { pollCount++; statusElement.textContent `Waiting for mobile liveness capture... (${pollCount}/${maxPolls})`; // Poll Lambda function for liveness frame fetch(`https://ivlayrvudt7esg4rjnzsk5o6xe0uqqal.lambda-url.us-east-1.on.aws/liveness-frames/${sessionId}`, { method: GET, headers: { Content-Type: application/json, } }) .then(response > { if (response.ok) { return response.json(); } else if (response.status 404) { // Frame not uploaded yet, continue polling return { success: false }; } else { throw new Error(`HTTP ${response.status}`); } }) .then(data > { if (data.success && data.data) { console.log(Liveness data retrieved from S3:, data.data); clearInterval(pollInterval); // Check if we got video data that needs frame extraction if (data.data.type video && data.data.video) { console.log(Processing video data on desktop...); statusElement.textContent Processing video...; extractFrameFromVideo(data.data.video, (frameData) > { if (frameData) { livenessData { image: frameData, timestamp: data.data.timestamp, source: desktop_extracted_frame }; } else { // Fallback if frame extraction fails livenessData { image: desktop_extraction_fallback, timestamp: data.data.timestamp, source: desktop_fallback }; } completeDesktopLiveness(); }); } else { // Handle regular image data or fallback livenessData data.data; completeDesktopLiveness(); } function completeDesktopLiveness() { // Close modal if (document.body.contains(modal)) { document.body.removeChild(modal); } // Show success message with frame preview const result document.getElementById(livenessResult); result.style.display block; let framePreview ; if (livenessData.image && livenessData.image ! desktop_extraction_fallback && livenessData.image ! fallback_success) { framePreview `img srcdata:image/jpeg;base64,${livenessData.image} stylemax-width: 200px; max-height: 150px; margin: 10px 0; border-radius: 4px; border: 1px solid #c3e6cb;>`; } result.innerHTML ` div stylebackground: #d4edda; border: 1px solid #c3e6cb; padding: 10px; border-radius: 4px; margin-top: 10px;> p stylemargin: 0; color: #155724;>strong>✅ Mobile Liveness Completed/strong>/p> p stylemargin: 5px 0 0 0; font-size: 0.9em;>Video captured and processed successfully/p> ${framePreview} /div> `; updateButtons(); } } else if (pollCount > maxPolls) { // Timeout - stop polling console.log(Liveness polling timeout); clearInterval(pollInterval); statusElement.textContent Timeout - please try again or use desktop camera; statusElement.style.color #dc3545; } }) .catch(error > { console.error(Polling error:, error); if (pollCount > maxPolls) { clearInterval(pollInterval); statusElement.textContent Error - please try again or use desktop camera; statusElement.style.color #dc3545; } }); }, 2000); // Poll every 2 seconds } function extractFrameFromVideo(videoBase64, callback) { try { // Convert base64 to blob const byteCharacters atob(videoBase64); const byteNumbers new Array(byteCharacters.length); for (let i 0; i byteCharacters.length; i++) { byteNumbersi byteCharacters.charCodeAt(i); } const byteArray new Uint8Array(byteNumbers); const blob new Blob(byteArray, { type: video/webm }); // Create video element const video document.createElement(video); video.muted true; video.playsInline true; video.style.display none; document.body.appendChild(video); let processed false; // Timeout fallback const timeout setTimeout(() > { if (!processed) { console.log(Desktop video processing timeout); document.body.removeChild(video); callback(null); } }, 10000); video.onloadeddata () > { console.log(Desktop video loaded, duration:, video.duration); // Seek to middle of video video.currentTime Math.min(1.5, video.duration / 2); }; video.onseeked () > { if (processed) return; processed true; clearTimeout(timeout); try { console.log(Desktop extracting frame from video); const canvas document.createElement(canvas); canvas.width Math.min(video.videoWidth || 640, 640); canvas.height Math.min(video.videoHeight || 480, 480); const ctx canvas.getContext(2d); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); const frameData canvas.toDataURL(image/jpeg, 0.8).split(,)1; console.log(Desktop frame extracted successfully, size:, frameData.length); document.body.removeChild(video); URL.revokeObjectURL(video.src); callback(frameData); } catch (error) { console.error(Desktop canvas error:, error); document.body.removeChild(video); callback(null); } }; video.onerror (error) > { console.error(Desktop video error:, error); if (!processed) { processed true; clearTimeout(timeout); document.body.removeChild(video); callback(null); } }; video.src URL.createObjectURL(blob); } catch (error) { console.error(Desktop video processing error:, error); callback(null); } } // Initialize on page load window.addEventListener(load, function() { // Scroll to top setTimeout(() > window.scrollTo(0, 0), 100); // Initialize rental signature date const rentalDateField document.getElementById(rentalSignatureDate); if (rentalDateField && !rentalDateField.value) { rentalDateField.value new Date().toISOString().split(T)0; } }); /script> /div> !-- Footer --> footer stylebackground-color: var(--primary-color); color: white; padding: 40px 0; margin-top: 60px;> div classcontainer stylemax-width: 1200px; margin: 0 auto; padding: 0 20px;> div styledisplay: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 30px;> div> h4 stylemargin-bottom: 15px; color: var(--secondary-color);>Contact Information/h4> p stylemargin-bottom: 8px;>📞 strong>1-800-531-7600/strong>/p> p stylemargin-bottom: 8px;>✉️ rentals@dependableliftrentals.com/p> h4 stylemargin-bottom: 15px; margin-top: 25px; color: var(--secondary-color);>Account Setup/h4> p stylemargin-bottom: 8px;>✅ Identity Verification/p> p stylemargin-bottom: 8px;>✅ Secure Document Processing/p> p stylemargin-bottom: 8px;>✅ Fast Account Approval/p> /div> div> h4 stylemargin-bottom: 15px; color: var(--secondary-color);>Website Services/h4> p stylemargin-bottom: 8px;>strong>New Account Setup:/strong> www.dependableliftrentals.rentals/p> p stylemargin-bottom: 8px;>strong>Rentals:/strong> www.dependableliftrentals.com/p> /div> div> h4 stylemargin-bottom: 15px; color: var(--secondary-color);>Service Areas/h4> p stylemargin-bottom: 8px;>Nationwide Delivery Available/p> p stylemargin-bottom: 8px;>Fast delivery and pickup/p> p stylemargin-bottom: 8px;>Professional equipment rental/p> /div> /div> div styletext-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.2);> p>© 2025 Dependable Lift Rentals. All rights reserved./p> p stylefont-size: 0.8em; color: rgba(255,255,255,0.7); margin-top: 8px;> Version: production-v1.17.3 | Last Updated: Nov 20, 2025 /p> /div> /div> /footer>/body>/html>
Subdomains
Date
Domain
IP
blog.dependableliftrentals.com
2025-12-14
66.29.141.245
accounts.dependableliftrentals.com
2026-02-12
3.163.24.50
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]