Scripting Solutions
Additional scripting solutions will be added in the future. Please reach out to Alchemer with comments and suggestions on solutions you'd like to see via the link here.
Goal
Autofill answers on a survey page to speed up your survey testing.
Effort: ✔ ✔ ✔
Solution
Create a bookmark in Chrome that runs Javascript to intelligently fill in a survey page.
To add the Autofill tool:
(1) In Chrome, choose the three vertical dots in the upper right > Bookmarks > Bookmark Manager
(2) Choose the three dots in the upper right below the original three dots
(3) Choose Add new Bookmark.
(4) Enter the name Autofill.
(5) Paste the code below in green for the URL.
To use the Autofill Tool:
When on a survey page click the Autofill bookmark and watch the page fill in.
Autofill works with the following question types:
- checkbox (with optional min number of selections or optional write-in)
- checkbox grid (with optional min number of selections)
- conjoint
- continuous sum (with optional required total)
- essay
- dropdown
- dropdown menu grid
- dropdown menu list
- image multi select (but doesn't look at min number...yet)
- image select
- likert
- max diff
- NPS®
- radio button (with optional write-in)
- radio button grid
- ranking grid
- semantic diff
- slider (optional min/max and step)
- slider list (same as above)
- star rating grid
- textbox
- date (all three formats but not min/max date)
- numeric with min/max number or min/max number of characters
- currency
- percent
- phone (based on the regex option)
- textbox grid (same as above)
- textbox list (same as above)
- Custom Groups containing any of the above
Code to add as a bookmark (copy everything in green below and paste it in as a new Chrome bookmark):
javascript:%2F*%20Alchemer%20v03%0A%0A%20%20%20Autofill%20Chrome%20Bookmarklet%0A%0A%20%20%20Works%20with%20question%20types%3A%0A%20%20%20%20%20%20checkbox%20(with%20optional%20min%20number%20of%20selections%20or%20optional%20write-in)%0A%20%20%20%20%20%20checkbox%20grid%20(with%20optional%20min%20number%20of%20selections)%0A%20%20%20%20%20%20conjoint%0A%20%20%20%20%20%20continuous%20sum%20(with%20optional%20required%20total)%0A%20%20%20%20%20%20essay%0A%20%20%20%20%20%20dropdown%0A%20%20%20%20%20%20dropdown%20menu%20grid%0A%20%20%20%20%20%20dropdown%20menu%20list%0A%20%20%20%20%20%20image%20multi%20select%20(but%20doesn%27t%20look%20at%20min%20number...yet)%0A%20%20%20%20%20%20image%20select%0A%20%20%20%20%20%20likert%0A%20%20%20%20%20%20max%20diff%0A%20%20%20%20%20%20nps%0A%20%20%20%20%20%20radio%20button%20(with%20optional%20write-in)%0A%20%20%20%20%20%20radio%20button%20grid%0A%20%20%20%20%20%20ranking%20grid%0A%20%20%20%20%20%20semantic%20diff%0A%20%20%20%20%20%20slider%20(optional%20min%2Fmax%20and%20step)%0A%20%20%20%20%20%20slider%20list%20(same%20as%20above)%0A%20%20%20%20%20%20star%20rating%20grid%0A%20%20%20%20%20%20textbox%0A%20%20%20%20%20%20--%20email%0A%20%20%20%20%20%20--%20date%20(all%20three%20formats%20but%20not%20min%2Fmax%20date)%0A%20%20%20%20%20%20--%20numeric%20with%20min%2Fmax%20number%20or%20min%2Fmax%20number%20of%20characters%0A%20%20%20%20%20%20--%20currency%0A%20%20%20%20%20%20--%20percent%0A%20%20%20%20%20%20--%20phone%20(based%20on%20the%20regex%20option)%0A%20%20%20%20%20%20textbox%20grid%20(same%20as%20above)%0A%20%20%20%20%20%20textbox%20list%20(same%20as%20above)%0A%0A%20%20%20%20%20%20Custom%20Groups%20containing%20any%20of%20the%20above%0A%0A%20%20%20Not%20working%20for%0A%20%20%20%20%20%20actions%0A%20%20%20%20%20%20audo%20sentiment%0A%20%20%20%20%20%20cascading%20dropdown%0A%20%20%20%20%20%20custom%20table%0A%20%20%20%20%20%20drag%20and%20drop%20ranking%0A%20%20%20%20%20%20file%20upload%0A%20%20%20%20%20%20grouping%20open%20%2F%20closed%20card%20sort%0A%20%20%20%20%20%20image%20heatmap%0A%20%20%20%20%20%20text%20highlighter%0A%20%20%20%20%20%20quick%20sort%0A%20%20%20%20%20%20signature%0A%20%20%20%20%20%20video%20feedback%0A%20%20%20%20%20%20video%20sentiment%0A%0A*%2F%0A%0A(function()%20%7B%0Aconst%20LOG%20%3D%20true%0Aconst%20BOOKMARKLET%20%3D%20true%20%2F%2F%20true%20when%20setting%20up%20as%20bookmarlet%2C%20false%20to%20run%20in%20the%20survey%20Style%20%3E%20HEADER%0A%0A%2F**%0A%20*%20Returns%20a%20random%20integer%20between%20min%20(inclusive)%20and%20max%20(inclusive).%0A%20*%20https%3A%2F%2Fstackoverflow.com%2Fquestions%2F1527803%2Fgenerating-random-whole-numbers-in-javascript-in-a-specific-range%0A%20*%2F%0Afunction%20getRandomInt(min%2C%20max)%20%7B%0A%20%20%20%20min%20%3D%20Math.ceil(min)%3B%0A%20%20%20%20max%20%3D%20Math.floor(max)%3B%0A%20%20%20%20return%20Math.floor(Math.random()%20*%20(max%20-%20min%20%2B%201))%20%2B%20min%3B%0A%7D%0A%0A%2F***%0A%20*%20Shuffle%20array%20in%20place%0A%20*%20Knuth%20shuffle%3A%20https%3A%2F%2Fstackoverflow.com%2Fquestions%2F2450954%2Fhow-to-randomize-shuffle-a-javascript-array%0A%20*%0A%20*%20array%20(array)%20will%20be%20mutated%0A%20*%20return%20(array)%20the%20original%20array%20after%20being%20shuffled%0A%20*%2F%0Afunction%20shuffle(array)%20%7B%0A%20%20var%20currentIndex%20%3D%20array.length%2C%20temporaryValue%2C%20randomIndex%3B%0A%0A%20%20%2F%2F%20While%20there%20remain%20elements%20to%20shuffle...%0A%20%20while%20(0%20!%3D%3D%20currentIndex)%20%7B%0A%0A%20%20%20%20%2F%2F%20Pick%20a%20remaining%20element...%0A%20%20%20%20randomIndex%20%3D%20Math.floor(Math.random()%20*%20currentIndex)%3B%0A%20%20%20%20currentIndex%20-%3D%201%3B%0A%0A%20%20%20%20%2F%2F%20And%20swap%20it%20with%20the%20current%20element.%0A%20%20%20%20temporaryValue%20%3D%20array%5BcurrentIndex%5D%3B%0A%20%20%20%20array%5BcurrentIndex%5D%20%3D%20array%5BrandomIndex%5D%3B%0A%20%20%20%20array%5BrandomIndex%5D%20%3D%20temporaryValue%3B%0A%20%20%7D%0A%0A%20%20return%20array%3B%0A%7D%0A%0A%2F**%0A%20*%20Parse%20an%20Alchemer%20element%20%23ID%20in%20the%20form%3A%0A%20*%20%20%20%20%20sgE-5901811-28-305-box%0A%20*%20%20%20%20%20sgE-5901811-28-305-10997-element%0A%20*%20return%20(obj)%20Returns%20object%20of%20the%20constituent%20parts%0A%20*%2F%0Aconst%20parseSgId%20%3D%20(id)%20%3D%3E%20%7B%0A%20%20const%20regexID%20%3D%20%2FsgE-(%5Cd%2B)-(%5Cd%2B)-(%5Cd%2B)(-(%5Cd%2B))%3F-(%5Cw%2B)%2F%0A%0A%20%20const%20aParsed%20%3D%20id.match(regexID)%0A%0A%20%20if%20(!aParsed%20%7C%7C%20aParsed.length%20!%3D%3D%207)%20%7B%0A%20%20%20%20alert(%27Javascript%20error%20parsing%20ID%20%3D%20%27%2C%20id)%0A%20%20%7D%0A%0A%20%20return%20%7B%0A%20%20%20%20sid%3A%20aParsed%5B1%5D%2C%20%2F%2F%20ex%3A%20%275901811%27%0A%20%20%20%20pid%3A%20aParsed%5B2%5D%2C%20%2F%2F%20ex%3A%20%2728%27%0A%20%20%20%20qid%3A%20aParsed%5B3%5D%2C%20%2F%2F%20ex%3A%20%27305%27%0A%20%20%20%20oid%3A%20aParsed%5B5%5D%2C%20%2F%2F%20ex%3A%20%2710997%27%20or%20undefined%20if%20this%20isn%E2%80%99t%20an%20ID%20for%20an%20option%0A%20%20%20%20type%3A%20aParsed%5B6%5D%20%2F%2F%20ex%3A%20%27box%27%20%2F%20%27element%27%0A%20%20%7D%0A%7D%0A%0A%2F***%0A%20*%20Get%20the%20survey%27s%20SGAPI%20variable%0A%20*%0A%20*%20return%20(obj)%20the%20SGAPI%20global%20vraible%0A%20*%2F%0Aconst%20getSGAPI%20%3D%20()%20%3D%3E%20%7B%0A%20%20%2F%2F%20Preview%20is%20in%20an%20iFrame%0A%20%20const%20iFrameElem%20%3D%20document.querySelector(%27iframe%23preview-the-page%27)%0A%20%20if%20(iFrameElem)%0A%20%20%20%20return%20iFrameElem.contentWindow.SGAPI%0A%20%20return%20sgapi%20%3D%20SGAPI%0A%7D%0A%0A%2F***%0A%20*%20Get%20the%20survey%27s%20document%20element%0A%20*%0A%20*%20return%20(elem)%0A%20*%2F%0Aconst%20getDocument%20%3D%20()%20%3D%3E%20%7B%0A%20%20%2F%2F%20Preview%20is%20in%20an%20iFrame%0A%20%20const%20iFrameElem%20%3D%20document.querySelector(%27iframe%23preview-the-page%27)%0A%20%20if%20(iFrameElem)%0A%20%20%20%20return%20iFrameElem.contentDocument%20%7C%7C%20iFrameElem.contentWindow.document%0A%20%20return%20document%0A%7D%0A%0A%2F***%0A%20*%20Get%20a%20property%20from%20SGAPI%20for%20the%20qid%0A%20*%0A%20*%20qid%20(int)%20question%20ID%0A%20*%20propertyName%20(string)%20name%20of%20property%0A%20*%20isInt%20(t%2Ff)%20convert%20return%20value%20to%20int%20if%20true%0A%20*%20return%20(int%2Fstring)%20the%20property%20or%200%20%2F%20%27%27%20if%20property%20doesn%27t%20exist%0A%20*%2F%0Aconst%20getProperty%20%3D%20(qid%2C%20propertyName%2C%20isInt)%20%3D%3E%20%7B%0A%20%20const%20val%20%3D%20getSGAPI().survey.surveyObject.questions%5Bqid%5D.properties%5BpropertyName%5D%0A%20%20if%20(isInt)%0A%20%20%20%20return%20parseInt(val)%20%7C%7C%200%0A%20%20return%20val%20%7C%7C%20%27%27%0A%20%7D%0A%0A%2F***%0A%20*%20autofill%20dropdowns%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20dropdowns%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20let%20autoFilled%20%3D%20false%0A%20%20const%20selectElems%20%3D%20questionElem.querySelectorAll(%27select%27)%0A%20%20selectElems.forEach(selectElem%20%3D%3E%20%7B%0A%20%20%20%20if%20(LOG)%20console.log(%22selectElem.value%20%3D%20%22%2C%20selectElem.value)%0A%20%20%20%20%2F%2F%20Don%27t%20change%20if%20dropdown%20already%20has%20a%20value%0A%20%20%20%20%2F%2F%20%20%20%27NoAnswer%27%20for%20dropdown%20and%20dropdown%20menu%20list%2C%20%27%27%20for%20dropdown%20menu%20grid%0A%20%20%20%20if%20(selectElem.value%20%3D%3D%3D%20%27NoAnswer%27%20%7C%7C%20selectElem.value%20%3D%3D%3D%20%27%27)%20%7B%0A%20%20%20%20%20%20autoFilled%20%3D%20true%0A%20%20%20%20%20%20const%20numOptions%20%3D%20selectElem.querySelectorAll(%27option%27).length%0A%20%20%20%20%20%20selectElem.selectedIndex%20%3D%20getRandomInt(1%2C%20numOptions%20-%201)%20%2F%2F%201%20to%20skip%20past%20%22--%20Please%20Select%20--%22%2C%20and%20-1%20since%20this%20is%200-based%0A%20%20%20%20%7D%0A%20%20%7D)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20star%20rating%20grid%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20starRatingGrid%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20let%20autoFilled%20%3D%20false%0A%20%20questionElem.querySelectorAll(%27tbody%20td%27).forEach(tdElem%20%3D%3E%20%7B%0A%20%20%20%20if%20(!tdElem.querySelector(%27input%3Achecked%27))%20%7B%0A%20%20%20%20%20%20autoFilled%20%3D%20true%0A%20%20%20%20%20%20const%20labelElems%20%3D%20tdElem.querySelectorAll(%27label%27)%0A%20%20%20%20%20%20const%20random%20%3D%20getRandomInt(1%2C%20labelElems.length%20-%201)%20%2F%2F%201%20to%20skip%20the%20initial%20X%2C%20-1%20since%20it%27s%20zero-based%0A%20%20%20%20%20%20if%20(LOG)%20console.log(%22--%20selecting%20stars%20%3D%20%22%2C%20random)%0A%20%20%20%20%20%20for%20(let%20i%20%3D%201%3B%20i%20%3C%3D%20random%3B%20i%2B%2B)%0A%20%20%20%20%20%20%20%20labelElems%5Bi%5D.classList.add(%27sg-star-on%27)%0A%20%20%20%20%20%20labelElems%5Brandom%5D.querySelector(%27input%27).checked%20%3D%20true%0A%20%20%20%20%7D%0A%20%20%7D)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20continuous%20sum%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20continuousSum%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20const%20inputElems%20%3D%20questionElem.querySelectorAll(%27tbody%20input%5Btype%3Dtext%5D%27)%0A%0A%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20inputElems.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20if%20(inputElems%5Bi%5D.value%20!%3D%3D%20%27%27)%0A%20%20%20%20%20%20return%20false%0A%20%20%7D%0A%0A%20%20const%20qid%20%3D%20parseSgId(questionElem.id).qid%0A%20%20%2F%2F%20const%20maxTotal%20%3D%20parseInt(SGAPI.survey.surveyObject.questions%5Bqid%5D.properties.max_total)%20%7C%7C%200%0A%20%20const%20maxTotal%20%3D%20getProperty(qid%2C%20%27max_total%27%2C%20true)%0A%20%20if%20(maxTotal)%20%7B%0A%20%20%20%20const%20val%20%3D%20Math.floor(maxTotal%20%2F%20inputElems.length)%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20inputElems.length%20-%201%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20inputElems%5Bi%5D.value%20%3D%20val%0A%20%20%20%20%7D%0A%20%20%20%20inputElems%5BinputElems.length%20-%201%5D.value%20%3D%20maxTotal%20-%20(val%20*%20(inputElems.length%20-%201))%0A%20%20%7D%0A%20%20else%20%7B%0A%20%20%20%20inputElems.forEach(inputElem%20%3D%3E%20inputElem.value%20%3D%20getRandomInt(0%2C10))%0A%20%20%7D%0A%0A%20%20%2F%2F%20update%20the%20total%0A%20%20inputElems%5B0%5D.focus()%0A%20%20inputElems%5B0%5D.blur()%0A%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20slider%0A%20*%0A%20*%20questionElem%20(element)%20can%20be%20a%20question%20or%20a%20sliderRowElem%20for%20a%20slider%20list%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20slider%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20%2F***%0A%20%20%20*%20Get%20a%20random%20value%20and%20percent%0A%20%20%20*%0A%20%20%20*%20return%20(obj%20of%20int)%20%7B%20randomValue%2C%20randomPercent%20%7D%0A%20%20%20*%2F%0A%20%20const%20getRandomValueAndPercent%20%3D%20()%20%3D%3E%20%7B%0A%0A%20%20%20%20const%20setupObj%20%3D%20JSON.parse(questionElem.querySelector(%27.slider-setup%27).value)%0A%20%20%20%20if%20(LOG)%20console.log(%22setupObj%20%3D%20%22%2C%20setupObj)%0A%20%20%20%20const%20steps%20%3D%20Math.floor((setupObj.max%20-%20setupObj.min)%20%2F%20setupObj.stepval)%0A%20%20%20%20const%20randomValue%20%3D%20setupObj.min%20%2B%20getRandomInt(0%2C%20steps)%20*%20setupObj.stepval%0A%20%20%20%20const%20randomPercent%20%3D%20Math.floor(((randomValue%20-%20setupObj.min)%20%2F%20(setupObj.max%20-%20setupObj.min))%20*%20100)%0A%20%20%20%20if%20(LOG)%20console.log(%22random%20val%20%2F%20percent%20%3D%20%22%2C%20randomValue%2C%20%27%20%2F%20%27%2C%20randomPercent%2C%20%27%25%27)%0A%0A%20%20%20%20return%20%7B%20randomValue%2C%20randomPercent%20%7D%0A%20%20%7D%0A%0A%20%20%2F***%0A%20%20%20*%20main()%0A%20%20%20*%2F%0A%0A%20%20%2F%2F%20already%20set%2C%20don%27t%20change%0A%20%20if%20(questionElem.querySelector(%27input.sg-input%27).value)%0A%20%20%20%20return%20false%0A%0A%20%20const%20%7B%20randomValue%2C%0A%20%20%20%20%20%20%20%20%20%20randomPercent%20%7D%20%3D%20getRandomValueAndPercent()%0A%0A%20%20%2F%2F%20set%20slider%20value%0A%20%20questionElem.querySelector(%27input.sg-input%27).value%20%3D%20randomValue%0A%0A%20%20%2F%2F%20set%20the%20slider%20handle%2C%20this%20must%20be%20on%20a%20timer%20b%2Fc%20of%20how%20the%20slider%20functions%0A%20%20const%20sliderHandleElem%20%3D%20questionElem.querySelector(%27.ui-slider-handle%27)%0A%20%20setTimeout(function%20()%20%7B%20sliderHandleElem.style.left%20%3D%20%60%24%7BrandomPercent%7D%25%60%20%7D%2C%20400)%0A%20%20setTimeout(function%20()%20%7B%20sliderHandleElem.style.left%20%3D%20%60%24%7BrandomPercent%7D%25%60%20%7D%2C%201000)%20%2F%2F%20ensure%20it%20worked!%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20sliderList%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20sliderList%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20let%20autoFilled%20%3D%20false%0A%20%20questionElem.querySelectorAll(%27.sg-slider-row%27).forEach(sliderRowElem%20%3D%3E%0A%20%20%20%20autoFilled%20%3D%20slider(sliderRowElem)%20%7C%7C%20autoFilled)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20textboxes%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20textboxes%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20const%20qid%20%3D%20parseSgId(questionElem.id).qid%0A%0A%20%20%2F***%0A%20%20%20*%20Get%20the%20validation%20parameters%20for%20min%2Fmax%20charachter%20count.%20or%200%20if%20not%20set%0A%20%20%20*%0A%20%20%20*%20return%20(obj%20of%20ints)%20%7B%20minCharacters%2C%20maxCharacters%20%7D%0A%20%20%20*%2F%0A%20%20const%20getMinMaxCharacters%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20%2F%2Fconst%20qid%20%3D%20parseSgId(questionElem.id).qid%0A%20%20%20%20const%20minCharacters%20%3D%20getProperty(qid%2C%20%27min_characters%27%2C%20true)%0A%20%20%20%20const%20maxCharacters%20%3D%20getProperty(qid%2C%20%27max_characters%27%2C%20true)%0A%0A%20%20%20%20if%20(LOG)%20console.log(%22textboxes()%2C%20qid%20%3D%20%22%2C%20qid)%0A%20%20%20%20if%20(LOG)%20console.log(%22-%20min%2Fmax%20chars%20%3D%20%22%2C%20minCharacters%2C%20%27%20%2F%20%27%2C%20maxCharacters)%0A%20%20%20%20return%20%7B%20minCharacters%2C%20maxCharacters%20%7D%0A%20%20%7D%0A%0A%20%20%2F***%0A%20%20%20*%20Get%20the%20validation%20parameters%20for%20min%2Fmax%20number%2C%20or%200%20if%20not%20set%0A%20%20%20*%0A%20%20%20*%20return%20(obj%20of%20ints)%20%7B%20minNumber%2C%20maxNumber%20%7D%0A%20%20%20*%2F%0A%20%20const%20getMinMaxNumber%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20%2F%2Fconst%20qid%20%3D%20parseSgId(questionElem.id).qid%0A%20%20%20%20const%20minNumber%20%3D%20getProperty(qid%2C%20%27min_number%27%2C%20true)%0A%20%20%20%20const%20maxNumber%20%3D%20getProperty(qid%2C%20%27max_number%27%2C%20true)%0A%0A%20%20%20%20if%20(LOG)%20console.log(%22textboxes()%2C%20qid%20%3D%20%22%2C%20qid)%0A%20%20%20%20if%20(LOG)%20console.log(%22-%20min%2Fmax%20number%20%3D%20%22%2C%20minNumber%2C%20%27%20%2F%20%27%2C%20maxNumber)%0A%20%20%20%20return%20%7B%20minNumber%2C%20maxNumber%20%7D%0A%20%20%7D%0A%0A%20%20%2F***%0A%20%20%20*%20Get%20the%20input%20mask%20(regex%20validation)%0A%20%20%20*%0A%20%20%20*%20reutrn%20(string)%20the%20question%20property%20inputmask.MASK%20or%20empty%20string%0A%20%20*%2F%0A%20%20const%20getInputMask%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20const%20inputMask%20%3D%20getProperty(qid%2C%20%27inputmask%27%2C%20false)%0A%20%20%20%20const%20retval%20%3D%20(inputMask)%20%3F%20inputMask.MASK%20%3A%20%27%27%20%0A%20%20%20%20if%20(LOG)%20console.log(%22getInputMask()%20%3D%20%22%2C%20retval)%0A%20%20%20%20return%20retval%0A%20%20%7D%0A%0A%20%20%2F***%0A%20%20%20*%20main()%0A%20%20%20*%2F%0A%0A%20%20let%20autoFilled%20%3D%20false%0A%0A%20%20const%20%7B%20minCharacters%2C%0A%20%20%20%20%20%20%20%20%20%20maxCharacters%20%7D%20%3D%20getMinMaxCharacters()%0A%0A%20%20let%20%7B%20minNumber%2C%0A%20%20%20%20%20%20%20%20maxNumber%20%7D%20%3D%20getMinMaxNumber()%0A%20%20minNumber%20%3D%20Math.ceil(minNumber)%0A%20%20maxNumber%20%3D%20Math.floor(maxNumber)%0A%0A%20%20const%20inputElems%20%3D%20questionElem.querySelectorAll(%27input%5Btype%3Dtext%5D%27)%0A%20%20inputElems.forEach(inputElem%20%3D%3E%20%7B%0A%0A%20%20%20%20%2F%2F%20only%20fill%20if%20there%27s%20no%20value%0A%20%20%20%20if%20(!inputElem.value)%20%7B%0A%0A%20%20%20%20%20%20autoFilled%20%3D%20true%0A%0A%20%20%20%20%20%20const%20classList%20%3D%20inputElem.classList%0A%0A%20%20%20%20%20%20%2F%2F%20EMAIL%0A%20%20%20%20%20%20if%20(classList.contains(%27sg-validation-email%27))%0A%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%27test%40test.com%27%0A%0A%20%20%20%20%20%20%2F%2F%20DATE%0A%20%20%20%20%20%20else%20if%20(classList.contains(%27sg-validation-date%27))%20%7B%0A%20%20%20%20%20%20%20%20if%20(classList.contains(%27sg-validation-date-yyyy%27))%20%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%272025%2F01%2F01%27%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%2701%2F01%2F2025%27%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20NUMERIC%0A%20%20%20%20%20%20else%20if%20(%20%20%20classList.contains(%27sg-validation-numeric%27)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20classList.contains(%27sg-validation-percent%27)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20classList.contains(%27sg-validation-currency%27)%20)%20%7B%0A%20%20%20%20%20%20%20%20if%20(minNumber%20%26%26%20maxNumber)%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20getRandomInt(minNumber%2C%20maxNumber)%0A%20%20%20%20%20%20%20%20else%20if%20(minNumber)%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20getRandomInt(minNumber%2C%20minNumber%20%2B%2020)%0A%20%20%20%20%20%20%20%20else%20if%20(maxNumber)%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20getRandomInt(0%2C%20maxNumber)%0A%20%20%20%20%20%20%20%20else%20if%20(minCharacters)%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%271%27.repeat(minCharacters)%0A%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%27123%27.slice(0%2C%20maxCharacters%20%7C%203)%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20US%20PHONE%20(from%20the%20Alchemer%20regex%20for%20a%20US%20Phone%2C%20note%3A%20the%20backslashes%20are%20escaped%20so%20they%20appear%20doubled)%0A%20%20%20%20%20%20else%20if%20(getInputMask()%20%3D%3D%3D%20%22%5E((%5C%5C(%5C%5Cd%7B3%7D%5C%5C)%20%3F)%7C(%5C%5Cd%7B3%7D%5B-%5C%5Cs%5D))%3F%5C%5Cd%7B3%7D%5B-%5C%5Cs%5D%5C%5Cd%7B4%7D%24%22)%20%7B%0A%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%27123-456-7890%27%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20OTHERWISE%2C%20normal%20text%0A%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20if%20(LOG)%20console.log(%22otherwise%2C%20normal%20text%22)%0A%20%20%20%20%20%20%20%20if%20(minCharacters)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(minCharacters%20%3D%3D%3D%205)%20%2F%2F%20special%20case%20for%20zip%0A%20%20%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%2712345%27%0A%20%20%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%27x%27.repeat(minCharacters)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20else%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20inputElem.value%20%3D%20%27test%27.slice(0%2C%20maxCharacters%20%7C%204)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20fire%20display%20logic%20on%20later%20questions%0A%20%20%20%20%20%20inputElem.focus()%0A%20%20%20%20%20%20inputElem.blur()%0A%20%20%20%20%7D%0A%20%20%7D)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20essay%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20essay%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20const%20textareaElem%20%3D%20questionElem.querySelector(%27textarea%27)%0A%0A%20%20%2F%2F%20if%20already%20has%20a%20value%2C%20do%20nothing%0A%20%20if%20(textareaElem.value)%0A%20%20%20%20return%20false%0A%0A%20%20textareaElem.value%20%3D%20%27test%27%0A%0A%20%20%2F%2F%20fire%20display%20logic%20on%20later%20questions%0A%20%20textareaElem.focus()%0A%20%20textareaElem.blur()%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20radio%20button%0A%20*%0A%20*%20questionElem%20(element)%20question%20or%20other%20elem%20type%20for%20radio%20button%20grid%20or%20conjoint%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20radioButton%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20if%20(LOG)%20console.log(%22radioButton%20%3D%20%22%2C%20questionElem)%0A%20%20%2F%2F%20if%20already%20selected%2C%20do%20nothing%0A%20%20if%20(questionElem.querySelectorAll(%27input%5Btype%3Dradio%5D%3Achecked%27).length)%0A%20%20%20%20return%20false%0A%0A%20%20const%20radioElems%20%3D%20questionElem.querySelectorAll(%27input%5Btype%3Dradio%5D%27)%0A%0A%20%20const%20radioElem%20%3D%20radioElems%5BgetRandomInt(0%2C%20radioElems.length%20-%201)%5D%0A%20%20if%20(LOG)%20console.log(%22clicking%20%22%2C%20radioElem)%0A%0A%20%20%2F%2F%20fire%20display%20logic%20on%20later%20questions%0A%20%20radioElem.click()%0A%0A%20%20%2F%2F%20check%20for%20Other%20Write%20In%0A%20%20if%20(radioElem.parentElement.classList.contains(%27sg-other-li%27))%0A%20%20%20%20radioElem.parentElement.querySelector(%27input%5Btype%3Dtext%5D%27).value%20%3D%20%27write-in%27%0A%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20radio%20button%20grid%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20radioButtonGrid%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20let%20autoFilled%20%3D%20false%0A%20%20const%20trElems%20%3D%20questionElem.querySelectorAll(%27tbody%20tr%27)%0A%20%20trElems.forEach(trElem%20%3D%3E%0A%20%20%20%20autoFilled%20%3D%20radioButton(trElem)%20%7C%7C%20autoFilled)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20image%20select%20and%20image%20multi%20select%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20imageSelect%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20if%20(questionElem.querySelector(%27.sg-image-selected%27))%0A%20%20%20%20return%20false%0A%0A%20%20const%20imageSelectElems%20%3D%20questionElem.querySelectorAll(%27.sg-image-box%20label%27)%0A%20%20const%20imageSelectElem%20%3D%20imageSelectElems%5BgetRandomInt(0%2C%20imageSelectElems.length%20-%201)%5D%0A%20%20if%20(LOG)%20console.log(%22clicking%20%22%2C%20imageSelectElem)%0A%0A%20%20%2F%2F%20fire%20display%20logic%20on%20later%20questions%0A%20%20imageSelectElem.click()%0A%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20conjoint%20--%20all%20pages%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20conjoint%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20if%20(questionElem.querySelector(%27input%5Btype%3Dradio%5D%3Achecked%27))%0A%20%20%20%20return%20false%0A%0A%20%20const%20conjointSetElems%20%3D%20questionElem.querySelectorAll(%27.sg-conjoint-set%27)%0A%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20conjointSetElems.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20if%20(LOG)%20console.log(%22%5Cnconjoint%20set%20%3D%20%22%2C%20conjointSetElems%5Bi%5D)%0A%20%20%20%20radioButton(conjointSetElems%5Bi%5D)%0A%0A%20%20%20%20if%20(i%20!%3D%3D%20conjointSetElems.length%20-%201)%0A%20%20%20%20%20%20document.querySelector(%27.sg-next-button.btn-conjoint%27).click()%0A%20%20%7D%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20max%20diff%20--%20all%20pages%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20maxDiff%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20if%20(questionElem.querySelectorAll(%27input%5Btype%3Dradio%5D%3Achecked%27).length)%0A%20%20%20%20return%20false%0A%0A%20%20const%20maxDiffSetElems%20%3D%20questionElem.querySelectorAll(%27.sg-maxdiff-set%27)%0A%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20maxDiffSetElems.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20if%20(LOG)%20console.log(%22%5CmaxDiff%20set%20%3D%20%22%2C%20maxDiffSetElems%5Bi%5D)%0A%0A%20%20%20%20const%20trElems%20%3D%20shuffle(%5B...maxDiffSetElems%5Bi%5D.querySelectorAll(%27tbody%20tr%27)%5D)%0A%20%20%20%20trElems%5B0%5D.querySelectorAll(%27input%5Btype%3Dradio%5D%27)%5B0%5D.click()%0A%20%20%20%20trElems%5B1%5D.querySelectorAll(%27input%5Btype%3Dradio%5D%27)%5B1%5D.click()%0A%0A%20%20%20%20if%20(i%20!%3D%3D%20maxDiffSetElems.length%20-%201)%0A%20%20%20%20%20%20document.querySelector(%27.sg-next-button%27).click()%0A%20%20%7D%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20ranking%20grid%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20rankingGrid%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%0A%20%20if%20(questionElem.querySelectorAll(%27tbody%20tr%20input%5Btype%3Dradio%5D%3Achecked%27).length)%0A%20%20%20%20return%20false%0A%0A%20%20const%20trElems%20%3D%20questionElem.querySelectorAll(%27tbody%20tr%27)%0A%0A%20%20%2F%2F%20get%20a%20randomized%20array%20of%20ints%20%5B0..trElems.length-1%5D%0A%20%20let%20aRanking%20%3D%20%5B%5D%0A%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20trElems.length%3B%20i%2B%2B)%0A%20%20%20%20aRanking.push(i)%0A%20%20aRanking%20%3D%20shuffle(aRanking)%0A%0A%20%20trElems.forEach((trElem%2C%20idx)%20%3D%3E%20%7B%0A%20%20%20%20const%20inputElems%20%3D%20trElem.querySelectorAll(%27input%5Btype%3Dradio%5D%27)%0A%20%20%20%20inputElems%5BaRanking%5Bidx%5D%5D.click()%0A%20%20%7D)%0A%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20checkbox%0A%20*%0A%20*%20checkboxElem%20(element)%20a%20question%20for%20a%20checkbox%20OR%20a%20TR%20for%20a%20checkbox%20grid%20row%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20checkbox%20%3D%20(questionElem%2C%20isCheckboxGridRow%20%3D%20false)%20%3D%3E%20%7B%0A%0A%20%20console.log(%22checkbox()%20questionElem%20%3D%20%22%2C%20questionElem)%0A%0A%20%20%2F%2F%20if%20already%20checked%2C%20do%20nothing%0A%20%20if%20(questionElem.querySelectorAll(%27input%5Btype%3Dcheckbox%5D%3Achecked%27).length)%0A%20%20%20%20return%20false%0A%0A%20%20%2F%2F%20checkboxes%0A%20%20const%20checkElems%20%3D%20questionElem.querySelectorAll(%27input%5Btype%3Dcheckbox%5D%27)%0A%0A%20%20%2F%2F%20the%20minimum%20number%20of%20checks%20based%20on%20the%20Validation%20for%20the%20checkbox%20question%20or%20checkbox%20grid%0A%20%20let%20minChecks%20%3D%20undefined%0A%20%20if%20(isCheckboxGridRow)%20%7B%0A%20%20%20%20%2F%2F%20checkbox%20grid%20TRs%20have%20a%20class%20name%20in%20the%20form%20%27row-12%27%2C%20where%2012%20is%20the%20QID%0A%20%20%20%20%2F%2Fconst%20row_qid%20%3D%20%5B...questionElem.classList%5D.find(s%20%3D%3E%20s.slice(0%2C%204)%20%3D%3D%3D%20%27row-%27)%0A%20%20%20%20const%20row_qid%20%3D%20%5B...questionElem.classList%5D.find(s%20%3D%3E%20s.startsWith(%27row-%27))%0A%20%20%20%20const%20qid%20%3D%20parseInt(row_qid.slice(4))%0A%20%20%20%20minChecks%20%3D%20getProperty(qid%2C%20%27min_answers_per_row%27%2C%20true)%0A%20%20%7D%0A%20%20else%20%7B%0A%20%20%20%20const%20qid%20%3D%20parseSgId(questionElem.id).qid%0A%20%20%20%20minChecks%20%3D%20getProperty(qid%2C%20%27minimum_response%27%2C%20true)%0A%20%20%7D%0A%20%20minChecks%20%3D%20Math.min(minChecks%2C%20checkElems.length)%20%7C%7C%201%0A%20%20console.log(%22minChecks%20%3D%20%22%2C%20minChecks)%0A%0A%20%20%2F%2F%20check%20the%20min%20number%20of%20checkboxes%20and%20fire%20display%20logic%20on%20later%20questions%0A%20%20let%20checked%20%3D%200%0A%20%20while%20(checked%20%3C%20minChecks)%20%7B%0A%20%20%20%20const%20random%20%3D%20getRandomInt(0%2C%20checkElems.length%20-%201)%0A%20%20%20%20if%20(!checkElems%5Brandom%5D.checked)%20%7B%0A%20%20%20%20%20%20const%20checkElem%20%3D%20checkElems%5Brandom%5D%0A%20%20%20%20%20%20checkElem.click()%0A%20%20%20%20%20%20checked%2B%2B%0A%0A%20%20%20%20%20%20%2F%2F%20check%20for%20Other%20Write%20In%0A%20%20%20%20%20%20if%20(checkElem.parentElement.classList.contains(%27sg-other-li%27))%0A%20%20%20%20%20%20%20%20checkElem.parentElement.querySelector(%27input%5Btype%3Dtext%5D%27).value%20%3D%20%27write-in%27%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20return%20true%0A%7D%0A%0A%2F***%0A%20*%20autofill%20checkbox%20grid%0A%20*%0A%20*%20questionElem%20(element)%0A%20*%20return%20(t%2Ff)%20true%20if%20auto-filled%2C%20false%20if%20there%20was%20already%20a%20value%0A%20*%2F%0Aconst%20checkboxGrid%20%3D%20(questionElem)%20%3D%3E%20%7B%0A%20%20let%20autoFilled%20%3D%20false%0A%20%20const%20trElems%20%3D%20questionElem.querySelectorAll(%27tbody%20tr%27)%0A%20%20trElems.forEach(trElem%20%3D%3E%0A%20%20%20%20autoFilled%20%3D%20checkbox(trElem%2C%20true)%20%7C%7C%20autoFilled)%0A%20%20return%20autoFilled%0A%7D%0A%0A%2F***%0A%20*%20autofill%20the%20page%0A%20*%0A%20*%20This%20function%20uses%20a%20Timeout%20to%20recurse.%20%20The%20Timeout%20allows%20the%20survey%27s%0A%20*%20display%20logic%20engine%20to%20run%20and%20we%20go%20through%20the%20questions%20again%20to%0A%20*%20fill%20any%20new%20ones%20that%20were%20displayed.%0A%20*%0A%20*%20questionElems%20(arr%20of%20elems)%20all%20question%20on%20the%20page%20including%20hidden%0A%20*%2F%0Aconst%20autofill%20%3D%20(questionElems)%20%3D%3E%20%7B%0A%0A%20%20let%20autoFilledAnyQuestion%20%3D%20false%0A%0A%20%20questionElems.forEach(questionElem%20%3D%3E%20%7B%0A%0A%20%20%20%20if%20(LOG)%20console.log(%22%5Cn---------------------%5CnquestionElem%20%3D%20%22%2C%20questionElem)%0A%0A%20%20%20%20let%20autoFilled%20%3D%20false%0A%0A%20%20%20%20if%20(!questionElem.classList.contains(%27sg-hide%27))%20%7B%0A%20%20%20%20%20%20if%20(LOG)%20console.log(%22--%20autopopulating%3A%20%22%2C%20questionElem.id)%0A%0A%20%20%20%20%20%20%2F%2F%20CHECKBOX%0A%20%20%20%20%20%20if%20(questionElem.classList.contains(%27sg-type-checkbox%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20checkbox(questionElem)%0A%20%20%20%20%20%20%2F%2F%20CHECKBOX%20GRID%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-table-checkbox%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20checkboxGrid(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20CONJOINT%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-conjoint_new%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20conjoint(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20CONTINUOUS%20SUM%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-continuous-sum%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20continuousSum(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20DROPDOWN%0A%20%20%20%20%20%20%2F%2F%20DROPDOWN%20MENU%20LIST%0A%20%20%20%20%20%20%2F%2F%20DROPDOWN%20MENU%20GRID%0A%20%20%20%20%20%20else%20if%20(%20%20%20questionElem.classList.contains(%27sg-type-menu%27)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20questionElem.classList.contains(%27sg-type-multimenu%27)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20questionElem.classList.contains(%27sg-type-table-menu-matrix%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20dropdowns(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20ESSAY%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-essay%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20essay(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20IMAGE%20SELECT%0A%20%20%20%20%20%20%2F%2F%20IMAGE%20MULTI%20SELECT%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-imageselect%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20imageSelect(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20MAX%20DIFF%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-maxdiff%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20maxDiff(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20RADIO%20BUTTON%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-radio%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20radioButton(questionElem)%0A%20%20%20%20%20%20%2F%2F%20RADIO%20BUTTON%20GRID%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-table-radio%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20radioButtonGrid(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20RANKING%20GRID%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-rank-table%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20rankingGrid(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20SEMANTIC%20DIFF%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-table-semantic%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20radioButtonGrid(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20SLIDER%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-slider%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20slider(questionElem)%0A%20%20%20%20%20%20%2F%2F%20SLIDER%20LIST%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-multi-slider%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20sliderList(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20STAR%20RATING%20GRID%0A%20%20%20%20%20%20else%20if%20(questionElem.classList.contains(%27sg-type-table-stars%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20starRatingGrid(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20TEXTBOX%20%2F%20TEXTBOX%20LIST%20%2F%20TEXTBOX%20GRID%0A%20%20%20%20%20%20else%20if%20(%20%20%20questionElem.classList.contains(%27sg-type-textbox%27)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20questionElem.classList.contains(%27sg-type-multitext%27)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20questionElem.classList.contains(%27sg-type-table-textbox%27))%0A%20%20%20%20%20%20%20%20autoFilled%20%3D%20textboxes(questionElem)%0A%0A%20%20%20%20%20%20%2F%2F%20UNKOWN%2C%20IGNORE%0A%20%20%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20console.log(%22--%20question%20type%20not%20recognized%2C%20ignored%22)%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20if%20(LOG%20%26%26%20!autoFilled)%20console.log(%22--%20Already%20set%22)%0A%20%20%20%20%20%20autoFilledAnyQuestion%20%3D%20autoFilled%20%7C%7C%20autoFilledAnyQuestion%0A%20%20%20%20%20%20questionElem.scrollIntoView()%0A%20%20%20%20%7D%0A%20%20%20%20else%20%7B%0A%20%20%20%20%20%20console.log(%22--%20question%20hidden%2C%20igore%20it%22)%0A%20%20%20%20%7D%0A%20%20%7D)%0A%20%20if%20(autoFilledAnyQuestion)%20%7B%0A%20%20%20%20if%20(LOG)%20console.log(%22%5Cn---------------------%5Cn%3E%3E%3E%3E%20Looping%20to%20see%20if%20display%20logic%20popped%20up%20anything%20else%22)%0A%20%20%20%20setTimeout(function()%20%7B%0A%20%20%20%20%20%20autofill(questionElems)%0A%20%20%20%20%7D%2C%20500)%20%2F%2F%20wait%20to%20allow%20the%20survey%20engine%27s%20display%20logic%20to%20fire%0A%20%20%7D%0A%20%20else%20%7B%0A%20%20%20%20if%20(LOG)%20console.log(%22%5CnDONE!%22)%0A%20%20%7D%0A%7D%0A%0A%2F***%0A%20*%20Get%20array%20of%20questions%20on%20the%20page%20(pulling%20up%20questions%20in%20a%20Custom%20Group)%0A%20*%2F%0Aconst%20getQuestionElems%20%3D%20()%20%3D%3E%20%7B%0A%0A%20%20let%20docElem%20%3D%20getDocument()%0A%0A%20%20if%20(LOG)%20console.log(%22docElem%20%3D%20%22%2C%20docElem)%0A%0A%20%20const%20a%20%3D%20%5B...docElem.querySelector(%27.sg-question-set%27).children%5D%0A%0A%20%20const%20questionElems%20%3D%20%5B%5D%0A%0A%20%20a.forEach(elem%20%3D%3E%20%7B%0A%0A%20%20%20%20%2F%2F%20if%20elem%20is%20a%20Custom%20Group%2C%20look%20for%20the%20question%20elements%20in%20it%0A%20%20%20%20if%20(elem.classList.contains(%27sg-type-group%27))%20%7B%0A%20%20%20%20%20%20%2F%2F%20the%20questions%20in%20a%20Custom%20Group%20are%20.sg-queston%27s%20under%20.sg-group-item%20elements%0A%20%20%20%20%20%20const%20groupItemElems%20%3D%20elem.querySelectorAll(%27.sg-group-item%27)%0A%20%20%20%20%20%20groupItemElems.forEach(groupItemElem%20%3D%3E%0A%20%20%20%20%20%20%20%20questionElems.push(groupItemElem.querySelector(%27.sg-question%27)))%0A%20%20%20%20%7D%0A%20%20%20%20%2F%2F%20else%20elem%20is%20a%20question%20itself%0A%20%20%20%20else%20%7B%0A%20%20%20%20%20%20questionElems.push(elem)%0A%20%20%20%20%7D%0A%20%20%20%7D)%0A%0A%20%20%20return%20questionElems%0A%20%7D%0A%0A%2F***%0A%20*%20main()%0A%20*%2F%0A%0Aif%20(BOOKMARKLET)%20%7B%0A%20%20const%20questionElems%20%3D%20getQuestionElems()%0A%20%20console.log(%22%5Cn---------------------%5CnquestionElems%20%3D%20%22%2C%20questionElems%2C%20%27%5Cn%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5Cn%27)%0A%20%20autofill(questionElems)%0A%7D%0Aelse%20%7B%0A%20%20document.addEventListener(%22DOMContentLoaded%22%2C%20function()%20%7B%0A%20%20%20%20const%20questionElems%20%3D%20getQuestionElems()%0A%20%20%20%20console.log(%22%5Cn---------------------%5CnquestionElems%20%3D%20%22%2C%20questionElems%2C%20%27%5Cn%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5E%5Cn%27)%0A%20%20%20%20autofill(questionElems)%0A%20%20%7D)%0A%7D%0A%7D)()%0A%0A
-------------------------------------------------------------
Unadulterated code and process below is for reference only, if you completed the steps below you have added the Autofill bookmarklet.
To make this code work as a Bookmarklet:
- Set BOOKMARK = true
- Copy only this part of the code (not the <script> tags): (function() { . . . })
- Run it through a URL Encoder to get the code shown above
To test the code in a single survey:
- Copy to Style tab > HTML/CSS Editor (lower right of page) > Custom Header
<script> /* Alchemer v03 Autofill Chrome Bookmarklet Works with question types: checkbox (with optional min number of selections or optional write-in) checkbox grid (with optional min number of selections) conjoint continuous sum (with optional required total) essay dropdown dropdown menu grid dropdown menu list image multi select (but doesn't look at min number...yet) image select likert max diff nps radio button (with optional write-in) radio button grid ranking grid semantic diff slider (optional min/max and step) slider list (same as above) star rating grid textbox -- email -- date (all three formats but not min/max date) -- numeric with min/max number or min/max number of characters -- currency -- percent -- phone (based on the regex option) textbox grid (same as above) textbox list (same as above) Custom Groups containing any of the above Not working for actions audo sentiment cascading dropdown custom table drag and drop ranking file upload grouping open / closed card sort image heatmap text highlighter quick sort signature video feedback video sentiment */ (function() { const LOG = true const BOOKMARKLET = false // true when setting up as bookmarlet, false to run in the survey Style > HEADER /** * Returns a random integer between min (inclusive) and max (inclusive). * https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range */ function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } /*** * Shuffle array in place * Knuth shuffle: https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array * * array (array) will be mutated * return (array) the original array after being shuffled */ function shuffle(array) { var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } /** * Parse an Alchemer element #ID in the form: * sgE-5901811-28-305-box * sgE-5901811-28-305-10997-element * return (obj) Returns object of the constituent parts */ const parseSgId = (id) => { const regexID = /sgE-(\d+)-(\d+)-(\d+)(-(\d+))?-(\w+)/ const aParsed = id.match(regexID) if (!aParsed || aParsed.length !== 7) { alert('Javascript error parsing ID = ', id) } return { sid: aParsed[1], // ex: '5901811' pid: aParsed[2], // ex: '28' qid: aParsed[3], // ex: '305' oid: aParsed[5], // ex: '10997' or undefined if this isn’t an ID for an option type: aParsed[6] // ex: 'box' / 'element' } } /*** * Get the survey's SGAPI variable * * return (obj) the SGAPI global vraible */ const getSGAPI = () => { // Preview is in an iFrame const iFrameElem = document.querySelector('iframe#preview-the-page') if (iFrameElem) return iFrameElem.contentWindow.SGAPI return sgapi = SGAPI } /*** * Get the survey's document element * * return (elem) */ const getDocument = () => { // Preview is in an iFrame const iFrameElem = document.querySelector('iframe#preview-the-page') if (iFrameElem) return iFrameElem.contentDocument || iFrameElem.contentWindow.document return document } /*** * Get a property from SGAPI for the qid * * qid (int) question ID * propertyName (string) name of property * isInt (t/f) convert return value to int if true * return (int/string) the property or 0 / '' if property doesn't exist */ const getProperty = (qid, propertyName, isInt) => { const val = getSGAPI().survey.surveyObject.questions[qid].properties[propertyName] if (isInt) return parseInt(val) || 0 return val || '' } /*** * autofill dropdowns * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const dropdowns = (questionElem) => { let autoFilled = false const selectElems = questionElem.querySelectorAll('select') selectElems.forEach(selectElem => { if (LOG) console.log("selectElem.value = ", selectElem.value) // Don't change if dropdown already has a value // 'NoAnswer' for dropdown and dropdown menu list, '' for dropdown menu grid if (selectElem.value === 'NoAnswer' || selectElem.value === '') { autoFilled = true const numOptions = selectElem.querySelectorAll('option').length selectElem.selectedIndex = getRandomInt(1, numOptions - 1) // 1 to skip past "-- Please Select --", and -1 since this is 0-based } }) return autoFilled } /*** * autofill star rating grid * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const starRatingGrid = (questionElem) => { let autoFilled = false questionElem.querySelectorAll('tbody td').forEach(tdElem => { if (!tdElem.querySelector('input:checked')) { autoFilled = true const labelElems = tdElem.querySelectorAll('label') const random = getRandomInt(1, labelElems.length - 1) // 1 to skip the initial X, -1 since it's zero-based if (LOG) console.log("-- selecting stars = ", random) for (let i = 1; i <= random; i++) labelElems[i].classList.add('sg-star-on') labelElems[random].querySelector('input').checked = true } }) return autoFilled } /*** * autofill continuous sum * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const continuousSum = (questionElem) => { const inputElems = questionElem.querySelectorAll('tbody input[type=text]') for (let i = 0; i < inputElems.length; i++) { if (inputElems[i].value !== '') return false } const qid = parseSgId(questionElem.id).qid // const maxTotal = parseInt(SGAPI.survey.surveyObject.questions[qid].properties.max_total) || 0 const maxTotal = getProperty(qid, 'max_total', true) if (maxTotal) { const val = Math.floor(maxTotal / inputElems.length) for (let i = 0; i < inputElems.length - 1; i++) { inputElems[i].value = val } inputElems[inputElems.length - 1].value = maxTotal - (val * (inputElems.length - 1)) } else { inputElems.forEach(inputElem => inputElem.value = getRandomInt(0,10)) } // update the total inputElems[0].focus() inputElems[0].blur() return true } /*** * autofill slider * * questionElem (element) can be a question or a sliderRowElem for a slider list * return (t/f) true if auto-filled, false if there was already a value */ const slider = (questionElem) => { /*** * Get a random value and percent * * return (obj of int) { randomValue, randomPercent } */ const getRandomValueAndPercent = () => { const setupObj = JSON.parse(questionElem.querySelector('.slider-setup').value) if (LOG) console.log("setupObj = ", setupObj) const steps = Math.floor((setupObj.max - setupObj.min) / setupObj.stepval) const randomValue = setupObj.min + getRandomInt(0, steps) * setupObj.stepval const randomPercent = Math.floor(((randomValue - setupObj.min) / (setupObj.max - setupObj.min)) * 100) if (LOG) console.log("random val / percent = ", randomValue, ' / ', randomPercent, '%') return { randomValue, randomPercent } } /*** * main() */ // already set, don't change if (questionElem.querySelector('input.sg-input').value) return false const { randomValue, randomPercent } = getRandomValueAndPercent() // set slider value questionElem.querySelector('input.sg-input').value = randomValue // set the slider handle, this must be on a timer b/c of how the slider functions const sliderHandleElem = questionElem.querySelector('.ui-slider-handle') setTimeout(function () { sliderHandleElem.style.left = `${randomPercent}%` }, 400) setTimeout(function () { sliderHandleElem.style.left = `${randomPercent}%` }, 1000) // ensure it worked! return true } /*** * autofill sliderList * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const sliderList = (questionElem) => { let autoFilled = false questionElem.querySelectorAll('.sg-slider-row').forEach(sliderRowElem => autoFilled = slider(sliderRowElem) || autoFilled) return autoFilled } /*** * autofill textboxes * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const textboxes = (questionElem) => { const qid = parseSgId(questionElem.id).qid /*** * Get the validation parameters for min/max charachter count. or 0 if not set * * return (obj of ints) { minCharacters, maxCharacters } */ const getMinMaxCharacters = () => { //const qid = parseSgId(questionElem.id).qid const minCharacters = getProperty(qid, 'min_characters', true) const maxCharacters = getProperty(qid, 'max_characters', true) if (LOG) console.log("textboxes(), qid = ", qid) if (LOG) console.log("- min/max chars = ", minCharacters, ' / ', maxCharacters) return { minCharacters, maxCharacters } } /*** * Get the validation parameters for min/max number, or 0 if not set * * return (obj of ints) { minNumber, maxNumber } */ const getMinMaxNumber = () => { //const qid = parseSgId(questionElem.id).qid const minNumber = getProperty(qid, 'min_number', true) const maxNumber = getProperty(qid, 'max_number', true) if (LOG) console.log("textboxes(), qid = ", qid) if (LOG) console.log("- min/max number = ", minNumber, ' / ', maxNumber) return { minNumber, maxNumber } } /*** * Get the input mask (regex validation) * * reutrn (string) the question property inputmask.MASK or empty string */ const getInputMask = () => { const inputMask = getProperty(qid, 'inputmask', false) const retval = (inputMask) ? inputMask.MASK : '' if (LOG) console.log("getInputMask() = ", retval) return retval } /*** * main() */ let autoFilled = false const { minCharacters, maxCharacters } = getMinMaxCharacters() let { minNumber, maxNumber } = getMinMaxNumber() minNumber = Math.ceil(minNumber) maxNumber = Math.floor(maxNumber) const inputElems = questionElem.querySelectorAll('input[type=text]') inputElems.forEach(inputElem => { // only fill if there's no value if (!inputElem.value) { autoFilled = true const classList = inputElem.classList // EMAIL if (classList.contains('sg-validation-email')) inputElem.value = 'test@test.com' // DATE else if (classList.contains('sg-validation-date')) { if (classList.contains('sg-validation-date-yyyy')) inputElem.value = '2025/01/01' else inputElem.value = '01/01/2025' } // NUMERIC else if ( classList.contains('sg-validation-numeric') || classList.contains('sg-validation-percent') || classList.contains('sg-validation-currency') ) { if (minNumber && maxNumber) inputElem.value = getRandomInt(minNumber, maxNumber) else if (minNumber) inputElem.value = getRandomInt(minNumber, minNumber + 20) else if (maxNumber) inputElem.value = getRandomInt(0, maxNumber) else if (minCharacters) inputElem.value = '1'.repeat(minCharacters) else inputElem.value = '123'.slice(0, maxCharacters | 3) } // US PHONE (from the Alchemer regex for a US Phone, note: the backslashes are escaped so they appear doubled) else if (getInputMask() === "^((\\(\\d{3}\\) ?)|(\\d{3}[-\\s]))?\\d{3}[-\\s]\\d{4}$") { inputElem.value = '123-456-7890' } // OTHERWISE, normal text else { if (LOG) console.log("otherwise, normal text") if (minCharacters) { if (minCharacters === 5) // special case for zip inputElem.value = '12345' else inputElem.value = 'x'.repeat(minCharacters) } else { inputElem.value = 'test'.slice(0, maxCharacters | 4) } } // fire display logic on later questions inputElem.focus() inputElem.blur() } }) return autoFilled } /*** * autofill essay * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const essay = (questionElem) => { const textareaElem = questionElem.querySelector('textarea') // if already has a value, do nothing if (textareaElem.value) return false textareaElem.value = 'test' // fire display logic on later questions textareaElem.focus() textareaElem.blur() return true } /*** * autofill radio button * * questionElem (element) question or other elem type for radio button grid or conjoint * return (t/f) true if auto-filled, false if there was already a value */ const radioButton = (questionElem) => { if (LOG) console.log("radioButton = ", questionElem) // if already selected, do nothing if (questionElem.querySelectorAll('input[type=radio]:checked').length) return false const radioElems = questionElem.querySelectorAll('input[type=radio]') const radioElem = radioElems[getRandomInt(0, radioElems.length - 1)] if (LOG) console.log("clicking ", radioElem) // fire display logic on later questions radioElem.click() // check for Other Write In if (radioElem.parentElement.classList.contains('sg-other-li')) radioElem.parentElement.querySelector('input[type=text]').value = 'write-in' return true } /*** * autofill radio button grid * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const radioButtonGrid = (questionElem) => { let autoFilled = false const trElems = questionElem.querySelectorAll('tbody tr') trElems.forEach(trElem => autoFilled = radioButton(trElem) || autoFilled) return autoFilled } /*** * autofill image select and image multi select * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const imageSelect = (questionElem) => { if (questionElem.querySelector('.sg-image-selected')) return false const imageSelectElems = questionElem.querySelectorAll('.sg-image-box label') const imageSelectElem = imageSelectElems[getRandomInt(0, imageSelectElems.length - 1)] if (LOG) console.log("clicking ", imageSelectElem) // fire display logic on later questions imageSelectElem.click() return true } /*** * autofill conjoint -- all pages * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const conjoint = (questionElem) => { if (questionElem.querySelector('input[type=radio]:checked')) return false const conjointSetElems = questionElem.querySelectorAll('.sg-conjoint-set') for (let i = 0; i < conjointSetElems.length; i++) { if (LOG) console.log("\nconjoint set = ", conjointSetElems[i]) radioButton(conjointSetElems[i]) if (i !== conjointSetElems.length - 1) document.querySelector('.sg-next-button.btn-conjoint').click() } return true } /*** * autofill max diff -- all pages * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const maxDiff = (questionElem) => { if (questionElem.querySelectorAll('input[type=radio]:checked').length) return false const maxDiffSetElems = questionElem.querySelectorAll('.sg-maxdiff-set') for (let i = 0; i < maxDiffSetElems.length; i++) { if (LOG) console.log("\maxDiff set = ", maxDiffSetElems[i]) const trElems = shuffle([...maxDiffSetElems[i].querySelectorAll('tbody tr')]) trElems[0].querySelectorAll('input[type=radio]')[0].click() trElems[1].querySelectorAll('input[type=radio]')[1].click() if (i !== maxDiffSetElems.length - 1) document.querySelector('.sg-next-button').click() } return true } /*** * autofill ranking grid * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const rankingGrid = (questionElem) => { if (questionElem.querySelectorAll('tbody tr input[type=radio]:checked').length) return false const trElems = questionElem.querySelectorAll('tbody tr') // get a randomized array of ints [0..trElems.length-1] let aRanking = [] for (let i = 0; i < trElems.length; i++) aRanking.push(i) aRanking = shuffle(aRanking) trElems.forEach((trElem, idx) => { const inputElems = trElem.querySelectorAll('input[type=radio]') inputElems[aRanking[idx]].click() }) return true } /*** * autofill checkbox * * checkboxElem (element) a question for a checkbox OR a TR for a checkbox grid row * return (t/f) true if auto-filled, false if there was already a value */ const checkbox = (questionElem, isCheckboxGridRow = false) => { console.log("checkbox() questionElem = ", questionElem) // if already checked, do nothing if (questionElem.querySelectorAll('input[type=checkbox]:checked').length) return false // checkboxes const checkElems = questionElem.querySelectorAll('input[type=checkbox]') // the minimum number of checks based on the Validation for the checkbox question or checkbox grid let minChecks = undefined if (isCheckboxGridRow) { // checkbox grid TRs have a class name in the form 'row-12', where 12 is the QID //const row_qid = [...questionElem.classList].find(s => s.slice(0, 4) === 'row-') const row_qid = [...questionElem.classList].find(s => s.startsWith('row-')) const qid = parseInt(row_qid.slice(4)) minChecks = getProperty(qid, 'min_answers_per_row', true) } else { const qid = parseSgId(questionElem.id).qid minChecks = getProperty(qid, 'minimum_response', true) } minChecks = Math.min(minChecks, checkElems.length) || 1 console.log("minChecks = ", minChecks) // check the min number of checkboxes and fire display logic on later questions let checked = 0 while (checked < minChecks) { const random = getRandomInt(0, checkElems.length - 1) if (!checkElems[random].checked) { const checkElem = checkElems[random] checkElem.click() checked++ // check for Other Write In if (checkElem.parentElement.classList.contains('sg-other-li')) checkElem.parentElement.querySelector('input[type=text]').value = 'write-in' } } return true } /*** * autofill checkbox grid * * questionElem (element) * return (t/f) true if auto-filled, false if there was already a value */ const checkboxGrid = (questionElem) => { let autoFilled = false const trElems = questionElem.querySelectorAll('tbody tr') trElems.forEach(trElem => autoFilled = checkbox(trElem, true) || autoFilled) return autoFilled } /*** * autofill the page * * This function uses a Timeout to recurse. The Timeout allows the survey's * display logic engine to run and we go through the questions again to * fill any new ones that were displayed. * * questionElems (arr of elems) all question on the page including hidden */ const autofill = (questionElems) => { let autoFilledAnyQuestion = false questionElems.forEach(questionElem => { if (LOG) console.log("\n---------------------\nquestionElem = ", questionElem) let autoFilled = false if (!questionElem.classList.contains('sg-hide')) { if (LOG) console.log("-- autopopulating: ", questionElem.id) // CHECKBOX if (questionElem.classList.contains('sg-type-checkbox')) autoFilled = checkbox(questionElem) // CHECKBOX GRID else if (questionElem.classList.contains('sg-type-table-checkbox')) autoFilled = checkboxGrid(questionElem) // CONJOINT else if (questionElem.classList.contains('sg-type-conjoint_new')) autoFilled = conjoint(questionElem) // CONTINUOUS SUM else if (questionElem.classList.contains('sg-type-continuous-sum')) autoFilled = continuousSum(questionElem) // DROPDOWN // DROPDOWN MENU LIST // DROPDOWN MENU GRID else if ( questionElem.classList.contains('sg-type-menu') || questionElem.classList.contains('sg-type-multimenu') || questionElem.classList.contains('sg-type-table-menu-matrix')) autoFilled = dropdowns(questionElem) // ESSAY else if (questionElem.classList.contains('sg-type-essay')) autoFilled = essay(questionElem) // IMAGE SELECT // IMAGE MULTI SELECT else if (questionElem.classList.contains('sg-type-imageselect')) autoFilled = imageSelect(questionElem) // MAX DIFF else if (questionElem.classList.contains('sg-type-maxdiff')) autoFilled = maxDiff(questionElem) // RADIO BUTTON else if (questionElem.classList.contains('sg-type-radio')) autoFilled = radioButton(questionElem) // RADIO BUTTON GRID else if (questionElem.classList.contains('sg-type-table-radio')) autoFilled = radioButtonGrid(questionElem) // RANKING GRID else if (questionElem.classList.contains('sg-type-rank-table')) autoFilled = rankingGrid(questionElem) // SEMANTIC DIFF else if (questionElem.classList.contains('sg-type-table-semantic')) autoFilled = radioButtonGrid(questionElem) // SLIDER else if (questionElem.classList.contains('sg-type-slider')) autoFilled = slider(questionElem) // SLIDER LIST else if (questionElem.classList.contains('sg-type-multi-slider')) autoFilled = sliderList(questionElem) // STAR RATING GRID else if (questionElem.classList.contains('sg-type-table-stars')) autoFilled = starRatingGrid(questionElem) // TEXTBOX / TEXTBOX LIST / TEXTBOX GRID else if ( questionElem.classList.contains('sg-type-textbox') || questionElem.classList.contains('sg-type-multitext') || questionElem.classList.contains('sg-type-table-textbox')) autoFilled = textboxes(questionElem) // UNKOWN, IGNORE else { console.log("-- question type not recognized, ignored") } if (LOG && !autoFilled) console.log("-- Already set") autoFilledAnyQuestion = autoFilled || autoFilledAnyQuestion questionElem.scrollIntoView() } else { console.log("-- question hidden, igore it") } }) if (autoFilledAnyQuestion) { if (LOG) console.log("\n---------------------\n>>>> Looping to see if display logic popped up anything else") setTimeout(function() { autofill(questionElems) }, 500) // wait to allow the survey engine's display logic to fire } else { if (LOG) console.log("\nDONE!") } } /*** * Get array of questions on the page (pulling up questions in a Custom Group) */ const getQuestionElems = () => { let docElem = getDocument() if (LOG) console.log("docElem = ", docElem) const a = [...docElem.querySelector('.sg-question-set').children] const questionElems = [] a.forEach(elem => { // if elem is a Custom Group, look for the question elements in it if (elem.classList.contains('sg-type-group')) { // the questions in a Custom Group are .sg-queston's under .sg-group-item elements const groupItemElems = elem.querySelectorAll('.sg-group-item') groupItemElems.forEach(groupItemElem => questionElems.push(groupItemElem.querySelector('.sg-question'))) } // else elem is a question itself else { questionElems.push(elem) } }) return questionElems } /*** * main() */ if (BOOKMARKLET) { const questionElems = getQuestionElems() console.log("\n---------------------\nquestionElems = ", questionElems, '\n^^^^^^^^^^^^^^^^^^^^^^\n') autofill(questionElems) } else { document.addEventListener("DOMContentLoaded", function() { const questionElems = getQuestionElems() console.log("\n---------------------\nquestionElems = ", questionElems, '\n^^^^^^^^^^^^^^^^^^^^^^\n') autofill(questionElems) }) } })() </script>
Net Promoter®, NPS®, NPS Prism®, and the NPS-related emoticons are registered trademarks of Bain & Company, Inc., Satmetrix Systems, Inc., and Fred Reichheld. Net Promoter Score℠ and Net Promoter System℠ are service marks of Bain & Company, Inc., Satmetrix Systems, Inc., and Fred Reichheld.