1131 lines
39 KiB
JavaScript
1131 lines
39 KiB
JavaScript
/*
|
||
$Revision$
|
||
$Id$
|
||
|
||
File: Suggest.js
|
||
Description:
|
||
Parameters: params.objectName,
|
||
params.queryField,
|
||
params.queryUrl,
|
||
params.initKey,
|
||
params.initExtraParam,
|
||
params.keyField,
|
||
params.fieldReadonly,
|
||
params.urlAdd,
|
||
params.extraParamField,
|
||
params.isBad,
|
||
params.highlightDescript,
|
||
params.noJustOne,
|
||
params.JSONP
|
||
params.embedded Kijk nooit naar het parent document (embedded)
|
||
Context:
|
||
Note: Pas op met gebruiken functies uit andere modules. Dit bestand
|
||
wordt ook bij vinder/vanuit API\phonebook_js.asp gebruikt en dan
|
||
heb je die andere bestanden niet.
|
||
|
||
*/
|
||
|
||
var $suggestbusy;
|
||
var suggesttimer;
|
||
function Suggest(inParams)
|
||
{
|
||
// Genereer een enkel stukje busy-html die voor alle suggests in deze pagina hergebruiken
|
||
if (!$suggestbusy)
|
||
$suggestbusy = $("<i class='fa fa-gear fa-spin suggestbusy'></i>").appendTo($("body"));
|
||
var params = inParams;
|
||
|
||
var storeKeyField; // = inStoreKeyField; // use function withKeyField() to assign field
|
||
var storeExtraParamField; // = instoreExtraParamField; // use function withExtraParamField() to assign field
|
||
var _completeDiv = null;
|
||
var _comboDiv = null;
|
||
|
||
//thisdoc.onclick = function(){debugger;};
|
||
// Kopie uit FcltJquery.js. Die hebben we niet vanuit API\phonebook_js.asp
|
||
var myEncode = function (s)
|
||
{
|
||
return unescape(encodeURIComponent(escape(s))).replace(/\+/g, "%2B");
|
||
}
|
||
|
||
var req;
|
||
var suggestionValues=[];
|
||
var resultsObj;
|
||
var selected = -1;
|
||
var latestServerQuery = "";
|
||
var THROTTLE_PERIOD = 250;
|
||
var timerID = null;
|
||
var lastHighlight = null;
|
||
var divWidth = 500;
|
||
|
||
var emptyKey = ("emptyKey" in params)?params.emptyKey:-1;
|
||
var emptyText = "";
|
||
var currentKey = -1;
|
||
var currentText = null;
|
||
var currentExtraParam = null;
|
||
// De this. functies zijn zo extern ook beschikbaar
|
||
this.makeReadOnly = makeReadOnly;
|
||
this.undoReadOnly = undoReadOnly;
|
||
this.isReadOnly = isReadOnly;
|
||
this.setValue = setValue;
|
||
this.getValue = getValue;
|
||
this.CheckJustOne = CheckJustOne;
|
||
this.reloadUrlAdd = reloadUrlAdd;
|
||
this.changeQueryUrl = changeQueryUrl;
|
||
this.sendQuery = sendQuery;
|
||
thisdoc = document; // Hier wordt de AutocompleteDiv aan gehangen
|
||
|
||
// TODO: Controleren of parent wel in hetzelfde domein zit?
|
||
if (!params.embedded && self != top && window.name != "fclttop" && document.body.id!="searchbody")
|
||
{
|
||
thisdoc = parent.document; // We gaan de AutocompleteDiv aan de parent hangen, uit het frame breken
|
||
|
||
// Mogelijke fix voor PNBR#22301
|
||
if (FcltMgr._pageManager.name == 'defaultManager' && parent.window.name != "fclttop" && parent.FcltMgr && !parent.FcltMgr.IsTheManager)
|
||
thisdoc = parent.parent.document; // Nog eentje hoger
|
||
|
||
if (!window.name || window.name == "")
|
||
{
|
||
// FcltMgr.alert("Internal error. " + document.referrer + " heeft een IFRAME zonder 'name' en 'id' voor " + window.location.pathname);
|
||
thisdoc = document;
|
||
//debugger;
|
||
}
|
||
/* Sommige mobile/3G proxies embedden de CSS 'netjes' zodat het bestand niet te vinden is
|
||
var cssfound = false;
|
||
for (i = 0; i < thisdoc.styleSheets.length; i++)
|
||
{
|
||
try
|
||
{ // TODO faalt op FF?
|
||
if (thisdoc.styleSheets[i].href.match(/suggest.css/i))
|
||
cssfound = true;
|
||
}
|
||
catch(e){};
|
||
}
|
||
if (!cssfound)
|
||
{
|
||
FcltMgr.alert("Internal error. " + document.referrer + " moet suggest.css ook includen!");
|
||
// debugger;
|
||
}
|
||
*/
|
||
}
|
||
var icon = inParams.icon || "fa-asterisk";
|
||
if (params.embedded && params.site)
|
||
var $klikker = $("<div class='suggestklikker' style='cursor:pointer'><img src='" + params.site + "/appl/shared/suggest/suggest.png'></i></div>");
|
||
else
|
||
var $klikker = $("<div class='suggestklikker'><i class='fa fa-fw " + icon + " suggestklikker'></i></div>");
|
||
$(params.queryField).after($klikker)
|
||
.addClass("hasIcon");
|
||
|
||
params.queryField.setAttribute("sgKey", params.initKey);
|
||
// IE6 en IE7 hebben functie hasAttribute niet
|
||
if (!params.queryField.hasAttribute)
|
||
params.queryField.hasAttribute = function (attr) { return !!params.queryField.getAttribute(attr) };
|
||
|
||
if (params.keyField)
|
||
{
|
||
storeKeyField = params.keyField;
|
||
//storeKeyField.value = params.queryField.getAttribute("sgKey");
|
||
storeKeyField.value = params.keyField.value;
|
||
}
|
||
else
|
||
this.withKeyField = function (inStoreKeyField)
|
||
{
|
||
storeKeyField = inStoreKeyField;
|
||
//storeKeyField.value = params.queryField.getAttribute("sgKey");
|
||
storeKeyField.value = params.keyField.value;
|
||
return this;
|
||
}
|
||
|
||
if (params.extraParamField)
|
||
{
|
||
storeExtraParamField = params.extraParamField;
|
||
if (params.queryField.hasAttribute("sgExtraParam"))
|
||
storeExtraParamField.value = params.queryField.getAttribute("sgExtraParam");
|
||
}
|
||
else
|
||
this.withExtraParamField = function (instoreExtraParamField)
|
||
{
|
||
storeExtraParamField = instoreExtraParamField;
|
||
if (params.queryField.hasAttribute("sgExtraParam"))
|
||
storeExtraParamField.value = params.queryField.getAttribute("sgExtraParam");
|
||
return this;
|
||
}
|
||
|
||
// Kan verwijderd worden. Kan ook bereikt worden door initi<74>le waarde -1 mee te geven
|
||
this.withEmpty = function (eKey, eText)
|
||
{
|
||
emptyText = eText? eText : "";
|
||
emptyKey = eKey;
|
||
return this;
|
||
}
|
||
|
||
// Niet extern bedoeld maar noodzakekelijk voor setTimeout
|
||
|
||
this.requestLoop = requestLoop;
|
||
|
||
//if (params.objectName == "sgPerson") FcltMgr.alert("initKey = " + params.initKey);
|
||
//if (params.initKey == -1) FcltMgr.alert(params.objectName + " " + params.initKey);
|
||
// Alleen als verplicht
|
||
if (params.initKey == -1 && !params.noJustOne && $(params.queryField).hasClass('required'))
|
||
CheckJustOne();
|
||
|
||
if (params.fieldReadonly)
|
||
{
|
||
params.queryField.readOnly = true;
|
||
setClass("suggestReadOnly");
|
||
}
|
||
else
|
||
startup();
|
||
|
||
if (params.isBad)
|
||
{
|
||
setValue(emptyKey, null, true, false, emptyText);
|
||
setClass("suggestBad");
|
||
}
|
||
else
|
||
this.isBad = function ()
|
||
{
|
||
setValue(emptyKey, null, true, false, emptyText);
|
||
setClass("suggestBad");
|
||
}
|
||
|
||
/**
|
||
*
|
||
* It all starts here!
|
||
*
|
||
*/
|
||
|
||
function startup() {
|
||
createAutocompletDiv();
|
||
hideAutocompleteDiv();
|
||
params.queryField.autocomplete = "off";
|
||
params.queryField.title = L("lcl_shared_suggest_hint");
|
||
params.queryField.onkeydown = keypressHandler;
|
||
params.queryField.onkeyup = keyupHandler;
|
||
params.queryField.onblur = function ()
|
||
{ if (document.activeElement &&
|
||
typeof thisdoc.activeElement != "unknown" &&
|
||
(document.activeElement == params.queryField ||
|
||
thisdoc.activeElement == _completeDiv ||
|
||
document.activeElement == _comboDiv ||
|
||
document.activeElement == $suggestbusy[0] ||
|
||
document.activeElement == $klikker[0] ||
|
||
$("body").hasClass("modal") || // Zou anders in IE9 sluiten bij klikken op scrollbar
|
||
thisdoc.activeElement.parentElement == _completeDiv))
|
||
return;
|
||
if (isAutocompleteDivVisible())
|
||
{
|
||
if (params.queryField.getAttribute("sgCurrentValue") == null)
|
||
setValues(true);
|
||
else //houden we de huidige waarde
|
||
setValue(params.queryField.getAttribute("sgKey"),
|
||
params.queryField.getAttribute("sgCurrentValue"), false, false,
|
||
params.queryField.getAttribute("sgExtraParam"));
|
||
|
||
hideAutocompleteDiv();
|
||
}
|
||
};
|
||
|
||
// http://stackoverflow.com/questions/3380458/looking-for-a-better-workaround-to-chrome-select-on-focus-bug
|
||
$(params.queryField).focus(function () {
|
||
$(params.queryField).select().mouseup(function (e) {
|
||
e.preventDefault();
|
||
$(this).unbind("mouseup");
|
||
});
|
||
if (!timerID)
|
||
timerID = setTimeout(params.objectName + '.requestLoop(-1);', THROTTLE_PERIOD);
|
||
});
|
||
|
||
$klikker.click(function (e)
|
||
{
|
||
if (!isReadOnly())
|
||
{
|
||
// Als je op vergrootglas hebt gedrukt moet checkExist altijd false zijn want je wilt een lijst.
|
||
// Anders krijg je ook het verschijnsel dat je 2 keer moet klikken voordat je de lijst krijgt.
|
||
params.queryField.removeAttribute("checkExist");
|
||
$(params.queryField).focus(); // zodat anderen door onblur verbergen
|
||
|
||
|
||
if (!isAutocompleteDivVisible())
|
||
{
|
||
autoOpen();
|
||
}
|
||
else
|
||
{
|
||
setValue(params.queryField.getAttribute("sgKey"),
|
||
params.queryField.getAttribute("sgCurrentValue"), false, false,
|
||
params.queryField.getAttribute("sgExtraParam"));
|
||
hideAutocompleteDiv();
|
||
}
|
||
|
||
return;
|
||
|
||
|
||
}
|
||
});
|
||
params.queryField.onmousemove = function (evt)
|
||
{
|
||
if (!isReadOnly())
|
||
{
|
||
evt = evt||window.event;
|
||
var offX = evt.offsetX || evt.layerX; // layerX is FireFox
|
||
if (params.queryField.clientWidth-offX<20) // op de 'combodiv' geklikt
|
||
params.queryField.style.cursor="pointer";
|
||
else
|
||
params.queryField.style.cursor="auto";
|
||
}
|
||
};
|
||
params.queryField.setAttribute("sgCurrentValue", params.queryField.defaultValue);
|
||
requestLoop(0);
|
||
}
|
||
|
||
function setClass(oneClass)
|
||
{
|
||
$(params.queryField).removeClass("suggest suggestBad suggestBusy suggestReadOnly");
|
||
$(params.queryField).addClass(oneClass);
|
||
}
|
||
|
||
function makeReadOnly()
|
||
{
|
||
params.queryField.readOnly = true;
|
||
setClass("suggestReadOnly");
|
||
params.queryField.title = "";
|
||
}
|
||
|
||
function undoReadOnly()
|
||
{
|
||
params.queryField.readOnly = false;
|
||
setClass("suggest");
|
||
params.queryField.title = L("lcl_shared_suggest_hint");
|
||
}
|
||
|
||
function isReadOnly()
|
||
{
|
||
return params.queryField.readOnly;
|
||
}
|
||
|
||
function setValue(key, txt, doonChange, checkExist, extraParam, lastTry)
|
||
{
|
||
// Onthoudt de huidige waarde als je checkt of de te zetten waarde bestaat.
|
||
// Als de nieuwe waarde niet voorkomt in de query en niet kan worden gezet
|
||
// dan wordt geprobeerd om de huidige waarde weer opnieuw te zetten.
|
||
// In dat laatste geval is dan lastTry == true. Indien ook de huidige waarde niet
|
||
// meer aanwezig is in de query, dan wordt de suggestbox leeg gemaakt.
|
||
// Als alleen de nieuwe waarde moet worden geprobeerd,
|
||
// kan ook direct voor lastTry de waarde true worden meegegeven.
|
||
|
||
if (checkExist && !lastTry) {
|
||
currentKey = params.queryField.getAttribute("sgKey");
|
||
currentText = params.queryField.value;
|
||
currentExtraParam = params.queryField.getAttribute("sgExtraParam");
|
||
}
|
||
if (params.queryField)
|
||
{
|
||
// Afhankelijk van het readonly zijn de suggest class zetten zodat de suggestBusy wordt vervangen.
|
||
// De suggest class toont het vergrootglas in beeld.
|
||
if (!isReadOnly())
|
||
setClass("suggest");
|
||
params.queryField.setAttribute("sgKey", key);
|
||
if (storeKeyField) storeKeyField.value = key;
|
||
if (extraParam != null) // De waarde 0 moet niet als false worden opgevat.
|
||
{
|
||
params.queryField.setAttribute("sgExtraParam", extraParam);
|
||
if (storeExtraParamField) storeExtraParamField.value = extraParam;
|
||
} else {
|
||
if (storeExtraParamField) storeExtraParamField.value = ""; // Als storeExtraParamField bestaat dan leeg maken (vullen met "")
|
||
}
|
||
if (!checkExist && txt != null)
|
||
{
|
||
params.queryField.value = txt;
|
||
params.queryField.setAttribute("sgCurrentValue", txt);
|
||
//latestServerQuery = txt.toLowerCase();
|
||
if (txt != "" &&
|
||
(document.activeElement && (typeof thisdoc.activeElement != "unknown") &&
|
||
(document.activeElement == params.queryField ||
|
||
thisdoc.activeElement == _completeDiv ||
|
||
thisdoc.activeElement == _comboDiv ||
|
||
thisdoc.activeElement.parentElement == _completeDiv
|
||
)
|
||
)
|
||
)
|
||
// selectRange zou focus stelen
|
||
// Alleen focus stelen als element al focus had (activeElement)
|
||
selectRange(0,0);
|
||
}
|
||
// bij 'checkExist' hoeven we nu nog geen onChange te doen: die komt later nog wel
|
||
if (!checkExist && doonChange && params.queryField.getAttribute("sgonChange"))
|
||
{
|
||
eval(params.queryField.getAttribute("sgonChange")).call(null, key, txt, {suggestid: params.queryField.id});
|
||
}
|
||
if (checkExist)
|
||
params.queryField.setAttribute("checkExist", true);
|
||
else
|
||
params.queryField.removeAttribute("checkExist");
|
||
|
||
if (doonChange)
|
||
params.queryField.setAttribute("doonChange", doonChange);
|
||
else
|
||
params.queryField.removeAttribute("doonChange");
|
||
if (checkExist) {
|
||
sendQuery(txt, false, checkExist);
|
||
}
|
||
}
|
||
}
|
||
|
||
function getValue()
|
||
{
|
||
return (params.queryField.getAttribute("sgKey"));
|
||
}
|
||
|
||
function process_one_choice_info(data, textStatus)
|
||
{
|
||
if (textStatus == "success")
|
||
{
|
||
// setValue(key, txt, doonChange, checkExist, extraParam, lastTry)
|
||
if (data && data.result.length == 1)
|
||
{
|
||
var txt = data.result[0].txt;
|
||
var key = data.result[0].key;
|
||
var extraParam = data.result[0].extra;
|
||
setValue(key, txt, true, false, extraParam);
|
||
}
|
||
// initiele waarden worden met initkey gezet
|
||
//else
|
||
//setValue(emptyKey, emptyText, true, false, emptyText); // Er zijn geen waarden. Suggestbox leeg maken.
|
||
}
|
||
else
|
||
FcltMgr.alert("process_one_choice_info: " + textStatus);
|
||
}
|
||
|
||
// Een urlAdd kan zijn meegegeven als val, field of jqfield
|
||
function _urlAddParams(urlAdd)
|
||
{
|
||
if (!urlAdd)
|
||
return "";
|
||
|
||
var urlAddParams = "";
|
||
for (var i = 0; i < urlAdd.length; i++)
|
||
{ // Url parameter aan link toevoegen
|
||
urlAddParams += "&" + urlAdd[i].urlParam + "=";
|
||
var theVal = urlAdd[i].val;
|
||
if (typeof theVal == "undefined")
|
||
{
|
||
var selector = urlAdd[i].jqfield;
|
||
var hasselector = "";
|
||
if (!selector || selector == "") // JQuery selector, handig voor radio
|
||
{
|
||
selector = "#" + urlAdd[i].field;
|
||
hasselector = "#has_" + urlAdd[i].field;
|
||
}
|
||
// Is het misschien een checkbox? Dan is de waarde niet belangrijk maar of deze is aangevinkt.
|
||
if (hasselector != "" && $(hasselector).length > 0)
|
||
{ // Het is een checkbox. Controleer of de checkbox is aangevinkt.
|
||
theVal = $(selector).prop("checked")? 1 : 0; // Wel (1) of niet (0) aangevinkt.
|
||
}
|
||
else
|
||
theVal = $(selector).val()? $(selector).val() : -1; // Indien niets geselecteerd dan -1;
|
||
}
|
||
urlAddParams += theVal;
|
||
}
|
||
return urlAddParams;
|
||
}
|
||
|
||
function CheckJustOne()
|
||
{
|
||
var url = params.queryUrl + _urlAddParams(params.urlAdd) + "&k=*";
|
||
// eerste 10 is m<><6D>r dan genoeg url += "&SuggestAll=1&";
|
||
|
||
$.getJSON(url, process_one_choice_info);
|
||
}
|
||
|
||
function reloadUrlAdd(newUrlAdd) {
|
||
params.urlAdd = newUrlAdd;
|
||
}
|
||
|
||
function changeQueryUrl(new_inQueryUrl) {
|
||
params.queryUrl = new_inQueryUrl;
|
||
latestServerQuery = null;
|
||
// gaat mis bij kostenplaats fe res setValue(-1, ""); // TODO: vriendelijk via checkExist?
|
||
}
|
||
|
||
function calculateOffsetLeft(r){
|
||
return SumAttr(r,"offsetLeft")
|
||
}
|
||
|
||
function calculateOffsetTop(r){
|
||
return SumAttr(r,"offsetTop")
|
||
}
|
||
|
||
function SumAttr(r,attr){
|
||
var kb = 0;
|
||
while(r)
|
||
{
|
||
kb += r[attr];
|
||
r = r.offsetParent
|
||
}
|
||
return kb
|
||
}
|
||
|
||
function createAutocompletDiv()
|
||
{
|
||
//_completeDiv=$(thisdoc).find("div#"+params.objectName+"completeDiv")[0];
|
||
// if (true||!_completeDiv)
|
||
{
|
||
_completeDiv=thisdoc.createElement("DIV");
|
||
|
||
_completeDiv.id=params.objectName+"completeDiv";
|
||
_completeDiv.className = "suggestautocompleteContainer";
|
||
_completeDiv.onblur = function () { params.queryField.onblur(); }
|
||
_completeDiv.style.zIndex="2199"; // JQuery dialog begint bij 1000, daar zitten we ruim boven
|
||
|
||
thisdoc.body.appendChild(_completeDiv);
|
||
//_completeDiv.appendChild(thisdoc.createTextNode("DIVVERtje"));
|
||
}
|
||
// pas bij echt openen afmetingen zetten setCompleteDivSize();
|
||
}
|
||
|
||
function setCompleteDivSize()
|
||
{
|
||
var _inputField = params.queryField;
|
||
|
||
if(_inputField && _completeDiv)
|
||
{
|
||
var newdivWidth = Math.min(divWidth, thisdoc.body.offsetWidth); // Voor Mobile
|
||
var newLeft = calculateOffsetLeft(_inputField);
|
||
var newTop =calculateOffsetTop(_inputField)+_inputField.offsetHeight-1;
|
||
if (thisdoc != document)
|
||
{
|
||
newTop += calculateOffsetTop(thisdoc.getElementById(window.name))+2;
|
||
newLeft += calculateOffsetLeft(thisdoc.getElementById(window.name))+2;
|
||
}
|
||
newTop -= $(document).scrollTop();
|
||
if (newLeft + newdivWidth > thisdoc.body.offsetWidth)
|
||
{ // Wel in beeld houden
|
||
newLeft = Math.max(0, thisdoc.body.offsetWidth - newdivWidth - 20); // ook speling voor scrollbar
|
||
}
|
||
_completeDiv.style.left=newLeft+"px";
|
||
_completeDiv.style.top=newTop+"px";
|
||
|
||
_completeDiv.style.width=newdivWidth + "px";
|
||
}
|
||
}
|
||
|
||
function doRequest(keyword, bAll)
|
||
{
|
||
var pos = $(params.queryField).next().position();
|
||
$suggestbusy.css("top", pos.top);
|
||
$suggestbusy.css("left", pos.left);
|
||
window.suggesttimer = setTimeout(function () { $suggestbusy.show(); }, 500);
|
||
|
||
sendQuery(keyword, bAll);
|
||
latestServerQuery = keyword;
|
||
}
|
||
|
||
var ESCAPE = 27;
|
||
var KEYUP = 38;
|
||
var KEYDOWN = 40;
|
||
var KEYENTER = 13;
|
||
var KEYTAB = 9;
|
||
var KEYPGUP = 33;
|
||
var KEYPGDOWN = 34;
|
||
// Deze functie is via een timer gescheduled
|
||
function requestLoop(toetskey)
|
||
{ // Toetskey
|
||
// -1: Geen toets, focus op veld
|
||
// 0: Initialisatie
|
||
// >0: Toets aangeklikt
|
||
|
||
// Als de suggest readonly is hoef ik niet verder te kijken.
|
||
if (isReadOnly()) return;
|
||
setClass("suggest");
|
||
|
||
timerID = null;
|
||
|
||
if (params.queryField.value == "" && params.queryField.getAttribute("sgKey") != emptyKey)
|
||
setValue(emptyKey, emptyText, true, false, emptyText);
|
||
|
||
// Bepaal wat de gebruiker heeft ingetikt exclusief het deel wat gehighlight is
|
||
var keyword = query().toLowerCase();
|
||
|
||
// Is het een te snelle tab zonder popup selectie. Dan is er geen waarde gezet. Focus terug.
|
||
var isToFastTab = (toetskey == KEYTAB && (params.queryField.value != params.queryField.getAttribute("sgCurrentValue")))
|
||
if (isToFastTab)
|
||
params.queryField.select();
|
||
|
||
var isUpDownKey = (toetskey == KEYUP || toetskey == KEYDOWN || toetskey == KEYPGUP || toetskey == KEYPGDOWN);
|
||
var isNoSpecialKey = (toetskey > 0 && !isUpDownKey && toetskey != KEYENTER && toetskey != KEYTAB);
|
||
// Bij toestsaanslag altijd popup scherm. Ook bij default waarde. Omschrijving tekst kan vaker voorkomen.
|
||
if (toetskey != 0 &&
|
||
(keyword!=latestServerQuery) &&
|
||
(keyword != '') &&
|
||
((params.queryField.value != params.queryField.defaultValue) || (toetskey > 0 && !isUpDownKey)) &&
|
||
(params.queryField.value != params.queryField.getAttribute("sgCurrentValue") || isNoSpecialKey))
|
||
{
|
||
doRequest(query().toLowerCase(), false);
|
||
}
|
||
|
||
if (params.queryField.value == '' || params.queryField.value == params.queryField.defaultValue)
|
||
{
|
||
hideAutocompleteDiv();
|
||
suggestionValues = [];
|
||
latestServerQuery = null;
|
||
selected = 0;
|
||
if (params.queryField.value == params.queryField.defaultValue && toetskey == 0)
|
||
setValue(params.initKey, null, false, false, params.initExtraParam)
|
||
else if (params.queryField.value == "" && params.queryField.getAttribute("sgKey") != emptyKey)
|
||
setValue(emptyKey, emptyText, true, false, emptyText);
|
||
if (keyword == "" && toetskey > 0)
|
||
{ // Eventuele sgCascade uitvoeren als suggest veld wordt leeg gemaakt.
|
||
eval(params.queryField.getAttribute("sgCascade"));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Bepaal wat de gebruiker heeft ingetikt exclusief het deel wat gehighlight is
|
||
function query()
|
||
{
|
||
var textbox = params.queryField;
|
||
var N;
|
||
if(textbox.createTextRange) // IE
|
||
{ // IE6/7 soms bij het opstarten: htmlfile: unspecified error. Daarom de catch
|
||
try
|
||
{
|
||
var fa = document.selection.createRange().duplicate();
|
||
N = fa.text.length;
|
||
}
|
||
catch(e)
|
||
{
|
||
N=0;
|
||
}
|
||
}
|
||
else
|
||
if(textbox.setSelectionRange) // Mozilla
|
||
{
|
||
try
|
||
{
|
||
N = textbox.selectionEnd-textbox.selectionStart;
|
||
}
|
||
catch(e) // Firefox-->Error
|
||
{
|
||
N = 0;
|
||
}
|
||
}
|
||
return textbox.value.substring(0, textbox.value.length-N);
|
||
}
|
||
|
||
function sendQuery(txt, bAll, checkExist)
|
||
{
|
||
var url = params.queryUrl + _urlAddParams(params.urlAdd) + "&k=" + myEncode(txt);
|
||
if (bAll) url += "&SuggestAll=1";
|
||
if (checkExist) url += "&checkExist=1";
|
||
|
||
jQuery.ajax({type: "GET",
|
||
url: url,
|
||
dataType: params.JSONP? "jsonp" : "json",
|
||
success: process
|
||
});
|
||
}
|
||
|
||
// Callback functie van HTTP Request
|
||
function process(data, textStatus)
|
||
{
|
||
clearTimeout(window.suggesttimer);
|
||
$suggestbusy.hide();
|
||
|
||
var checkExist = params.queryField.hasAttribute("checkExist");
|
||
var doonChange = params.queryField.getAttribute("doonChange");
|
||
|
||
// Afhankelijk van het readonly zijn de suggest class zetten zodat de suggestBusy wordt vervangen.
|
||
// De suggest class toont het vergrootglas in beeld.
|
||
if (!isReadOnly())
|
||
setClass("suggest");
|
||
|
||
showAutocompleteDiv();
|
||
resultsObj = data;
|
||
selected = resultsObj.isAll? Math.min(resultsObj.result.length - 1, 10 - 1) : 0;
|
||
|
||
// voorheen htmlFormat(checkExist, doonChange), nu ingeplakt
|
||
|
||
lastHighlight = null;
|
||
while(_completeDiv.childNodes.length > 0)
|
||
{
|
||
_completeDiv.removeChild(_completeDiv.childNodes[0]);
|
||
}
|
||
|
||
if (!resultsObj.result.length) // Hmm, geen resultaten gevonden
|
||
{
|
||
if (params.queryField.value.length > 0 && params.queryField.value != "*")
|
||
{
|
||
// Als de nieuwe waarde niet voorkomt in de query en niet kan worden gezet
|
||
// dan wordt geprobeerd om de huidige waarde weer opnieuw te zetten (currentKey > 0).
|
||
// Indien ook de huidige waarde niet meer aanwezig is in de query,
|
||
// dan wordt de suggestbox leeg gemaakt.
|
||
if (checkExist && currentKey > 0)
|
||
{
|
||
var pcurrentKey = currentKey;
|
||
var pcurrentText = currentText;
|
||
var pcurrentExtraParam = currentExtraParam;
|
||
currentKey = emptyKey;
|
||
currentText = "";
|
||
currentExtraParam = null;
|
||
setValue(pcurrentKey, pcurrentText, doonChange, true, pcurrentExtraParam, true);
|
||
}
|
||
else {
|
||
setValue(emptyKey, (checkExist? "" : null), true, false, (checkExist? "" : null));
|
||
}
|
||
if (!checkExist)
|
||
{
|
||
setClass("suggestBad");
|
||
}
|
||
}
|
||
else
|
||
{ // Geen resultaten gevonden en params.queryField was leeg.
|
||
// Zet nu opnieuw de lege waarde.
|
||
setValue(-1, "", true, false, null, true);
|
||
}
|
||
hideAutocompleteDiv();
|
||
return;
|
||
}
|
||
|
||
// Maak suggestbox leeg indien checkExist is true en gevonden key en ingevoerde key komen niet overeen:
|
||
if (checkExist && resultsObj.result.length == 1 && resultsObj.result[0].key != params.queryField.getAttribute("sgKey"))
|
||
{ // Maak suggestbox leeg
|
||
if (params.queryField.value.length > 0)
|
||
{
|
||
setValue(emptyKey, "", true, false, "");
|
||
setClass("suggestBad");
|
||
}
|
||
hideAutocompleteDiv();
|
||
return;
|
||
}
|
||
// Indien checkExist is true en er zijn meerdere waarden gevonden (Begin gedeelte van de omschrijving komt overeen) dan:
|
||
// 1) Komt maar van 1 van deze de gevonden key en ingevoerde key overeen, dan deze selecteren
|
||
// 2) Anders selectbox leeg maken.
|
||
else if (checkExist && resultsObj.result.length > 1)
|
||
{
|
||
var nr_of_hits = 0;
|
||
for (var i = 0; i < resultsObj.result.length; i++)
|
||
{
|
||
if (resultsObj.result[i].key == params.queryField.getAttribute("sgKey"))
|
||
{
|
||
nr_of_hits++;
|
||
var hit_i = i;
|
||
}
|
||
}
|
||
if (nr_of_hits == 1)
|
||
{ // Geselecteerde text selecteren (enige overeenkomende resultaat van de meerdere gevonden waarden)
|
||
selected = hit_i; // Kies wel de juiste overeenkomende resultaat met de juiste key
|
||
setValues(doonChange);
|
||
if (!checkExist) selectRange(0,0);
|
||
hideAutocompleteDiv();
|
||
return;
|
||
}
|
||
else
|
||
{ // Gevonden key en ingevoerde key komen vaker overeen. Maak suggestbox leeg
|
||
if (params.queryField.value.length > 0)
|
||
{
|
||
setValue(emptyKey, "", true, false, "");
|
||
setClass("suggestBad");
|
||
}
|
||
hideAutocompleteDiv();
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (checkExist && resultsObj.result.length == 1 && resultsObj.result[0].key == params.queryField.getAttribute("sgKey"))
|
||
{
|
||
// geselecteerde text selecteren (enige resultaat)
|
||
setValues(doonChange);
|
||
if (!checkExist) selectRange(0,0);
|
||
hideAutocompleteDiv();
|
||
return;
|
||
}
|
||
|
||
|
||
suggestionValues = [];
|
||
if (params.queryField.getAttribute("sgCurrentValue") && params.queryField.getAttribute("sgKey") > 0)
|
||
{
|
||
// Plak de current er voor als eerste keuze
|
||
resultsObj.result = [ { txt: params.queryField.getAttribute("sgCurrentValue"),
|
||
key: params.queryField.getAttribute("sgKey"),
|
||
desc: L("lcl_shared_suggest_huidig")}].concat(resultsObj.result);
|
||
}
|
||
|
||
if (resultsObj.similar)
|
||
$(_completeDiv).addClass("suggestsimilar");
|
||
else
|
||
$(_completeDiv).removeClass("suggestsimilar");
|
||
|
||
for (var i = 0; i < resultsObj.result.length; i++)
|
||
{
|
||
if (resultsObj.result[i].txt)
|
||
{
|
||
suggestionValues.push(resultsObj.result[i].txt);
|
||
|
||
var xDiv = thisdoc.createElement("div");
|
||
xDiv.sgIndex = i;
|
||
if (i==selected)
|
||
{
|
||
xDiv.className = "suggestsrs";
|
||
lastHighlight = xDiv;
|
||
}
|
||
else
|
||
{
|
||
xDiv.className = "suggestsr";
|
||
}
|
||
|
||
xDiv.onmouseover = function() { highlight(this.sgIndex, this); return false; };
|
||
xDiv.onmousedown = function() { setValues(true);hideAutocompleteDiv(); return true; };
|
||
if ("title" in resultsObj.result[i])
|
||
xDiv.title = resultsObj.result[i].title;
|
||
if (resultsObj.anyfoto)
|
||
{
|
||
var fotourl = resultsObj.result[i].foto;
|
||
if (fotourl && fotourl.match(/\.(png|jpg|jpeg)$/i)) // het is een image
|
||
{
|
||
var xFoto = thisdoc.createElement("img");
|
||
xFoto.setAttribute("src", fotourl);
|
||
xFoto.setAttribute("height", "40");
|
||
xFoto.className = "suggestfoto";
|
||
xDiv.appendChild(xFoto);
|
||
}
|
||
else
|
||
{
|
||
var xFoto = thisdoc.createElement("div");
|
||
xFoto.innerHTML = " ";
|
||
xFoto.className = "suggestfoto";
|
||
xDiv.appendChild(xFoto);
|
||
}
|
||
}
|
||
var xSpan = thisdoc.createElement("span");
|
||
xSpan.className = "suggestsrt";
|
||
if (i == 0 &&
|
||
params.queryField.getAttribute("sgCurrentValue") &&
|
||
params.queryField.getAttribute("sgKey") > 0) // eerste krijgt aparte opmaak
|
||
{
|
||
xSpan.style.borderBottom = "1px solid black"; // TODO: Stijl voor verzinnen
|
||
xSpan.style.paddingBottom = "10px";
|
||
}
|
||
|
||
var ll = 0;
|
||
// first matching part bold
|
||
var lsq = latestServerQuery;
|
||
if (lsq && lsq.charAt(0) == "*") lsq = lsq.substring(1);
|
||
ll = resultsObj.result[i].txt.toLowerCase().indexOf(lsq);
|
||
if (resultsObj.result[i].isHTML)
|
||
xSpan.innerHTML = resultsObj.result[i].txt;
|
||
else if (ll >= 0 && lsq.length)
|
||
{
|
||
if (ll > 0)
|
||
xSpan.appendChild(thisdoc.createTextNode(resultsObj.result[i].txt.substring(0, ll)));
|
||
var xb = thisdoc.createElement("b");
|
||
xb.appendChild(thisdoc.createTextNode(resultsObj.result[i].txt.substring(ll, ll + lsq.length)));
|
||
xSpan.appendChild(xb);
|
||
xSpan.appendChild(thisdoc.createTextNode(resultsObj.result[i].txt.substring(ll + lsq.length)));
|
||
}
|
||
else
|
||
xSpan.appendChild(thisdoc.createTextNode(resultsObj.result[i].txt));
|
||
|
||
xDiv.appendChild(xSpan);
|
||
|
||
if ("desc" in resultsObj.result[i]) // Extra description present
|
||
{
|
||
var x3 = thisdoc.createElement("span");
|
||
x3.className = "suggestsrc";
|
||
var desc = resultsObj.result[i].desc || "";
|
||
ll = desc && desc.toLowerCase().indexOf(lsq);
|
||
if (params.highlightDescript && ll >= 0)
|
||
{
|
||
x3.appendChild(thisdoc.createTextNode(desc.substring(0, ll)));
|
||
var xb = thisdoc.createElement("b");
|
||
xb.appendChild(thisdoc.createTextNode(desc.substring(ll, ll + lsq.length)));
|
||
x3.appendChild(xb);
|
||
x3.appendChild(thisdoc.createTextNode(desc.substring(ll + lsq.length)));
|
||
}
|
||
else
|
||
x3.appendChild(thisdoc.createTextNode(desc));
|
||
xDiv.appendChild(x3);
|
||
}
|
||
|
||
_completeDiv.appendChild(xDiv);
|
||
}
|
||
}
|
||
|
||
// xDiv is op dit moment de laatste
|
||
if (!xDiv || (xDiv.offsetTop + xDiv.clientHeight < 300))
|
||
_completeDiv.style.height = ""; // Auto
|
||
else
|
||
{ //_completeDiv.style.overflow = "hidden";
|
||
_completeDiv.style.height = "300px";
|
||
if (/MSIE 7.0/.test(navigator.userAgent))
|
||
_completeDiv.style.paddingRight = "16px"; // Anders valt bij IE7 een deel achter de scrollbar
|
||
_completeDiv.style.overflowY = "auto";
|
||
_completeDiv.style.overflowX = "hidden";
|
||
|
||
}
|
||
|
||
if (resultsObj.hasMore) // Is gezet als er 10 of meer records zijn
|
||
{
|
||
var xDiv = thisdoc.createElement("div");
|
||
|
||
xDiv.className = "suggestsr";
|
||
var xSpan = thisdoc.createElement("span");
|
||
xSpan.className = "suggestMore";
|
||
if (resultsObj.isAll)
|
||
xSpan.appendChild(thisdoc.createTextNode(resultsObj.hasMoreMsg)); // Maximum bereikt
|
||
else
|
||
{
|
||
$(xDiv).mousedown(function (evt) {
|
||
doRequest(latestServerQuery, true);
|
||
evt.preventDefault(); // Voorkom blur
|
||
});
|
||
xSpan.appendChild(thisdoc.createTextNode(resultsObj.hasMoreMsg)); // Er is meer
|
||
}
|
||
xDiv.appendChild(xSpan);
|
||
_completeDiv.appendChild(xDiv);
|
||
}
|
||
//requestSuggestions();
|
||
}
|
||
|
||
function keyupHandler (evt)
|
||
{
|
||
// make sure we have a valid event variable
|
||
if(!evt && window.event) {
|
||
evt = window.event;
|
||
}
|
||
var key = evt.keyCode;
|
||
|
||
if (key < 32 || (key >= 33 && key <= 46) || (key >= 112 && key <= 123))
|
||
{
|
||
//ignore
|
||
}
|
||
else
|
||
{
|
||
//request suggestions from the suggestion provider
|
||
// Backspace key(8), Delete key (48)
|
||
if (key != 8 && key != 48)
|
||
{
|
||
requestSuggestions();
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function keypressHandler (evt)
|
||
{
|
||
params.queryField.removeAttribute("checkExist");
|
||
params.queryField.setAttribute("doonChange", true);
|
||
|
||
// make sure we have a valid event variable
|
||
if (!evt && window.event)
|
||
{
|
||
evt = window.event;
|
||
}
|
||
var key = evt.keyCode;
|
||
|
||
// if this key isn't one of the ones we care about, just return
|
||
|
||
if (timerID) // reset oude. Is goed voor de performance/response
|
||
clearTimeout(timerID);
|
||
timerID = setTimeout(params.objectName + ".requestLoop(" + key + ");", THROTTLE_PERIOD);
|
||
|
||
// don't do anything if the div is hidden
|
||
if (!isAutocompleteDivVisible())
|
||
{
|
||
if (key == KEYDOWN || key == KEYPGDOWN)
|
||
autoOpen();
|
||
return;
|
||
}
|
||
|
||
if (key == KEYPGDOWN)
|
||
{
|
||
if (resultsObj.hasMore && !resultsObj.isAll)
|
||
{
|
||
doRequest(latestServerQuery, true);
|
||
}
|
||
else if (isAutocompleteDivVisible)
|
||
{
|
||
_completeDiv.scrollTop += _completeDiv.clientHeight;
|
||
//selected += 20;
|
||
//highlight(selected);
|
||
}
|
||
return false;
|
||
}
|
||
if (key == KEYPGUP)
|
||
{
|
||
if (isAutocompleteDivVisible)
|
||
{
|
||
_completeDiv.scrollTop -= _completeDiv.clientHeight;
|
||
//selected -= 20;
|
||
//highlight(selected);
|
||
}
|
||
return false;
|
||
}
|
||
if (key == KEYUP)
|
||
{
|
||
if ((selected-1) >= 0)
|
||
selected = selected -1;
|
||
else if (!resultsObj.hasMore)
|
||
selected = resultsObj.result.length-1;
|
||
highlight(selected);
|
||
return false;
|
||
}
|
||
|
||
if (key == KEYDOWN)
|
||
{
|
||
if ((selected + 1) < resultsObj.result.length)
|
||
selected = selected + 1;
|
||
else if (resultsObj.hasMore && !resultsObj.isAll)
|
||
doRequest(latestServerQuery, true);
|
||
else
|
||
selected = 0;
|
||
highlight(selected);
|
||
return false;
|
||
}
|
||
|
||
if (key == ESCAPE)
|
||
{
|
||
params.queryField.onblur();
|
||
}
|
||
if (key == KEYENTER)
|
||
{
|
||
setValues(true);
|
||
selectRange(0,0);
|
||
hideAutocompleteDiv();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// De gewone scrollIntoView() heeft wel eens de neiging de complete pagina ook te scrollen
|
||
function myScrollIntoView(theDiv, toView)
|
||
{
|
||
if (theDiv.scrollTop + theDiv.clientHeight < toView.offsetTop + toView.clientHeight)
|
||
theDiv.scrollTop = toView.offsetTop + toView.clientHeight - theDiv.clientHeight;
|
||
if (theDiv.scrollTop > toView.offsetTop)
|
||
theDiv.scrollTop = toView.offsetTop;
|
||
}
|
||
|
||
function highlight(number)
|
||
{
|
||
selected = number;
|
||
// Herstel vorige highlight
|
||
if (lastHighlight && lastHighlight.className)
|
||
lastHighlight.className = "suggestsr";
|
||
// Zet nieuwe highlight
|
||
var ieDIVs = $(_completeDiv).find('div').not(".suggestfoto");
|
||
lastHighlight = ieDIVs[selected];
|
||
if (lastHighlight && lastHighlight.className)
|
||
lastHighlight.className = "suggestsrs";
|
||
myScrollIntoView(_completeDiv, lastHighlight);
|
||
}
|
||
|
||
function setValues(doonChange)
|
||
{
|
||
if (resultsObj)
|
||
{
|
||
var result = resultsObj.result[selected][resultsObj.storeDesc? "desc" : "txt"];
|
||
setValue(resultsObj.result[selected].key, result, doonChange, false, resultsObj.result[selected].extra) // doonChange
|
||
}
|
||
}
|
||
|
||
function autoOpen()
|
||
{
|
||
params.queryField.value = latestServerQuery || "*"; //"*";
|
||
doRequest(params.queryField.value, false);
|
||
if (params.queryField.createTextRange)
|
||
{
|
||
// Alleen IE komt hier, FF niet!
|
||
// alert(JSON.stringify(params));
|
||
// var el = document.getElementById("sgRoom");
|
||
// var range = el.createTextRange();
|
||
// range.move("character", 1);
|
||
// range.select();
|
||
var oRange = params.queryField.createTextRange();
|
||
// alert(JSON.stringify(params.queryField)); // = {"jQuery{blablabla": 103}
|
||
// alert(JSON.stringify(oRange)); // oRange = {}
|
||
// oRange.collapse(true);
|
||
// oRange.moveEnd('character', 1);
|
||
// oRange.moveStart('character', 1);
|
||
oRange.move("character", 1);
|
||
oRange.select();
|
||
// oRange.setSelectionRange(1,1);
|
||
}
|
||
// TODO: else wat voor FireFox?
|
||
//set focus back to the textbox (when field is not disabled)
|
||
//if (!textbox.disabled) textbox.focus();
|
||
|
||
//params.queryField.focus();
|
||
//selectRange(0,1);
|
||
}
|
||
|
||
function showAutocompleteDiv()
|
||
{
|
||
setCompleteDivSize(); // voor de zekerheid
|
||
_completeDiv.style.display="block";
|
||
}
|
||
|
||
function hideAutocompleteDiv()
|
||
{
|
||
_completeDiv.style.display="none";
|
||
}
|
||
|
||
function isAutocompleteDivVisible()
|
||
{
|
||
return (_completeDiv.style.display == "block");
|
||
}
|
||
|
||
function requestSuggestions()
|
||
{
|
||
var sTextboxValue = query().toLowerCase();
|
||
var aSuggestions = [];
|
||
|
||
if (suggestionValues.length > 0)
|
||
{ //search for matching states
|
||
for (var i=0; i < suggestionValues.length; i++)
|
||
{
|
||
if (suggestionValues[i].toLowerCase().indexOf(sTextboxValue) == 0)
|
||
aSuggestions.push(suggestionValues[i]);
|
||
}
|
||
autosuggest(aSuggestions);
|
||
}
|
||
};
|
||
|
||
function autosuggest(aSuggestions)
|
||
{ //make sure there's at least one suggestion
|
||
if (aSuggestions.length == 1)
|
||
typeAhead(aSuggestions[0]);
|
||
}
|
||
|
||
function typeAhead(sSuggestion)
|
||
{
|
||
var textbox = params.queryField;
|
||
//check for support of typeahead functionality
|
||
if (textbox.createTextRange || textbox.setSelectionRange)
|
||
{
|
||
var iLen = query().length;
|
||
textbox.value = sSuggestion;
|
||
selectRange(iLen, sSuggestion.length);
|
||
}
|
||
};
|
||
|
||
function selectRange(iStart, iLength)
|
||
{
|
||
var textbox = params.queryField;
|
||
//use text ranges for Internet Explorer
|
||
if (textbox.createTextRange)
|
||
{
|
||
var oRange = textbox.createTextRange();
|
||
oRange.moveStart("character", iStart);
|
||
oRange.moveEnd("character", iLength - textbox.value.length);
|
||
oRange.select();
|
||
//use setSelectionRange() for Mozilla
|
||
}
|
||
else if (textbox.setSelectionRange)
|
||
{
|
||
textbox.setSelectionRange(iStart, iLength);
|
||
}
|
||
|
||
//set focus back to the textbox (when field is not disabled)
|
||
if (!textbox.disabled) textbox.focus();
|
||
};
|
||
} |