diff --git a/bin/AutoComplete.adc b/bin/AutoComplete.adc index 17201e7..59b83cf 100644 Binary files a/bin/AutoComplete.adc and b/bin/AutoComplete.adc differ diff --git a/changelog.md b/changelog.md index 1a3a363..319ecdf 100755 --- a/changelog.md +++ b/changelog.md @@ -19,3 +19,5 @@ * v3.1.6 - 2021/03/31 - Prabath Gamage - json encode/parse issue fixed * v3.2.0 - 2021/11/10 - Prabath Gamage - Auto select when one result found * v3.2.1 - 2023/06/20 - Prabath Gamage - A tiny functional fix +* v3.3.0 - 2026/06/10 - Jordan Grindle - overhaul of the JS and HTML files to allow the use of double quotes without breaking the control +v3.3.1 - 2026/06/11 - Jordan Grindle - change of double quote syntax to accomodate 5.5 survey engine \ No newline at end of file diff --git a/config.xml b/config.xml index a6d8545..7cca9d9 100755 --- a/config.xml +++ b/config.xml @@ -7,13 +7,13 @@ AutoComplete 4b7058ef-4656-4aa4-ae9c-269c8a2810f8 - 3.2.1 - 2023-06-20 + 3.3.1 + 2026-03-11 Askia ]]> http://www.askia.com - + General diff --git a/resources/dynamic/open.html b/resources/dynamic/open.html index b01e5af..23511f1 100644 --- a/resources/dynamic/open.html +++ b/resources/dynamic/open.html @@ -1,50 +1,74 @@ -{% - ' Input settings - Dim inputId - Dim inputName - Dim inputValue - Dim inputSearchValue = "" - - ' Get properties - Dim autofocus = "" - If (CurrentADC.PropValue("autofocus") = "autofocus") Then - autofocus = "autofocus" - EndIf - - Dim selectOnHover = CurrentADC.PropValue("selectOnHover") - - Dim arrParentAnswers = CurrentQuestion.ParentLoop.Answers - Dim idxPA - - For idxPA = 1 To arrParentAnswers.Count - inputName = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputName() - inputId = inputName - inputValue = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputValue() - - ' Get selected answer - If (arrParentAnswers[idxPA].Caption = CurrentADC.PropValue("searchField")) Then - inputSearchValue = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputValue() - EndIf -%} - - - -{% - Next idxPA -%} -
- {% - Dim highlightInput = CurrentADC.PropValue("highlightInputOnError") - Dim highlightConditionBlockingMessage = CurrentADC.PropValue("highlightConditionBlockingErrorMessage").ToLowerCase().Trim() - Dim highlightConditionNonBlockingMessage = CurrentADC.PropValue("highlightConditionNonBlockingErrorMessage").ToLowerCase().Trim() -Dim classHighlight = On(highlightInput = "yes" And highlightConditionBlockingMessage = "true"," blocking_errormessage",On(highlightInput = "yes" And highlightConditionNonBlockingMessage = "true"," nonblocking_errormessage","")) -Dim placeholder = CurrentADC.PropValue("placeholder") - %} - - - -
+{% + ' Input settings + Dim inputId + Dim inputName + Dim inputValue + Dim inputSearchValue = "" + + ' Get properties + Dim autofocus = "" + If (CurrentADC.PropValue("autofocus") = "autofocus") Then + autofocus = "autofocus" + EndIf + + Dim selectOnHover = CurrentADC.PropValue("selectOnHover") + + Dim arrParentAnswers = CurrentQuestion.ParentLoop.Answers + Dim idxPA + + For idxPA = 1 To arrParentAnswers.Count + inputName = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputName() + inputId = inputName + inputValue = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputValue() + + ' Get selected answer + If (arrParentAnswers[idxPA].Caption = CurrentADC.PropValue("searchField")) Then + inputSearchValue = CurrentQuestion.Iteration(arrParentAnswers[idxPA].Index).InputValue() + EndIf + + ' Safe escaped value for hidden open-end input + Dim safeInputValue = inputValue + safeInputValue = safeInputValue.Replace("&", "&").Replace("<", "<").Replace(">", ">") +%} + + + +{% + Next idxPA + + Dim highlightInput = CurrentADC.PropValue("highlightInputOnError") + Dim highlightConditionBlockingMessage = CurrentADC.PropValue("highlightConditionBlockingErrorMessage").ToLowerCase().Trim() + Dim highlightConditionNonBlockingMessage = CurrentADC.PropValue("highlightConditionNonBlockingErrorMessage").ToLowerCase().Trim() + Dim classHighlight = On(highlightInput = "yes" And highlightConditionBlockingMessage = "true"," blocking_errormessage",On(highlightInput = "yes" And highlightConditionNonBlockingMessage = "true"," nonblocking_errormessage","")) + Dim placeholder = CurrentADC.PropValue("placeholder") + + ' Safe escaped value for visible text node + Dim safeInputSearchValue = inputSearchValue + safeInputSearchValue = safeInputSearchValue.Replace("&", "&").Replace("<", "<").Replace(">", ">") + + ' Safe escaped placeholder + Dim safePlaceholder = placeholder + safePlaceholder = safePlaceholder.Replace("&", "&").Replace("<", "<").Replace(">", ">") +%} +
+ + + + + + + + +
\ No newline at end of file diff --git a/resources/dynamic/single.html b/resources/dynamic/single.html index e6c488f..ac34162 100644 --- a/resources/dynamic/single.html +++ b/resources/dynamic/single.html @@ -1,50 +1,83 @@ -{% - ' Input settings - Dim inputId - Dim inputName - Dim inputValue - Dim inputSearchValue = "" - - ' Get properties - Dim autofocus = "" - If (CurrentADC.PropValue("autofocus") = "autofocus") Then - autofocus = "autofocus" - EndIf - - Dim selectOnHover = CurrentADC.PropValue("selectOnHover") - - - ' Generate input fields - inputName = CurrentQuestion.InputName() - inputId = inputName - inputValue = CurrentQuestion.InputValue() - - ' Get selected answer - Dim arrAnswers = CurrentQuestion.Answers - Dim idxA - - For idxA = 1 To arrAnswers.Count - inputSearchValue = arrAnswers[idxA].Caption - Next idxA - %} - - - - - -
- {% - Dim highlightInput = CurrentADC.PropValue("highlightInputOnError") - Dim highlightConditionBlockingMessage = CurrentADC.PropValue("highlightConditionBlockingErrorMessage").ToLowerCase().Trim() - Dim highlightConditionNonBlockingMessage = CurrentADC.PropValue("highlightConditionNonBlockingErrorMessage").ToLowerCase().Trim() - Dim classHighlight = On(highlightInput = "yes" And highlightConditionBlockingMessage = "true"," blocking_errormessage",On(highlightInput = "yes" And highlightConditionNonBlockingMessage = "true"," nonblocking_errormessage","")) - Dim placeholder = CurrentADC.PropValue("placeholder") - %} - - - -
+{% + ' Input settings + Dim inputId + Dim inputName + Dim inputValue + Dim inputSearchValue = "" + + ' Get properties + Dim autofocus = "" + If (CurrentADC.PropValue("autofocus") = "autofocus") Then + autofocus = "autofocus" + EndIf + + Dim selectOnHover = CurrentADC.PropValue("selectOnHover") + + ' Generate input fields + inputName = CurrentQuestion.InputName() + inputId = inputName + inputValue = CurrentQuestion.InputValue() + + ' Get selected answer + Dim arrAnswers = CurrentQuestion.Answers + Dim idxA + + For idxA = 1 To arrAnswers.Count + inputSearchValue = arrAnswers[idxA].Caption + Next idxA + + Dim highlightInput = CurrentADC.PropValue("highlightInputOnError") + Dim highlightConditionBlockingMessage = CurrentADC.PropValue("highlightConditionBlockingErrorMessage").ToLowerCase().Trim() + Dim highlightConditionNonBlockingMessage = CurrentADC.PropValue("highlightConditionNonBlockingErrorMessage").ToLowerCase().Trim() + Dim classHighlight = On(highlightInput = "yes" And highlightConditionBlockingMessage = "true"," blocking_errormessage",On(highlightInput = "yes" And highlightConditionNonBlockingMessage = "true"," nonblocking_errormessage","")) + Dim placeholder = CurrentADC.PropValue("placeholder") + Dim safeInputValue = inputValue + Dim safeInputSearchValue = inputSearchValue + Dim safePlaceholder = placeholder + + + + ' Escape for HTML attributes + safeInputValue = safeInputValue.Replace("&", "&").Replace("<", "<").Replace(">", ">") + safeInputSearchValue = safeInputSearchValue.Replace("&", "&").Replace("<", "<").Replace(">", ">") + safePlaceholder = safePlaceholder.Replace("&", "&").Replace("<", "<").Replace(">", ">") +%} + + + + +
+ + + + + + + + +
\ No newline at end of file diff --git a/resources/static/auto-complete.js b/resources/static/auto-complete.js index e8c29d0..7523fcc 100755 --- a/resources/static/auto-complete.js +++ b/resources/static/auto-complete.js @@ -108,6 +108,20 @@ var autoComplete = (function(){ } } + function escapeHtmlAttr(value) { + return String(value) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); + } + + function decodeHtmlAttr(value) { + var txt = document.createElement('textarea'); + txt.innerHTML = value; + return txt.value; + } + var o = { selector: 0, source: 0, @@ -134,10 +148,10 @@ var autoComplete = (function(){ var excapedSearchSeparator = o.searchSeparator.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$]/g, "\\$&"); var splitRegExp = new RegExp(" |" + excapedSearchSeparator,"gi"); var re = new RegExp(search.split(splitRegExp).join('|').replace(/\\\|/gi, "|"), "gi"); - return '
' + item.toString().replace(re, function (x) {return "" + x + "";}) + '
'; + return '
' + item.toString().replace(re, function (x) {return "" + x + "";}) + '
'; }, onSelect: function(e, term, item){ - var obj = JSON.parse(item.getAttribute('data-fullval')); + var obj = JSON.parse(decodeHtmlAttr(item.getAttribute('data-fullval'))); if (o.questionType === 'single') { document.getElementById(options.inputName).value = obj.inputValue; @@ -220,13 +234,13 @@ var autoComplete = (function(){ var selectOnHover = window.value; if(selectOnHover == "yes"){ - that.value = this.getAttribute('data-val'); + that.value = decodeHtmlAttr(this.getAttribute('data-val')); } }, that.sc); live('autocomplete-suggestion', 'mousedown', function(e){ if (hasClass(this, 'autocomplete-suggestion')) { // else outside click - var v = this.getAttribute('data-val'); + var v = decodeHtmlAttr(this.getAttribute('data-val')); that.value = v; that.last_val = v; that.nchild = 1; @@ -267,7 +281,7 @@ var autoComplete = (function(){ // Auto select the only result found if (options.autoSelect == 'yes' & data.length == 1) { let firstData = that.sc.firstElementChild; - var v = firstData.getAttribute('data-val'); + var v = decodeHtmlAttr(firstData.getAttribute('data-val')); that.value = v; that.last_val = v; that.nchild = 1; @@ -293,13 +307,13 @@ var autoComplete = (function(){ if (!sel) { next = (key == 40) ? that.sc.querySelector('.autocomplete-suggestion') : that.sc.childNodes[that.sc.childNodes.length - 1]; // first : last next.className += ' selected'; - that.value = next.getAttribute('data-val'); + that.value = decodeHtmlAttr(next.getAttribute('data-val')); } else { next = (key == 40) ? sel.nextSibling : sel.previousSibling; if (next) { sel.className = sel.className.replace(' selected', ''); next.className += ' selected'; - that.value = next.getAttribute('data-val'); + that.value = decodeHtmlAttr(next.getAttribute('data-val')); } else { sel.className = sel.className.replace(' selected', ''); that.value = that.last_val; next = 0; } } @@ -310,7 +324,12 @@ var autoComplete = (function(){ else if (key == 27) { that.value = that.last_val; that.sc.style.display = 'none'; } // enter else if (key == 13) { - if (sel && that.sc.style.display != 'none') { o.onSelect(e, sel.getAttribute('data-val'), sel); that.last_val = sel.getAttribute('data-val'); that.nchild = 1; setTimeout(function(){ that.sc.style.display = 'none'; }, 20); } + if (sel && that.sc.style.display != 'none') { + o.onSelect(e, decodeHtmlAttr(sel.getAttribute('data-val')), sel); + that.last_val = decodeHtmlAttr(sel.getAttribute('data-val')); + that.nchild = 1; + setTimeout(function(){ that.sc.style.display = 'none'; }, 20); + } return false; } }; @@ -383,4 +402,4 @@ var autoComplete = (function(){ else autoComplete.databases = {}; window.autoComplete = autoComplete; -})(); +})(); \ No newline at end of file