1/* For AI Prompt */ 2// All the constants: 3window.chatPrompt = { 4 prompt: "Using the following Case description submitted by a potential customer, what follow up high level (maximum three listed in bullet points) most important questions would you ask in an SMS format in order to qualify the case and connect them to the right attorney? Note that we already have the zip code of the incident so we don't need the location\n Name: {{name}}\n Case Description: {{case_details}}", 5 context: "Act as a legal office intake assistant whose job is to qualify the legal case leads submitted on our legal content website for a free consultation.", 6 model: 'gpt-4' 7}; 8 9function getCookie(name) { 10 const value = `; ${document.cookie}`; 11 const parts = value.split(`; ${name}=`); 12 if (parts.length === 2) return parts.pop().split(';').shift(); 13} 14 15//server side API url: 16const CHAT_API_URL = "https://tools.nolo.com/public/form-prompt"; 17const FORM_CACHE_API_URL = "https://tools.nolo.com/form-lead"; 18const SPAM_CHAR_LIMIT = 1000; 19 20// Universal Form Version 21const ERROR_COMPLETION_LINE = "OpenAI completion failed"; 22 23// user visiting history in session 24userTrackKey = window.userTrackKey || 'userWebJourney'; 25 26const showModal = true; 27 28function submitToForm(parentWrapper, source) { 29 30 // send out Remove cache request 31 var reqHander = new Headers(); 32 reqHander.append("Content-Type", "application/json"); 33 reqHander.append("X-XSRF-TOKEN", decodeURIComponent(getCookie("XSRF-TOKEN"))); 34 35 console.log("submitToForm -- parentWrapper: ", parentWrapper); 36 37 var user_name = parentWrapper.querySelector("[name=name]").value.replace(/\s+/g, ' '); 38 // if (!parentWrapper.querySelector("[name=name]").value.includes(' ')) { 39 // user_name += ' LNU'; //last name undefined 40 // } else if (parentWrapper.querySelector("[name=name]").value.split(' ')[1].length <= 0) { 41 // user_name += 'LNU'; 42 // } 43 44 var raw = { 45 // "_token": $("input[name=_token]")[0].value, 46 "name": user_name, 47 "email": parentWrapper.querySelector("[name=email]").value, 48 "phone": parentWrapper.querySelector("[name=phone]").value, 49 "zip": parentWrapper.querySelector("[name=zip]").value, 50 "description": parentWrapper.querySelector("[name=description]").value, 51 "loc": parentWrapper.querySelector("[name=loc]").value, 52 "lead_id": parentWrapper.querySelector("[name=lead_id]").value, 53 "WebsiteID": "15-103-212-197-7-216-247-236", 54 "referer": document.referrer, 55 "ai_case_id": window.ai_case_id, 56 "skip_prediction": true 57 }; 58 59 if (NCMS.pageType != 'front') { 60 raw["form_type"] = parentWrapper.querySelector("[name=form_type]").value 61 } 62 63 raw['sms_optin_status'] = parentWrapper.querySelector("[name=sms_optin_status]").value; 64 raw['tcpa_consent_verbiage'] = parentWrapper.querySelector("[name=tcpa_consent_verbiage]").value; 65 raw['trusted_form_id'] = parentWrapper.querySelector("[name=trusted_form_id]").value; 66 67 if (source == 'submit') { 68 raw['ai'] = []; 69 document.querySelectorAll('.ai-radio-label').forEach(element => { 70 var radioFor = element.getAttribute('data-q-id'); 71 radioFor = radioFor.replace('[', '\\['); 72 radioFor = radioFor.replace(']', '\\]'); 73 74 var radioAnswer, radioLabel = '', radioAnswer = ''; 75 try { 76 radioLabel = document.querySelector('#' + formPrefix + "chat-content label[data-q-id=" + radioFor + "]").innerText; 77 radioAnswer = document.querySelector('#' + formPrefix + "chat-content input[name=" + radioFor + "]:checked").value; 78 } catch (error) { 79 radioAnswer = 'undefined'; 80 } 81 raw['ai'].push({'question': radioLabel, 'answer': radioAnswer}); 82 }); 83 } 84 85 var requestOptions = { 86 method: 'POST', //'DELETE', 87 headers: reqHander, 88 body: JSON.stringify(raw), 89 redirect: 'follow' 90 }; 91 92 window.cacheTimeStamp = ""; 93 94 if (window.gptAbort == true) { 95 console.log("aborting GPT, no AI Qs"); 96 // parentWrapper.querySelector(".submit-error").classList.remove('d-none'); 97 // $('#uform-universalFormPromptModal').modal('hide'); 98 99 // if we failed uformMCQ, then directly submit 100 fetch('/uformSubmit', requestOptions) 101 .then(function (response) { 102 return response.text() 103 }).then(function (data) { 104 console.log('moving on to success') 105 window.promptSubmit.click(); 106 }).catch(function (error) { 107 console.error(error) 108 }); 109 } else { 110 fetch('/uformSubmit', requestOptions) 111 .then(function (response) { 112 return response.text() 113 }).then(function (data) { 114 console.log('moving on to success') 115 window.promptSubmit.click(); 116 }).catch(function (error) { 117 console.error(error) 118 }); 119 } 120 121 // hit the submit btn or close the window 122 if (["x", "close"].includes(source)) { 123 // https://jira.internetbrands.com/browse/EHCS-3415 124 if (window.modelOpen && window.modelOpen == "uquiz") { 125 // close quiz model and show typ step 126 // show step-4 127 $('#uquiz-universalFormPromptModal').modal('hide'); 128 document.querySelector(".modal-progress_bar").setAttribute("data-progress-step", "4"); 129 // clear form 130 window.parentWrapper.querySelectorAll(".modal-body_main-step input").forEach(function (i) { 131 i.value = "" 132 }) 133 window.parentWrapper.querySelectorAll(".modal-body_main-step textarea").forEach(function (i) { 134 i.value = "" 135 }) 136 document.getElementById(quizPrefix + "loading").classList.remove("d-none"); 137 document.getElementById(quizPrefix + "chat-wrapper").classList.add("d-none"); 138 } else { 139 delete window.modelOpen 140 //redirect to typ 141 // window.location.replace("https://www.nolo.com/uform/success"); 142 } 143 delete window.modelOpen 144 } else { 145 // $('#uform-universalFormPromptModal').modal('hide'); 146 if (typeof promptModalQuiz !== 'undefined') { 147 // $('#uquiz-universalFormPromptModal').modal('hide'); 148 displayLoadingForSubmit(); 149 } 150 displayLoadingForSubmit(); 151 // window.promptSubmit.click(); 152 } 153} 154 155// Tracking function 156function triggerTrack(link_name, quiz = false) { 157 if (typeof gtag == 'undefined') { 158 function gtag() { 159 dataLayer.push(arguments); 160 } 161 } 162 var selector, journey_name; 163 if (quiz) { 164 journey_name = 'Form_Top'; 165 } else { 166 var form = parentWrapper.querySelector("form"); 167 var type = form.getAttribute("onsubmit").split(",")[1].replaceAll("'", "").replaceAll(" ", ""); 168 var selector, journey_name; 169 if (type == 'desktop') { 170 selector = '.form-connect-with-attorneys.desktop-form'; 171 journey_name = 'Form';
172 } else if (type == 'mobile') { 173 selector = '.form-connect-with-attorneys.mobile-form'; 174 journey_name = 'Form'; 175 } else if (type == 'top') { 176 selector = '.form-connect-with-attorneys.top-form'; 177 journey_name = 'Form_Top'; 178 } else if (type == 'bottom') { 179 selector = '.form-connect-with-attorneys.bottom-form'; 180 journey_name = 'Form_Bottom'; 181 } 182 183 } 184 185 const zipRegex = /^(00[67]\d{2}|009\d{2})(-\d{4})?$/; 186 187 var aop_id = document.getElementById('aop_abbreviation').value; 188 gtag('event', 'optimize_journey', { 189 'journey_name': aop_id + '_' + journey_name, 190 'step_name': aop_id + '_universalform', 191 'link_name': link_name, 192 'test_lead': zipRegex.test(document.querySelector(selector+ ' [name=zip]').value) ? "Yes" : "No", 193 'send_to': 'G-RJLCGB9QZ9' 194 195 }); 196} 197 198// hide the real submit button 199var formDescriptions = document.querySelectorAll(".find-a-lawyer-widget textarea[name=description]"); 200// hide the real click here button 201var formClickHeres = document.querySelectorAll(".find-a-lawyer-widget #universal-form-click-here"); 202 203// hide the real submit button 204var formSubmitBtns = document.querySelectorAll(".find-a-lawyer-widget button[type=submit]"); 205 206 207var promptModal; 208var formPrefix = "uform-"; 209 210if (document.getElementById('aop_abbreviation') && document.getElementById('aop_abbreviation').value !== 'SSDI') { 211 startChat(); 212} 213 214function startChat() { 215 // if (!document.querySelector(".find-a-lawyer-widget textarea[name=description]")) injectCaseField(); 216 if (!document.querySelector("#submit-to-prompt")) { 217 formSubmitBtns.forEach(function (formSubmitBtn) { 218 injectCaseButton(formSubmitBtn); 219 }); 220 } 221 222 if (!document.querySelector("#universal-form-click-here-ai")) formClickHeres.forEach(function (formClickHere) { 223 injectClickHere(formClickHeres) 224 }) 225 // if (!document.getElementById(`${formPrefix}universalFormPromptModal`) && showModal) bindModalEvents(); 226 // bind the modal to BS library: 227 bindModalEvents(); 228 // promptModal = new bootstrap.Modal(document.getElementById(`${formPrefix}universalFormPromptModal`), {backdrop:'static'}); 229} 230 231 232// create a prompt casedetails textarea to collect the first hand case description from universal form which should look exact same as case description field:' 233function injectClickHere(formClickHeres) { 234 formClickHeres.forEach(function (formClickHere) { 235 if (formClickHere.closest(".universal-form-top") && formClickHere.closest(".universal-form-top").parentNode.querySelector('#universal-form-modal')) { 236 // using uquiz-injection 237 } else { 238 if (formClickHere.parentElement.parentElement.querySelectorAll("#universal-form-click-here-ai").length>=1) { 239 return; 240 } else { 241 var clickHereDiv = document.createElement("div"); 242 clickHereDiv.innerText = "click here" 243 clickHereDiv.setAttribute("id", "universal-form-click-here-ai") 244 formClickHere.classList.add("d-none"); 245 // Adding the prompt continue button to the page: 246 formClickHere.insertAdjacentElement("afterend", clickHereDiv); 247 clickHereDiv.addEventListener("mousedown", function () { 248 event.preventDefault(); 249 formClickHere.parentElement.parentElement 250 var parentWrapper = event.currentTarget.closest(".find-a-lawyer-widget"); 251 parentWrapper.querySelector('input[name="sms_optin_status"]').value = '0'; 252 aiPromptSubmission(parentWrapper, formClickHere) 253 }) 254 } 255 } 256 }); 257} 258 259// create a prompt casedetails textarea to collect the first hand case description from universal form which should look exact same as case description field:' 260// function injectCaseField() { 261// formDescriptions.forEach(function (formDescription) { 262// if (formDescription.closest(".universal-form-top") && formDescription.closest(".universal-form-top").parentNode.querySelector('#universal-form-modal')) { 263// // using uquiz-injection 264// } else { 265// var promptCaseField = document.createElement("textarea"); 266// promptCaseField.classList.add("form-control"); 267// promptCaseField.setAttribute("style", "max-width:100%; flex-basis:100%;"); 268// promptCaseField.setAttribute("row", "7"); 269// promptCaseField.setAttribute("name", "casedetails"); 270// promptCaseField.setAttribute("placeholder", "Please describe your legal situation in as much detail as possible."); 271// formDescription.classList.add("d-none"); 272// // Adding the prompt continue button to the page: 273// formDescription.insertAdjacentElement("afterend", promptCaseField); 274// promptCaseField.addEventListener("input", function () { 275// promptCaseField.parentNode.classList.add("done"); 276// formDescription.value = promptCaseField.value; 277// promptCaseField.classList.remove('error') 278// }) 279// } 280// }); 281// } 282 283// create prompt continue button which should look exact same as submit button: 284 285// button to replace "real" submission for AI prompt 286function injectCaseButton(formSubmitBtn) { 287 var promptContinueBtn = document.createElement("button"); 288 promptContinueBtn.classList.add("btn"); 289 promptContinueBtn.classList.add("btn-CTA"); 290 promptContinueBtn.classList.add("find-a-lawyer-button"); 291 promptContinueBtn.setAttribute("id", "submit-to-prompt"); 292 promptContinueBtn.innerText = formSubmitBtn.innerText.replaceAll("\n", ""); 293 promptContinueBtn.type = "button"; 294 if(formSubmitBtn.getElementsByTagName('svg').length > 0) { 295 var btnSVG = formSubmitBtn.getElementsByTagName('svg')[0]; 296 btnSVG.style.cssText = 'margin-left: auto; margin-top: 5px;float: right;'; 297 promptContinueBtn.append(btnSVG); 298 } 299 // event of click: post the questionnaire to the API, get the answer from AI 300 promptContinueBtn.addEventListener("mousedown", async function (event) { 301 event.preventDefault(); 302 var parentWrapper = event.currentTarget.closest(".find-a-lawyer-widget"); 303 aiPromptSubmission(parentWrapper, formSubmitBtn) 304 }) 305 306 formSubmitBtn.classList.add("d-none"); 307 // Adding the prompt continue button to the page: Parent Node:.button-container 308 formSubmitBtn.parentNode.appendChild(promptContinueBtn); 309} 310 311async function aiPromptSubmission(parentWrapper, formSubmitBtn) { 312 313 // disable the button and wait for the result: 314 parentWrapper.querySelector("form #submit-to-prompt").disabled = true; 315 parentWrapper.querySelector("form #universal-form-click-here-ai").disabled = true; 316 317 //collect all answers: 318 // var name = parentWrapper.querySelector("[name=name]").value; 319 // var phone = parentWrapper.querySelector("[name=phone]").value; 320 // var email = parentWrapper.querySelector("[name=email]").value; 321 // var zip = parentWrapper.querySelector("[name=zip]").value; 322 // var caseDetails = parentWrapper.querySelector("[name=description]").value; 323 324 var form = parentWrapper.querySelector("form"); 325 var type = form.getAttribute("onsubmit").split(",")[1].replaceAll("'", "").replaceAll(" ", ""); 326 327 var validation = await validateForm(event, type, form); 328 329 if (validation == true) { 330 // Post information to the OpenAI 331 var reqHander = new Headers(); 332 reqHander.append("Content-Type", "application/json"); 333 reqHander.append("X-XSRF-TOKEN", decodeURIComponent(getCookie("XSRF-TOKEN"))); 334 335 // Looking for session storage for the information 336 var curUserTracking = JSON.parse(sessionStorage.getItem(userTrackKey)); 337 338 // handle the case where homepage and bottom uform can submit with just one name 339 var user_name = parentWrapper.querySelector("[name=name]").value.replace(/\s+/g, ' '); 340 341 // need to generate unique id ("ai_case_id") 342 window.ai_case_id = Math.floor(Math.random() * (1e13 + 1e12 +1)) + 1e12; 343 344 var raw_small = { 345 "zip": parentWrapper.querySelector("[name=zip]").value, 346 "description": parentWrapper.querySelector("[name=description]").value, 347 "prompt": "generic_questions", 348 "ai_case_id": window.ai_case_id, 349 "skip_prediction": true 350 } 351 352 var raw = { 353 "name": user_name, 354 "email": parentWrapper.querySelector("[name=email]").value, 355 "phone": parentWrapper.querySelector("[name=phone]").value, 356 "zip": parentWrapper.querySelector("[name=zip]").value, 357 "description": parentWrapper.querySelector("[name=description]").value, 358 "loc": parentWrapper.querySelector("[name=loc]").value, 359 "WebsiteID": "15-103-212-197-7-216-247-236", 360 "referer": document.referrer, 361 "prompt": "generic_questions",
362 'cache_lead': true, 363 "ai_case_id": window.ai_case_id, 364 "skip_prediction": true 365 }; 366 367 if (NCMS.pageType != 'front') { 368 raw["form_type"] = parentWrapper.querySelector("[name=form_type]").value 369 } 370 371 raw['sms_optin_status'] = parentWrapper.querySelector("[name=sms_optin_status]").value; 372 raw['tcpa_consent_verbiage'] = parentWrapper.querySelector("[name=tcpa_consent_verbiage]").value; 373 raw['trusted_form_id'] = parentWrapper.querySelector("[name=trusted_form_id]").value; 374 375 var body = JSON.stringify(raw); 376 377 window.promptSubmit = formSubmitBtn; 378 379 var requestOptions = { 380 method: 'POST', 381 headers: reqHander, 382 body: body, 383 redirect: 'follow' 384 }; 385 386 var requestOptionsSmall = { 387 method: 'POST', 388 headers: reqHander, 389 body: JSON.stringify(raw_small), 390 redirect: 'follow' 391 }; 392 393 if (parentWrapper.querySelector("[name=description]").value.length >= SPAM_CHAR_LIMIT ) { 394 var source; 395 if (parentWrapper.querySelector("[name=description]").value.length >= SPAM_CHAR_LIMIT) { 396 source = "long-desc"; 397 } else source = "error" 398 // console.log("parentWrapper -- ", parentWrapper); 399 submitToForm(parentWrapper, source); 400 } else { 401 $('#uform-universalFormPromptModal').modal('show'); 402 $('#bottom-right-black-bubble-invite-container').addClass("move-back"); 403 $('.ngageMobileFloat').addClass("move-back"); 404 document.getElementById(formPrefix + "prompt-form").setAttribute("disabled",true); 405 406 fetch('/uformMCQ', requestOptionsSmall) 407 .then(function (response) { 408 return response.json() 409 }) 410 .then(function (data) { 411 if (data && data.status == 200) { 412 var mcqQuestions = data.responseBody.questions; 413 if (showModal) { 414 document.getElementById(formPrefix + "loading").classList.add("d-none"); 415 // open modal with AI questions: 416 417 // create radio questions from tools GPT MCQ response 418 for (let i = 0; i < mcqQuestions.length; i++) { 419 // create single question container 420 var questionContainer = document.createElement('div'); 421 questionContainer.classList.add("ai-question-container"); 422 423 // create label and append ^ 424 var questionLabel = document.createElement('label'); 425 questionLabel.innerHTML = mcqQuestions[i].text; 426 questionLabel.classList.add('ai-radio-label'); 427 questionLabel.setAttribute('data-q-id', 'aiQuestion['+i+']'); 428 questionContainer.appendChild(questionLabel); 429 430 // create radioContainer (contains input + label) 431 var radioContainer = document.createElement('div'); 432 radioContainer.classList.add('ai-radio-container'); 433 434 // create radio buttons and append ^ 435 for (let j = 0; j < mcqQuestions[i].options.length; j++) { 436 var radioSelection = document.createElement('div'); 437 var radioInput = document.createElement('input'); 438 radioInput.setAttribute('type', 'radio'); 439 radioInput.setAttribute('class', 'radio-input'); 440 radioInput.setAttribute('id', 'aiQuestion['+i+']_'+j); 441 radioInput.setAttribute('name', 'aiQuestion['+i+']'); 442 443 // just use obj.answer 444 // try { 445 radioInput.setAttribute('value', mcqQuestions[i].options[j].answer); 446 // } catch (error) { 447 // console.log("using .answer", error); 448 // radioInput.setAttribute('value', mcqQuestions[i].options[j].answer); 449 // } 450 451 //styling class 452 radioInput.addEventListener('change', function(event) { 453 if (event.target.checked) { 454 document.querySelector("#uform-prompt-form").classList.add("interacted"); 455 document.querySelector("#uform-prompt-form").removeAttribute("disabled"); 456 } 457 }); 458 459 //now the label 460 var radioLabel = document.createElement('label'); 461 radioLabel.setAttribute('class', 'radio-label'); 462 radioLabel.setAttribute('for', 'aiQuestion['+i+']_'+j); 463 radioLabel.innerHTML = mcqQuestions[i].options[j].text; 464 465 radioSelection.appendChild(radioInput); 466 radioSelection.appendChild(radioLabel); 467 468 // append 469 radioContainer.appendChild(radioSelection); 470 } 471 questionContainer.appendChild(radioContainer); 472 473 //^^ append all that to (formPrefix + "chat-content") 474 document.getElementById(formPrefix + "chat-content").appendChild(questionContainer); 475 } 476 477 // document.getElementById(quizPrefix + "chat-content").innerText = completion; 478 document.getElementById(formPrefix + "loading").classList.add("d-none"); 479 document.querySelector("#uform-universalFormPromptModal .subtitle").classList.remove("d-none"); 480 document.getElementById(formPrefix + "chat-wrapper").classList.remove("d-none"); 481 482 window.parentWrapper = parentWrapper; 483 document.querySelector('.modal-backdrop').classList.add("darker") 484 } else { 485 // Show the question as the second step of form, hide all other answer: 486 showPromptStep(parentWrapper, completion); 487 } 488 window.modelOpen = "uform"; 489 triggerTrack("first_form_submit", false) 490 } else { 491 console.error('return empty ai response, submit nGage now') 492 submitToForm(window.parentWrapper, "error"); 493 } 494 }) 495 .catch(function (error) { 496 console.error('failed getting gpt qs', error); 497 // $('#uform-universalFormPromptModal').modal('hide'); 498 window.gptAbort = true; 499 submitToForm(parentWrapper, "error"); 500 }); 501 502 fetch('/uformQueue', requestOptions) 503 .then(function (response) { 504 return response.json() 505 }).then(function (data) { 506 parentWrapper.querySelector("[name=lead_id]").value = data.responseBody.lead_id; 507 requestOptions.body = JSON.stringify(raw); 508 }).catch(function (error) { 509 console.error(error) 510 }); 511 } 512 } else { 513 parentWrapper.querySelector("form #submit-to-prompt").removeAttribute("disabled") 514 } 515} 516 517//Modal Events 518function bindModalEvents() { 519 // document.querySelector("#" + formPrefix + "chat-wrapper textarea").addEventListener("input", function (event) { 520 // event.preventDefault(); 521 // if (event.target.value.length > 0 && (event.target.value.trim() !== '')) document.getElementById(formPrefix + "prompt-form").removeAttribute('disabled') 522 // else document.getElementById(formPrefix + "prompt-form").setAttribute('disabled', '') 523 // }) 524 525 // bind the Modal prompt-form button 526 document.getElementById(formPrefix + "prompt-form").addEventListener("mousedown", function (event) { 527 if (event.button == 0) { 528 event.preventDefault(); 529 // append the content to the form case description: 530 // document.querySelectorAll('.ai-radio-label').forEach(element => { 531 // var radioFor = element.getAttribute('for'); 532 // radioFor = radioFor.replace('[', '\\['); 533 // radioFor = radioFor.replace(']', '\\]'); 534 535 // var radioAnswer; 536 // try { 537 // radioAnswer = document.querySelector('#' + formPrefix + "chat-content input[name=" + radioFor + "]:checked").value; 538 // } catch (error) { 539 // radioAnswer = 'undefined'; 540 // } 541 // window.parentWrapper.querySelector("[name=description]").value += " \n\n " + element.innerText + ' ' + radioAnswer; 542 // }); 543 544 // hit real submission button: 545 submitToForm(window.parentWrapper, "submit"); 546 triggerTrack("ai_prompt_submit", false) 547 displayLoadingForSubmit(); 548 } 549 }); 550 551 // bind the Modal prompt-form-close button 552 document.getElementById(formPrefix + "prompt-form-close").addEventListener("mousedown", function (event) { 553 event.preventDefault(); 554 triggerTrack("ai_prompt_cancel_clicked", false) 555 // $('#uform-universalFormPromptModal').modal('hide'); 556 displayLoadingForSubmit(); 557 558 // hit real submission button: 559 submitToForm(window.parentWrapper, "close"); 560 }); 561 562 // bind the Modal close X icon button 563 document.getElementById(formPrefix + "close-modal-icon").addEventListener("mousedown", function (event) { 564 event.preventDefault(); 565 triggerTrack("ai_prompt_x_clicked", false) 566 // $('#uform-universalFormPromptModal').modal('hide'); 567 displayLoadingForSubmit(); 568 // hit real submission button: 569 submitToForm(window.parentWrapper, "x"); 570 }); 571} 572 573function displayLoadingForSubmit() { 574 document.querySelector("#uform-chat-wrapper").classList.add("d-none"); 575 document.querySelector("#uform-loading").classList.remove("d-none"); 576 document.querySelector("#uform-universalFormPromptModal .subtitle").classList.add("d-none"); 577} 578//# sourceMappingURL=universalFormPrompt.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.