Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified bin/AutoComplete.adc
Binary file not shown.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 3 additions & 3 deletions config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
<info>
<name>AutoComplete</name>
<guid>4b7058ef-4656-4aa4-ae9c-269c8a2810f8</guid>
<version>3.2.1</version>
<date>2023-06-20</date>
<version>3.3.1</version>
<date>2026-03-11</date>
<description><![CDATA[autoComplete: open text box with search capability as google search]]></description>
<company>Askia</company>
<author><![CDATA[Jérôme Duparc <jeromed@askia.com>]]></author>
<site>http://www.askia.com</site>
<helpURL></helpURL>
<helpURL></helpURL>
<categories>
<category>General</category>
</categories>
Expand Down
124 changes: 74 additions & 50 deletions resources/dynamic/open.html
Original file line number Diff line number Diff line change
@@ -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
%}
<script type="text/javascript">
window.value = '{%= selectOnHover %}';
</script>

<input id="{%= inputId %}" aria-label="{%=inputId%}" class="autocomplete hiddens" type="text" autocomplete="off" name="{%= inputName%}" value="{%:= inputValue %}" />
{%
Next idxPA
%}
<br />
{%
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 id="adc_{%= CurrentADC.InstanceId %}_input" aria-label="{%:= placeholder %}"
class="autocomplete{%= classHighlight %}" {%= autofocus %} type="search" autocomplete="off" name="adc_{%= CurrentADC.InstanceId %}_input"
placeholder="{%:= placeholder %}" value="{%:= inputSearchValue %}" />
<button class="close-icon" type="reset"></button>
<div class="nomatch"></div>
{%
' 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("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
%}
<script type="text/javascript">
window.value = '{%= selectOnHover %}';
</script>

<input id="{%= inputId %}" aria-label="{%= inputId %}" class="autocomplete hiddens" type="text" autocomplete="off" name="{%= inputName %}" value="{%:= safeInputValue %}" />
{%
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("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")

' Safe escaped placeholder
Dim safePlaceholder = placeholder
safePlaceholder = safePlaceholder.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
%}
<br />

<input id="adc_{%= CurrentADC.InstanceId %}_input" aria-label="{%:= safePlaceholder %}"
class="autocomplete{%= classHighlight %}" {%= autofocus %} type="search" autocomplete="off" name="adc_{%= CurrentADC.InstanceId %}_input"
placeholder="{%:= safePlaceholder %}" value="" />

<span id="adc_{%= CurrentADC.InstanceId %}_text" style="display:none;">{%:= safeInputSearchValue %}</span>

<script type="text/javascript">
(function () {
var input = document.getElementById("adc_{%= CurrentADC.InstanceId %}_input");
var text = document.getElementById("adc_{%= CurrentADC.InstanceId %}_text");
if (input && text) {
input.value = text.textContent || text.innerText || "";
}
})();
</script>

<button class="close-icon" type="reset"></button>
<div class="nomatch"></div>
133 changes: 83 additions & 50 deletions resources/dynamic/single.html
Original file line number Diff line number Diff line change
@@ -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
%}

<script type="text/javascript">
window.value = '{%= selectOnHover %}';
</script>

<input id="{%= inputId %}" aria-label="{%=inputId%}" class="autocomplete hiddens" type="text" autocomplete="off" name="{%= inputName%}" value="{%:= inputValue %}" />

<br />
{%
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 id="adc_{%= CurrentADC.InstanceId %}_input" aria-label="{%:= placeholder %}"
class="autocomplete{%= classHighlight %}" {%= autofocus %} type="search" autocomplete="off" name="adc_{%= CurrentADC.InstanceId %}_input"
placeholder="{%:= placeholder %}" value="{%:= inputSearchValue %}" />
<button class="close-icon" type="reset"></button>
<div class="nomatch"></div>
{%
' 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("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
safeInputSearchValue = safeInputSearchValue.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
safePlaceholder = safePlaceholder.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;")
%}

<script type="text/javascript">
window.value = '{%= selectOnHover %}';
</script>

<input id="{%= inputId %}"
aria-label="{%= inputId %}"
class="autocomplete hiddens"
type="text"
autocomplete="off"
name="{%= inputName %}"
value="{%:= safeInputValue %}" />
<br />

<input id="adc_{%= CurrentADC.InstanceId %}_input"
aria-label="{%:= safePlaceholder %}"
class="autocomplete{%= classHighlight %}"
{%= autofocus %}
type="search"
autocomplete="off"
name="adc_{%= CurrentADC.InstanceId %}_input"
placeholder="{%:= safePlaceholder %}"
value="" />

<span id="adc_{%= CurrentADC.InstanceId %}_text" style="display:none;">{%:= safeInputSearchValue %}</span>

<script type="text/javascript">
window.value = '{%= selectOnHover %}';
(function () {
var input = document.getElementById("adc_{%= CurrentADC.InstanceId %}_input");
var text = document.getElementById("adc_{%= CurrentADC.InstanceId %}_text");
if (input && text) {
input.value = text.textContent || text.innerText || "";
}
})();
</script>

<button class="close-icon" type="reset"></button>
<div class="nomatch"></div>
37 changes: 28 additions & 9 deletions resources/static/auto-complete.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ var autoComplete = (function(){
}
}

function escapeHtmlAttr(value) {
return String(value)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

function decodeHtmlAttr(value) {
var txt = document.createElement('textarea');
txt.innerHTML = value;
return txt.value;
}

var o = {
selector: 0,
source: 0,
Expand All @@ -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 '<div class="autocomplete-suggestion" data-val="' + item + '" data-fullval="' + fullItem + '">' + item.toString().replace(re, function (x) {return "<b>" + x + "</b>";}) + '</div>';
return '<div class="autocomplete-suggestion" data-val="' + escapeHtmlAttr(item) + '" data-fullval="' + escapeHtmlAttr(fullItem) + '">' + item.toString().replace(re, function (x) {return "<b>" + x + "</b>";}) + '</div>';
},
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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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; }
}
Expand All @@ -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;
}
};
Expand Down Expand Up @@ -383,4 +402,4 @@ var autoComplete = (function(){
else
autoComplete.databases = {};
window.autoComplete = autoComplete;
})();
})();
Loading