Autofill tool

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
    • 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

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:

  1. Set BOOKMARK = true
  2. Copy only this part of the code (not the <script> tags):  (function() { . . . }) 
  3. Run it through a URL Encoder to get the code shown above

To test the code in a single survey:

  1. 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.

Basic Standard Market Research HR Professional Full Access Reporting
Free Individual Team & Enterprise
Feature Included In