//==============================================================================
// Copyright 2010-2025 Moraware, Inc.
//
// Purpose: Supports a search popup window for assisted selection list
// population. Allows you to type in the textbox and
// (asynchronously) bring up a selection list of available options.
//
//==============================================================================
/* eslint no-unused-vars: "off" */
/* eslint no-eval: "off" */
/*global
getObj
trim
htmlEncode
checkResponseForError
doPositionPicker
setFocus
// ConsoleLogging.js
ConsoleLogging
// Found in ShopSharedFunctions.js
cancelEvent
mjtElemData
// Found in ShopSharedFullFunctions.js
checkAttribute
stringStartsWith
// StringificationUtils.js
StringificationUtils
// Found in ClientFunctions.js
setFocusIfFocusable
scrollElementIntoViewWithinContainerElement
*/
var g_typeAheadTextChangeTimer = null;
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function isTypeaheadDivShowing(objTypeAheadDiv_) {
return objTypeAheadDiv_.style.display !== 'none';
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function setInvalidTextClass(objTargetField_, isInvalid_) {
var strClassName = objTargetField_.className,
strInvalidClassName = 'invalidTextField';
if (isInvalid_) {
if (strClassName.indexOf(strInvalidClassName) < 0) {
strClassName = trim(strClassName + ' ' + strInvalidClassName);
}
} else {
strClassName = trim(strClassName.replace(strInvalidClassName, ''));
}
objTargetField_.className = strClassName;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function updateTypeaheadTextboxValidation(params_) {
var objTypeAheadDiv = params_.typeaheadDiv;
if (objTypeAheadDiv.typeAheadConfigParams && objTypeAheadDiv.typeAheadConfigParams.forceMatchItem) {
// If the field doesn't match an item, mark it as invalid...
var theForm = getObj(objTypeAheadDiv.typeAheadConfigParams.formId) || 0,
objTextField = theForm[objTypeAheadDiv.typeAheadConfigParams.textFieldName] || 0,
objIdField = theForm[objTypeAheadDiv.typeAheadConfigParams.idFieldName] || 0,
strTextFieldValue = objTextField.value || '';
if (objIdField.value || !strTextFieldValue.length) {
// Either there is a matching selection or the field is empty.
// Regardless, clear the invalid marker.
setInvalidTextClass(
objTextField,
0); // isInvalid_
} else if (objTextField.form &&
objTextField.form.ignoreFieldValue &&
objTextField.form.ignoreFieldValue.value === strTextFieldValue) {
setInvalidTextClass(
objTextField,
0); // isInvalid_
} else {
setInvalidTextClass(
objTextField,
1); // isInvalid_
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function hideTypeAheadDiv() {
var objTypeAheadDiv = getObj('typeAheadDiv'),
rc = 0;
if (objTypeAheadDiv) {
//Before hiding the typeahead div
//update the validation status if necessary
updateTypeaheadTextboxValidation({
typeaheadDiv: objTypeAheadDiv
});
rc = isTypeaheadDivShowing(objTypeAheadDiv);
objTypeAheadDiv.scrollTop = 0;
objTypeAheadDiv.ignoreBlur = false;
objTypeAheadDiv.typeAheadConfigParams = null;
objTypeAheadDiv.innerHTML = '';
objTypeAheadDiv.style.display = 'none';
}
return rc;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function hideTypeAheadDivIfNotTypeAheadDiv(clickedElement_) {
var objTypeAheadDiv = getObj('typeAheadDiv'),
objTypeAheadConfigParams =
(objTypeAheadDiv || {}).typeAheadConfigParams;
if (objTypeAheadConfigParams) {
var doHideTypeAhead = true,
theForm = getObj(objTypeAheadConfigParams.formId),
objTextField =
(theForm || {})[objTypeAheadConfigParams.textFieldName];
if (objTextField) {
doHideTypeAhead = clickedElement_ !== objTextField;
}
if (doHideTypeAhead) {
hideTypeAheadDiv();
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function fireTypeAheadOnChangeIfNecessary(params_) {
var objTextControl = params_.textField,
tmpUseAsynchForOnChange = params_.useAsynchForOnChange,
strOnChangeFunction = objTextControl.getAttribute('mjttypeaheadonchange');
if (strOnChangeFunction) {
if (tmpUseAsynchForOnChange) {
setTimeout(function () {
eval(unescape(strOnChangeFunction));
}, 0);
} else {
eval(unescape(strOnChangeFunction));
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function handleExplicitTypeAheadSelection(params_) {
params_ = params_ || {};
var objResultOptions = params_.resultOptions,
rc = false,
objTypeAheadDiv = getObj('typeAheadDiv');
if (objTypeAheadDiv) {
var typeAheadConfigParams = objTypeAheadDiv.typeAheadConfigParams,
fnHandleExplicitSelection =
typeAheadConfigParams ?
StringificationUtils.getOrResolveFunction({
functionNameOrReference:
typeAheadConfigParams.fnHandleExplicitSelection,
descriptionOfFunctionBeingResolved:
'typeAheadConfigParams.fnHandleExplicitSelection',
suppressErrorsOnMissingNameOrReference: 1
}) :
null,
fnHandleExplicitSelectionP =
typeAheadConfigParams ?
StringificationUtils.getOrResolveFunction({
functionNameOrReference:
typeAheadConfigParams.fnHandleExplicitSelectionP,
descriptionOfFunctionBeingResolved:
'typeAheadConfigParams.fnHandleExplicitSelectionP',
suppressErrorsOnMissingNameOrReference: 1
}) :
null,
objExplicitSelectionResultOptions = {
};
if (fnHandleExplicitSelection || fnHandleExplicitSelectionP) {
if (typeof objTypeAheadDiv.selectedRowIndex !== 'undefined' &&
objTypeAheadDiv.selectedRowIndex !== null &&
objTypeAheadDiv.selectedRowIndex > -1) {
var objSelectedRow = getObj('typeAheadRow' + objTypeAheadDiv.selectedRowIndex);
if (objSelectedRow) {
if (fnHandleExplicitSelectionP) {
fnHandleExplicitSelectionP({
idxSelectedRow: objTypeAheadDiv.selectedRowIndex,
selectedRow: objSelectedRow,
formId: typeAheadConfigParams.formId,
typeAheadConfigParams: typeAheadConfigParams,
explicitSelectionFnParameter: typeAheadConfigParams.explicitSelectionFnParameter,
arrTypeaheadRowVals: mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals') || null,
explicitSelectionResultOptions: objExplicitSelectionResultOptions
});
if (objResultOptions) {
objResultOptions.suppressSetFocus =
objExplicitSelectionResultOptions.suppressSetFocus;
}
} else {
fnHandleExplicitSelection(
objTypeAheadDiv.selectedRowIndex,
objSelectedRow,
typeAheadConfigParams.formId,
typeAheadConfigParams,
typeAheadConfigParams.explicitSelectionFnParameter,
mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals') || null);
}
rc = true;
} else {
if (typeAheadConfigParams.fnHandleExplicitEmptySelection) {
if (typeAheadConfigParams.fnHandleExplicitEmptySelection()) {
rc = true;
}
}
}
} else {
if (typeAheadConfigParams.fnHandleExplicitEmptySelection) {
if (typeAheadConfigParams.fnHandleExplicitEmptySelection()) {
rc = true;
}
}
}
}
}
return rc;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function scrollTypeaheadSelectionIntoViewIfNecessary() {
var objTypeAheadDiv = getObj('typeAheadDiv');
if (!objTypeAheadDiv.typeAheadConfigParams) {
// The type-ahead has already been disimissed.
return;
}
if (-1 === objTypeAheadDiv.selectedRowIndex) {
// No selection...no need to scroll...
return;
}
var objSelectedRow = getObj('typeAheadRow' + objTypeAheadDiv.selectedRowIndex);
if (objSelectedRow) {
scrollElementIntoViewWithinContainerElement(objSelectedRow, objTypeAheadDiv);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function getLastTypeaheadDisplayValue(objTextField_) {
return objTextField_.getAttribute('data-mjtLastTypeAheadDisplayValue');
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function setLastTypeaheadDisplayValue(objTextField_, displayValue_, dontSetFieldValue_) {
objTextField_.setAttribute('data-mjtLastTypeAheadDisplayValue', displayValue_);
if (!dontSetFieldValue_) {
objTextField_.value = displayValue_;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function checkIfTypeaheadDisplayValueChangedAndSetLastValue(objTextField_) {
var strLastValue = getLastTypeaheadDisplayValue(objTextField_),
strCurrValue = objTextField_.value,
rc = strLastValue !== strCurrValue;
if (rc) {
setLastTypeaheadDisplayValue(
objTextField_,
strCurrValue, // displayValue_
1); // dontSetFieldValue_
}
return rc;
}
//------------------------------------------------------------------------------
//
// TODO: Ideally, we'd simply be using element.classList to add and remove the
// style classes we need. However, to keep differences simpler until such time
// as we at least remove v1 support, this function will be used to set a class
// while exclusively preserving the new "typeAheadPriorSelection" style class
// and ignoring any others (as has always been done).
//
//------------------------------------------------------------------------------
function setClassWhileRetainingPriorSelectionInd(params_) {
var objElement = params_.targetElement,
strNewClassName = params_.newClassName,
tmpWasMarkedPriorSelection;
if (objElement) {
tmpWasMarkedPriorSelection =
objElement.classList.contains('typeAheadPriorSelection');
objElement.className =
strNewClassName +
(tmpWasMarkedPriorSelection ? ' typeAheadPriorSelection' : '');
objElement.setAttribute('mjtclass', objElement.className);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function selectTypeAheadItem(rowIndex_, activeSelection_, setFocus_, event) {
var rc = false,
objTypeAheadDiv = getObj('typeAheadDiv');
if (event && event.button && event.button !== 1) {
return false;
}
if (!objTypeAheadDiv.typeAheadConfigParams) {
// Already possible duplicate select call.
// Regardless, there's no typeahead config, so there's nothing to do...
return false;
}
objTypeAheadDiv.ignoreBlur = setFocus_ ? true : false;
if (objTypeAheadDiv.selectedRowIndex !== rowIndex_) {
setClassWhileRetainingPriorSelectionInd({
targetElement: getObj(
'typeAheadRow' + objTypeAheadDiv.selectedRowIndex),
newClassName: 'typeAheadRow'
});
}
objTypeAheadDiv.selectedRowIndex = rowIndex_;
var objSelectedRow = getObj('typeAheadRow' + rowIndex_),
objTypeaheadConfigParams = objTypeAheadDiv.typeAheadConfigParams,
strFormId = objTypeaheadConfigParams.formId,
strTextFieldName = objTypeaheadConfigParams.textFieldName,
strIdFieldName = objTypeaheadConfigParams.idFieldName,
tmpAsyncFocusOnSelect = objTypeaheadConfigParams.asyncFocusOnSelect,
theForm = getObj(strFormId),
objCurrText = theForm[strTextFieldName],
objExplicitSelectionResultOptions = {};
if (objSelectedRow) {
rc = true;
// Highlight the newly selected row...
setClassWhileRetainingPriorSelectionInd({
targetElement: objSelectedRow,
newClassName: 'typeAheadSelectedRow'
});
// Now copy the value of the newly selected row into the textbox.
var objCurrId = theForm[strIdFieldName];
// Got a valid selection, so clear the invalid indicator.
setInvalidTextClass(
objCurrText,
0); // isInvalid_
// Be sure to ignore changes here.
// Don't wipe out the list simply because they've arrowed down...
objTypeAheadDiv.ignoreTextChange = true;
if (g_typeAheadTextChangeTimer) {
clearTimeout(g_typeAheadTextChangeTimer);
}
setLastTypeaheadDisplayValue(
objCurrText, // objTextField_
unescape(objSelectedRow.getAttribute('mjtvalue'))); // displayValue_, dontSetFieldValue_
objCurrId.value = unescape(objSelectedRow.getAttribute('mjtid'));
objTypeAheadDiv.ignoreTextChange = false;
scrollTypeaheadSelectionIntoViewIfNecessary();
fireTypeAheadOnChangeIfNecessary({
textField: objCurrText,
useAsynchForOnChange: false
});
if (activeSelection_) {
handleExplicitTypeAheadSelection({
resultOptions: objExplicitSelectionResultOptions
});
}
}
if (setFocus_) {
if (!objExplicitSelectionResultOptions.suppressSetFocus) {
if (tmpAsyncFocusOnSelect) {
setTimeout(
function () {
setFocus(objCurrText);
},
0);
} else {
setFocus(objCurrText);
}
}
if (activeSelection_) {
// Hide the type-ahead since we're actively selecting an item
// and the focus event might otherwise force it to re-display.
hideTypeAheadDiv();
}
}
return rc;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function removeTypeAheadSelectionOrHoverHighlight(removeSelHightlight_) {
var i,
objRow,
strTargetClass =
removeSelHightlight_ ?
'typeAheadSelectedRow' :
'typeAheadHoverRow';
objRow = getObj('typeAheadRow0');
for (i = 0; objRow; ++i) {
if (objRow.className === strTargetClass) {
objRow.className = 'typeAheadRow';
if (!removeSelHightlight_) {
var objTypeAheadDiv = getObj('typeAheadDiv');
objTypeAheadDiv.selectedRowIndex = i;
}
}
objRow = getObj('typeAheadRow' + (i + 1));
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function mousedownOnTypeahead(event) {
var objTypeAheadDiv = getObj('typeAheadDiv');
objTypeAheadDiv.ignoreBlur = true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function blurTypeahead(event) {
hideTypeAheadDiv();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function setFocusToTextField(formId_, fieldName_) {
var theForm = getObj(formId_);
if (theForm) {
var objField = theForm[fieldName_];
if (objField) {
setFocus(objField);
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function clearSelection() {
var objTypeAheadDiv = getObj('typeAheadDiv');
if (objTypeAheadDiv) {
var objSelectedRow = getObj('typeAheadRow' + objTypeAheadDiv.selectedRowIndex);
if (objSelectedRow) {
objSelectedRow.className = 'typeAheadRow';
objTypeAheadDiv.selectedRowIndex = -1;
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function getNoMatchFoundHtml(
taConfig_,
additionalLastRowHtml_,
originalPayload_,
fnNoMessagePostfix_) {
var strNoDataMessageOverride = taConfig_.noDataMessage,
strNoMessagePostfix =
fnNoMessagePostfix_ ?
fnNoMessagePostfix_(
taConfig_.showAdditionalLastRow,
originalPayload_) :
'',
strNoDataMessageToUse =
strNoDataMessageOverride ?
strNoDataMessageOverride + strNoMessagePostfix :
'No matches found',
strNoMatchFoundAdditionalClasses =
taConfig_.typeAheadTableAdditionalClasses,
strClassAtr =
strNoMatchFoundAdditionalClasses ?
' class="' + strNoMatchFoundAdditionalClasses + '" ' :
'',
strNoMatchFoundHTML =
'
' +
'' +
'| ' + strNoDataMessageToUse + ' | ' +
'
';
if (taConfig_) {
if(additionalLastRowHtml_ && taConfig_.showAdditionalLastRow) {
strNoMatchFoundHTML += additionalLastRowHtml_;
}
}
strNoMatchFoundHTML += '
';
return strNoMatchFoundHTML;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function processTypeAheadSearchTextArray(
arrEscapedValues_,
forceFireTypeAheadOnChange_,
areMoreItems_,
idColumnIndex_,
nameColumnIndex_,
isInactiveColumnIndex_,
extraHTMLColumnIndex_,
inactiveMeansCompleteColumnIndex_,
forceIgnoreCurrentTextForBold_) {
var objTypeAheadDiv = getObj('typeAheadDiv'),
objTAConfig = objTypeAheadDiv.typeAheadConfigParams;
if (!objTAConfig) {
// The type-ahead has already been dismissed.
return;
}
// Make sure that clicking on the typeahead scrollbar doesn't cause the typeahead
// to be hidden and also make sure that once the typeahead loses focus, blur handling
// is turned back on again.
objTypeAheadDiv.onmousedown = mousedownOnTypeahead;
objTypeAheadDiv.onblur = blurTypeahead;
objTypeAheadDiv.ignoreBlur = false;
objTypeAheadDiv.selectedRowIndex = -1;
var theForm = getObj(objTAConfig.formId);
if (!theForm) {
// Not sure what may have gone wrong here. Don't bother continuing.
ConsoleLogging.logBlockClose('no Form! looking for formId=' + objTAConfig.formId);
return;
}
var objCurrText = theForm[objTAConfig.textFieldName],
objCurrId = theForm[objTAConfig.idFieldName],
originalIdValue = objCurrId ? objCurrId.value : null,
isText = objCurrText.value.length > 0,
tmpIsExactMatch = false,
tmpMatchedOriginalIdValue = false,
doAutoMatch = objTAConfig.autoMatchOnKeyInput;
if (typeof extraHTMLColumnIndex_ === 'undefined' ||
null === extraHTMLColumnIndex_ ||
extraHTMLColumnIndex_ < 0) {
extraHTMLColumnIndex_ = -1;
}
if (!(arrEscapedValues_ && arrEscapedValues_.length)) {
if(isText) {
var strNoMatchFoundHtml = getNoMatchFoundHtml(objTAConfig);
objTypeAheadDiv.style.display = 'none';
objTypeAheadDiv.style.zIndex = 501;
objTypeAheadDiv.innerHTML = strNoMatchFoundHtml;
setTimeout(doPositionPicker, 0);
} else {
mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals', null);
hideTypeAheadDiv();
if (forceFireTypeAheadOnChange_) {
fireTypeAheadOnChangeIfNecessary({
textField: objCurrText,
useAsynchForOnChange: false
});
}
}
} else {
setInvalidTextClass(
objCurrText,
0); // isInvalid_
var strColspanAttr = (extraHTMLColumnIndex_ > -1) ? ' colspan="2"' : '',
strAdditionalClasses =
(objTAConfig) ?
(objTAConfig.typeAheadTableAdditionalClasses || '') :
'',
strHTML =
'' +
'',
currTextRaw = objCurrText.value,
currText = (forceIgnoreCurrentTextForBold_ || objTAConfig.ignoreCurrentTextForBold) ? '' : currTextRaw,
rowId = 0,
i,
arrTypeaheadRowVals = [];
objTypeAheadDiv.itemCount = arrEscapedValues_.length;
mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals', arrTypeaheadRowVals);
for (i = 0; i < arrEscapedValues_.length; ++i) {
var strRowValue = arrEscapedValues_[i],
strOnMouseDown =
'onmousedown="selectTypeAheadItem(' +
rowId + ',' +
'true,' + // activeSelection_
'true,' + // setFocus_
'event)" ',
strOnMouseUp =
'onmouseup="hideTypeAheadDiv();' +
'setFocusToTextField(' +
'\'' + theForm.id + '\',' +
'\'' + objCurrText.name + '\');"';
if (strRowValue === '-') {
// It's a separator!
--objTypeAheadDiv.itemCount;
strHTML +=
'' +
' | ' +
'
';
} else {
var arrRowValues = strRowValue.split(':');
arrTypeaheadRowVals[i] = arrRowValues;
var escapedId = arrRowValues[idColumnIndex_],
escapedName = arrRowValues[nameColumnIndex_],
name = unescape(escapedName);
if ((!tmpMatchedOriginalIdValue) &&
name &&
name.toLowerCase() === currTextRaw.toLowerCase()) {
// If there is more than one product with the same name, we want to select the first item
// with that name, unless the previously selected item is also an identical match, but is NOT
// the FIRST such match.
var strUnescapedId = unescape(escapedId),
tmpThisRowMatchesTheOriginalId = objCurrId && (strUnescapedId === originalIdValue);
if (doAutoMatch && (tmpThisRowMatchesTheOriginalId || !tmpIsExactMatch)) {
// Either this row is an exact match and its id matches the previously selected id,
// or it is simply the first exact match.
//
// In the former case, we definitely want it to be the selected row (and definitely
// won't want any other row to be selected).
//
// In the latter case, we want this to be the selected row unless there is a later
// row that is also an exact match, and that is the same row as was previously selected.
if (objCurrId) {
objCurrId.value = strUnescapedId;
}
tmpMatchedOriginalIdValue = tmpMatchedOriginalIdValue || tmpThisRowMatchesTheOriginalId;
tmpIsExactMatch = true;
objTypeAheadDiv.selectedRowIndex = rowId;
}
}
var tmpHasInactiveColumn =
typeof isInactiveColumnIndex_ !== 'undefined' &&
isInactiveColumnIndex_ !== null &&
isInactiveColumnIndex_ > -1,
isInactive = tmpHasInactiveColumn ? '1' === arrRowValues[isInactiveColumnIndex_] : false,
tmpHasInactiveMeansCompleteColumn =
typeof inactiveMeansCompleteColumnIndex_ !== 'undefined' &&
inactiveMeansCompleteColumnIndex_ !== null &&
inactiveMeansCompleteColumnIndex_ > -1,
inactiveMeansComplete =
tmpHasInactiveMeansCompleteColumn ? '1' === arrRowValues[inactiveMeansCompleteColumnIndex_] : false,
strBolded = name.substring(0, currText.length),
strPostBolded = name.substring(currText.length),
strRow =
'' +
'| ';
if (isInactive) {
strRow +=
'';
}
strRow +=
'' +
htmlEncode(strBolded) +
'' +
htmlEncode(strPostBolded);
if (isInactive) {
strRow += '';
}
strRow +=
' | ';
if (extraHTMLColumnIndex_ > -1) {
strRow +=
'';
if (extraHTMLColumnIndex_ < arrRowValues.length) {
strRow +=
unescape(arrRowValues[extraHTMLColumnIndex_]);
}
strRow +=
' | ';
}
strRow +=
'
';
strHTML += strRow;
++rowId;
}
}
if (areMoreItems_) {
var strMoreMsg =
' There are more items' +
(currText ? ' starting with "' + htmlEncode(currText) + '"' : '');
strHTML +=
'' +
'| ' +
strMoreMsg +
' | ' +
'
';
}
strHTML +=
'' +
'
';
objTypeAheadDiv.style.display = 'none';
objTypeAheadDiv.style.zIndex = 501;
objTypeAheadDiv.innerHTML = strHTML;
if (tmpIsExactMatch && doAutoMatch) {
var objSelectedRow = getObj('typeAheadRow' + objTypeAheadDiv.selectedRowIndex);
objSelectedRow.className = 'typeAheadSelectedRow';
objSelectedRow.setAttribute('mjtclass', 'typeAheadSelectedRow');
// We've found a match (and thus set the selected row and id value (if there is one) so
// fire the onchange event (if there is one)
fireTypeAheadOnChangeIfNecessary({
textField: objCurrText,
useAsynchForOnChange: false
});
}
setTimeout(doPositionPicker, 0);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Processes Remote Scripting responses.
function rsCallbackGetTypeAheadSearchText(responseString_, contextId_, processResultParams_) {
var objTypeAheadDiv = getObj('typeAheadDiv'),
objTAConfig = objTypeAheadDiv.typeAheadConfigParams;
if (!objTAConfig) {
// The type-ahead has already been dismissed.
return;
}
//At this point we know that a type-ahead is currently open, but we can't be sure that
//1) This callback is for this type-ahead (i.e. it could be a really slow response from a prior type-ahead)
//2) This callback is the latest request from this type-ahead
//To handle these cases
//First check that the remote scripting context id stored on the type-ahead element
//matches up with the context id of this callback.
//If it doesn't match, then this is at the very least a stale request.
//ContextIds look like mjtrs#, ex: mjtrs1
if (objTAConfig.expectedContextId &&
contextId_ &&
objTAConfig.expectedContextId !== contextId_) {
ConsoleLogging.logMessage('TypeAheadPopup: expected to receive callback from ContextId=' + objTAConfig.expectedContextId +
', but instead received callback from ContextId=' + contextId_);
return;
}
if (checkResponseForError(responseString_)) {
var arrEscapedValues,
tmpAreMoreItems;
if (!trim(responseString_)) {
// No items...process an empty array
arrEscapedValues = [];
} else {
// The response should be prefixed by a "1;" or a "0;",
// indicating whether or not there are more after what is listed...
tmpAreMoreItems = responseString_.substring(0, 1) === '1';
responseString_ = responseString_.substring(2);
arrEscapedValues = responseString_.split(',');
}
var tmpHasExtraColumnIndex =
typeof processResultParams_.extraHTMLColumnIndex !== 'undefined' &&
processResultParams_.extraHTMLColumnIndex !== null,
extraHTMLColumnIndex = tmpHasExtraColumnIndex ? processResultParams_.extraHTMLColumnIndex : -1,
tmpHasIsInactiveColumnIndex =
typeof processResultParams_.isInactiveColumnIndex !== 'undefined' &&
processResultParams_.isInactiveColumnIndex !== null,
isInactiveColumnIndex = tmpHasIsInactiveColumnIndex ? processResultParams_.isInactiveColumnIndex : -1,
tmpHasInactiveMeansCompleteColumnIndex =
typeof processResultParams_.inactiveMeansCompleteColumnIndex !== 'undefined' &&
processResultParams_.inactiveMeansCompleteColumnIndex !== null,
inactiveMeansCompleteColumnIndex =
tmpHasInactiveMeansCompleteColumnIndex ? processResultParams_.inactiveMeansCompleteColumnIndex : -1;
processTypeAheadSearchTextArray(
arrEscapedValues,
false, // Force fire type-ahead on change
tmpAreMoreItems, // Are more items
0, // Id column index
1, // Name column index
isInactiveColumnIndex, // Inactive column index
extraHTMLColumnIndex, // Extra HTML Column Index
inactiveMeansCompleteColumnIndex, // Index of value indicating whether Inactive items are simply "Complete".
(processResultParams_.forceIgnoreCurrentTextForBold ? 1 : 0)); // forceIgnoreCurrentTextForBold_
}
}
//------------------------------------------------------------------------------
//
// Given a child element of a row within the typeahead, walks up the dom to
// find the row element and pull the "data-mjtTAIdx" attribute, which indicates
// the index into the original data used to build the typeahead in the
// "processTypeAheadSearchOfJSONArray()" function.
//
//------------------------------------------------------------------------------
function taGetItemIndexFromTypeAheadRowChildElem(taRowChildElement_) {
var rc = null,
objTrElem = checkAttribute(taRowChildElement_, "data-mjtTAIdx");
if (objTrElem) {
rc =
parseInt(
objTrElem.getAttribute('data-mjtTAIdx'), 10);
}
return rc;
}
//------------------------------------------------------------------------------
//
// params_:
// {
// extraHTMLPropertyName,
// renderExtraOnTheLeftPropertyName,
// arrObjects,
// forceFireTypeAheadOnChange,
// fnPostProcessTypeaheadDisplay
// }
//
//------------------------------------------------------------------------------
function processTypeAheadSearchOfJSONArray(params_) {
var objTypeAheadDiv = getObj('typeAheadDiv'),
objTAConfig = objTypeAheadDiv.typeAheadConfigParams;
if (!objTAConfig) {
// The type-ahead has already been dismissed.
return;
}
// Make sure that clicking on the typeahead scrollbar doesn't cause the typeahead
// to be hidden and also make sure that once the typeahead loses focus, blur handling
// is turned back on again.
objTypeAheadDiv.onmousedown = mousedownOnTypeahead;
objTypeAheadDiv.onblur = blurTypeahead;
objTypeAheadDiv.ignoreBlur = false;
objTypeAheadDiv.selectedRowIndex = -1;
var theForm = getObj(objTAConfig.formId);
if (!theForm) {
// Not sure what may have gone wrong here. Don't bother continuing.
ConsoleLogging.logBlockClose('No Form! looking for formId=' + objTAConfig.formId);
return;
}
var objCurrText = theForm[objTAConfig.textFieldName],
objCurrId = theForm[objTAConfig.idFieldName],
originalIdValue = objCurrId ? objCurrId.value : null,
isText = objCurrText.value.length > 0,
tmpIsExactMatch = false,
tmpMatchedOriginalIdValue = false,
doAutoMatch = objTAConfig.autoMatchOnKeyInput,
extraHTMLPropertyName = params_.extraHTMLPropertyName,
strRenderExtraOnTheLeftPropertyName =
params_.renderExtraOnTheLeftPropertyName,
arrObjects = params_.arrObjects,
forceFireTypeAheadOnChange_ = params_.forceFireTypeAheadOnChange;
if (!(arrObjects && arrObjects.length)) {
if(isText) {
var strNoMatchFoundHtml = getNoMatchFoundHtml(
objTAConfig,
params_.additionalLastRowHtml,
params_.originalPayload,
params_.fnNoMessagePostfix);
objTypeAheadDiv.style.display = 'none';
objTypeAheadDiv.style.zIndex = 501;
objTypeAheadDiv.innerHTML = strNoMatchFoundHtml;
setTimeout(doPositionPicker, 0);
} else {
mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals', null);
hideTypeAheadDiv();
if (forceFireTypeAheadOnChange_) {
fireTypeAheadOnChangeIfNecessary({
textField: objCurrText,
useAsynchForOnChange: false
});
}
}
} else {
setInvalidTextClass(
objCurrText,
0); // isInvalid_
var strColspanAttr = extraHTMLPropertyName ? ' colspan="2"' : '',
strAdditionalClasses =
(objTAConfig) ?
(objTAConfig.typeAheadTableAdditionalClasses || '') :
'',
strHTML =
'' +
'',
currTextRaw = objCurrText.value,
currText = (params_.forceIgnoreCurrentTextForBold || objTAConfig.ignoreCurrentTextForBold) ? '' : currTextRaw,
rowId = 0,
i;
objTypeAheadDiv.itemCount = arrObjects.length;
var arrTypeaheadRowVals = [];
mjtElemData(objTypeAheadDiv, 'arrTypeaheadRowVals', arrTypeaheadRowVals);
if (params_.additionalFirstRowHtml) {
strHTML += params_.additionalFirstRowHtml;
}
for (i = 0; i < arrObjects.length; ++i) {
var objRow = arrObjects[i],
// Include an attribute on each row of the typeahead that gives
// the index into the original set of data used to build the
// typeahead.
strObjIndexDataAttr = ' data-mjtTAIdx="' + i + '"',
strOnMouseDown =
'onmousedown="selectTypeAheadItem(' +
rowId + ',' +
'true,' + // activeSelection_
'true,' + // setFocus_
'event)" ',
strOnMouseUp =
'onmouseup="hideTypeAheadDiv();' +
'setFocusToTextField(' +
'\'' + theForm.id + '\',' +
'\'' + objCurrText.name + '\');"';
if (objRow.isSeparator) {
// It's a separator!
--objTypeAheadDiv.itemCount;
strHTML +=
'' +
' | ' +
'
';
} else {
arrTypeaheadRowVals[rowId] = objRow;
var strUnescapedId = objRow.id,
name = objRow.name;
if (objRow && !(strUnescapedId || name) && strUnescapedId !== '' && name !== '') {
// Looks like the given rows are not objects, so just use
// them directly as both the id and name...
strUnescapedId = objRow;
name = objRow;
}
if ((!tmpMatchedOriginalIdValue) &&
name &&
name.toLowerCase() === currTextRaw.toLowerCase()) {
// If there is more than one product with the same name, we want to select the first item
// with that name, unless the previously selected item is also an identical match, but is NOT
// the FIRST such match.
var tmpThisRowMatchesTheOriginalId = objCurrId && (strUnescapedId === originalIdValue);
if (doAutoMatch && (tmpThisRowMatchesTheOriginalId || !tmpIsExactMatch)) {
// Either this row is an exact match and its id matches the previously selected id,
// or it is simply the first exact match.
//
// In the former case, we definitely want it to be the selected row (and definitely
// won't want any other row to be selected).
//
// In the latter case, we want this to be the selected row unless there is a later
// row that is also an exact match, and that is the same row as was previously selected.
if (objCurrId) {
objCurrId.value = strUnescapedId;
}
tmpMatchedOriginalIdValue = tmpMatchedOriginalIdValue || tmpThisRowMatchesTheOriginalId;
tmpIsExactMatch = true;
objTypeAheadDiv.selectedRowIndex = rowId;
}
}
var isInactive = objRow.isInactive,
inactiveMeansComplete = objRow.inactiveMeansComplete,
strBolded = name.substring(0, currText.length),
strPostBolded = name.substring(currText.length),
strExistingSelectionText = objTAConfig.existingSelectionText,
tmpMarkAsPriorSelection =
strExistingSelectionText &&
name &&
(name.toLowerCase() === strExistingSelectionText.toLowerCase()),
strAdditionalRowClass =
tmpMarkAsPriorSelection ? ' typeAheadPriorSelection' : '',
strRow =
'',
tmpRenderExtraOnTheLeft =
strRenderExtraOnTheLeftPropertyName ?
objRow[strRenderExtraOnTheLeftPropertyName] :
false,
strExtraHTML;
strExtraHTML =
extraHTMLPropertyName ?
objRow[extraHTMLPropertyName] || '' :
'';
if (extraHTMLPropertyName) {
strExtraHTML =
'| ' +
(strExtraHTML ? strExtraHTML : '') +
' | ';
}
if (strExtraHTML && tmpRenderExtraOnTheLeft) {
strRow += strExtraHTML;
}
strRow +=
'';
if (isInactive) {
strRow +=
'';
}
strRow +=
'' +
htmlEncode(strBolded) +
'' +
htmlEncode(strPostBolded);
if (isInactive) {
strRow +=
' (' + (inactiveMeansComplete ? 'Complete' : 'Inactive') + ')' +
'';
}
strRow +=
' | ';
if (strExtraHTML && !tmpRenderExtraOnTheLeft) {
strRow += strExtraHTML;
}
strRow +=
'
';
strHTML += strRow;
++rowId;
}
}
if (params_.areMoreItems) {
var strMoreMsg =
' There are more items' +
(currText ? ' starting with "' + htmlEncode(currText) + '"' : '');
strHTML +=
'' +
'| ' +
strMoreMsg +
' | ' +
'
';
}
if (objTAConfig) {
if(params_.additionalLastRowHtml && objTAConfig.showAdditionalLastRow) {
strHTML += params_.additionalLastRowHtml;
}
}
strHTML +=
'' +
'
';
objTypeAheadDiv.style.display = 'none';
objTypeAheadDiv.style.zIndex = 501;
objTypeAheadDiv.innerHTML = strHTML;
if (tmpIsExactMatch && doAutoMatch) {
var objSelectedRow = getObj('typeAheadRow' + objTypeAheadDiv.selectedRowIndex);
objSelectedRow.className = 'typeAheadSelectedRow';
objSelectedRow.setAttribute('mjtclass', 'typeAheadSelectedRow');
// We've found a match (and thus set the selected row and id value (if there is one) so
// fire the onchange event (if there is one)
fireTypeAheadOnChangeIfNecessary({
textField: objCurrText,
useAsynchForOnChange: false
});
}
if (params_.fnPostProcessTypeaheadDisplay) {
params_.fnPostProcessTypeaheadDisplay({
typeaheadDiv: objTypeAheadDiv,
arrObjects: arrObjects
});
}
setTimeout(doPositionPicker, 0);
}
}
//------------------------------------------------------------------------------
//
// Processes callback data that has a JSON form.
//
//------------------------------------------------------------------------------
function rsCallbackGetTypeAheadSearchTextJSON(json_, optionsHolder_) {
var objOriginalOptions = optionsHolder_.originalOptions,
objOriginalPayload = optionsHolder_.originalPayload,
tmpContextId = optionsHolder_.contextId,
objTypeAheadDiv = getObj('typeAheadDiv'),
objTAConfig = (objTypeAheadDiv || {}).typeAheadConfigParams,
strArrayPropertyName =
objOriginalOptions.arrayPropertyName ?
objOriginalOptions.arrayPropertyName :
'array',
arrObjects = json_[strArrayPropertyName],
fnPreprocessArray = objOriginalOptions.fnPreprocessArray,
fnPreprocessObject = objOriginalOptions.fnPreprocessObject,
strSearchTerm = (objOriginalPayload || {}).term || '',
tmpAreMoreItems = json_.isMore;
if(!objTAConfig) {
// The type-ahead has already been dismissed.
return;
}
//At this point we know that a type-ahead is currently open, but we can't be sure that
//1) This callback is for this type-ahead (i.e. it could be a really slow response from a prior type-ahead)
//2) This callback is the latest request from this type-ahead
//To handle these cases
//First check that the remote scripting context id stored on the type-ahead element
//matches up with the context id of this callback.
//If it doesn't match, then this is at the very least a stale request.
//ContextIds look like mjtrs#, ex: mjtrs1
if(objTAConfig.expectedContextId &&
tmpContextId &&
objTAConfig.expectedContextId !== tmpContextId) {
ConsoleLogging.logMessage('TypeAheadPopup: expected to receive json-callback from ContextId=' + objTAConfig.expectedContextId +
', but instead received callback from ContextId=' + tmpContextId);
return;
}
if(fnPreprocessArray) {
fnPreprocessArray({
arrItems: arrObjects,
searchTerm: strSearchTerm
});
}
if (fnPreprocessObject && arrObjects) {
arrObjects.forEach(
function (object_) {
fnPreprocessObject(object_);
});
}
processTypeAheadSearchOfJSONArray(
{
extraHTMLPropertyName: objOriginalOptions.extraHTMLPropertyName,
renderExtraOnTheLeftPropertyName: objOriginalOptions.renderExtraOnTheLeftPropertyName,
forceIgnoreCurrentTextForBold: objOriginalOptions.forceIgnoreCurrentTextForBold,
arrObjects: arrObjects,
areMoreItems: tmpAreMoreItems,
fnPostProcessTypeaheadDisplay: objOriginalOptions.fnPostProcessTypeaheadDisplay,
additionalLastRowHtml: objOriginalOptions.additionalLastRowHtml,
additionalFirstRowHtml: objOriginalOptions.additionalFirstRowHtml,
originalPayload: optionsHolder_.originalPayload,
fnNoMessagePostfix: objOriginalOptions.fnNoMessagePostfix
});
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function updateTypeAheadBoxTimerFired() {
var objTypeAheadDiv = getObj('typeAheadDiv'),
configParams = objTypeAheadDiv.typeAheadConfigParams;
if (configParams) {
var theForm = getObj(configParams.formId);
// Do a lookup using the current text...
if (!theForm[configParams.textFieldName]) {
// Debugging info!
console.log(
'\n' +
'\n' +
'!!!!\n' +
'Can not find text field for type-ahead.\n' +
' configParams.textFieldName="' + configParams.textFieldName + '"\n' +
'!!!!');
}
var currText = theForm[configParams.textFieldName].value.toLowerCase(),
fnGetTypeAheadData;
if(currText.length === 0 && !configParams.showTypeAheadWhenUnmatched) {
processTypeAheadSearchTextArray(
[], // Items (none)
true, // Force handling of the type-ahead change. (yes, but doesn't really matter)
false, // Are more items (no)
0, // Id column (doesn't really matter)
1); // Name column (doesn't really matter)
} else {
fnGetTypeAheadData =
StringificationUtils.getOrResolveFunction({
functionNameOrReference: configParams.fnGetTypeAheadData,
descriptionOfFunctionBeingResolved:
'configParams.fnGetTypeAheadData'
});
if(fnGetTypeAheadData) {
fnGetTypeAheadData(
theForm,
currText,
configParams,
configParams.getTypeAheadDataFnParameter);
}
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function getSelectedTypeAheadRow(objTypeAheadDiv_) {
var rc = -1;
if (objTypeAheadDiv_) {
if (typeof objTypeAheadDiv_.selectedRowIndex === 'undefined') {
return objTypeAheadDiv_.selectedRowIndex;
}
}
return rc;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function handleTypeAheadTextBoxBlur(formId_, typeAheadConfigParams) {
var objTypeAheadDiv = getObj('typeAheadDiv');
if (objTypeAheadDiv && !objTypeAheadDiv.ignoreBlur) {
hideTypeAheadDiv();
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function fireAsynchHandleTypeAheadTextChange() {
if (g_typeAheadTextChangeTimer) {
clearTimeout(g_typeAheadTextChangeTimer);
}
var timeout = 300; // By default, wait 1/3 of a second before running off for data...
if (getObj('typeAheadDiv').typeAheadConfigParams.forceImmediatePopup) {
timeout = 0;
}
g_typeAheadTextChangeTimer = setTimeout(updateTypeAheadBoxTimerFired, timeout);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function handleTypeAheadTextBoxFocus(e_, formId_, typeAheadConfigParams_) {
var objTypeAheadDiv = getObj('typeAheadDiv');
objTypeAheadDiv.ignoreBlur = false;
if (!objTypeAheadDiv.ignoreFocus) {
objTypeAheadDiv.typeAheadConfigParams = typeAheadConfigParams_;
var theForm = getObj(typeAheadConfigParams_.formId),
objIdField = theForm[typeAheadConfigParams_.idFieldName];
if (typeAheadConfigParams_.forceTypeAheadOnFocus ||
(typeAheadConfigParams_.showTypeAheadWhenUnmatched && !objIdField.value.length)) {
fireAsynchHandleTypeAheadTextChange();
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function incrementTypeAheadSelectionIndex(moveDown_) {
var objTypeAheadDiv = getObj('typeAheadDiv'),
itemCount = objTypeAheadDiv.itemCount ? objTypeAheadDiv.itemCount : 0;
removeTypeAheadSelectionOrHoverHighlight(false);
if (typeof objTypeAheadDiv.selectedRowIndex === 'undefined') {
// Nothing selected yet, just select the first one.
selectTypeAheadItem(0, false);
} else {
var currSelIndex = parseInt(objTypeAheadDiv.selectedRowIndex, 10);
if (moveDown_) {
currSelIndex++;
} else {
--currSelIndex;
}
if (currSelIndex >= itemCount) {
currSelIndex = itemCount - 1;
}
if (currSelIndex < 0) {
currSelIndex = 0;
}
selectTypeAheadItem(currSelIndex, false);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function selectFirstLastInTypeahead(objTypeAheadDiv_, objTextField_, first_) {
if (isTypeaheadDivShowing(objTypeAheadDiv_)) {
var itemCount = objTypeAheadDiv_.itemCount ? objTypeAheadDiv_.itemCount : 0;
if (itemCount) {
selectTypeAheadItem(
first_ ? 0 : itemCount - 1, // rowIndex_
false, // activeSelection
false); // setFocus
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function pageTypeAheadIfNecessary(objTypeAheadDiv_, objTextField_, up_) {
if (isTypeaheadDivShowing(objTypeAheadDiv_)) {
var itemCount = objTypeAheadDiv_.itemCount ? objTypeAheadDiv_.itemCount : 0,
tmpSelectedIndex = objTypeAheadDiv_.selectedRowIndex ? objTypeAheadDiv_.selectedRowIndex : 0,
firstVisibleItem = 0,
lastVisibleItem = itemCount - 1,
topOfViewport = objTypeAheadDiv_.scrollTop,
bottomOfViewport = topOfViewport + objTypeAheadDiv_.clientHeight - 1,
i;
for (i = 0; i < itemCount; ++i) {
var objRow = getObj('typeAheadRow' + i);
if (topOfViewport + objRow.offsetHeight / 2 >= objRow.offsetTop &&
topOfViewport + objRow.offsetHeight / 2 < objRow.offsetTop + objRow.offsetHeight) {
// Found the first visible item...
firstVisibleItem = i;
}
if (bottomOfViewport - objRow.offsetHeight / 2 >= objRow.offsetTop &&
bottomOfViewport - objRow.offsetHeight / 2 < objRow.offsetTop + objRow.offsetHeight) {
// Found the last visible item...
lastVisibleItem = i;
}
}
var indexToSelect;
if (up_ && tmpSelectedIndex > firstVisibleItem) {
// Paging up, but we're not at the top of the viewport,
// so simply select the top item in the viewport.
indexToSelect = firstVisibleItem;
} else if ((!up_) && tmpSelectedIndex < lastVisibleItem) {
// Paging down, but we're not at the bottom of the viewport,
// so simply select the bottom item in the viewport.
indexToSelect = lastVisibleItem;
} else {
var itemsPerPage = lastVisibleItem - firstVisibleItem;
if (itemsPerPage < 1) {
itemsPerPage = 1;
}
if (up_) {
// Paging to the top of the previous page...
indexToSelect = firstVisibleItem - itemsPerPage;
} else {
// Paging to the bottom of the next page...
indexToSelect = lastVisibleItem + itemsPerPage;
}
}
if (indexToSelect >= itemCount) {
indexToSelect = itemCount - 1;
}
if (indexToSelect < 0) {
indexToSelect = 0;
}
selectTypeAheadItem(
indexToSelect,
false, // activeSelection
false); // setFocus
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function clearForAndHandleTypeAheadChange(params_) {
var objIdField = params_.idField,
objTextField = params_.textField,
tmpUseAsynchForOnChange = params_.useAsynchForOnChange;
// Clear out the id pending selection...
if (objIdField && objIdField.value.length) {
objIdField.value = '';
fireTypeAheadOnChangeIfNecessary({
textField: objTextField,
useAsynchForOnChange: tmpUseAsynchForOnChange
});
}
fireAsynchHandleTypeAheadTextChange();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function handleTypeAheadTextBoxOnInput(e_, formId_, typeAheadConfigParams_) {
var objTypeAheadDiv = getObj('typeAheadDiv');
if (!objTypeAheadDiv.ignoreTextChange) {
objTypeAheadDiv.typeAheadConfigParams = typeAheadConfigParams_;
var theForm = getObj(typeAheadConfigParams_.formId),
objTextField = theForm[typeAheadConfigParams_.textFieldName],
objIdField = theForm[typeAheadConfigParams_.idFieldName];
if (checkIfTypeaheadDisplayValueChangedAndSetLastValue(objTextField)) {
clearForAndHandleTypeAheadChange({
idField: objIdField,
textField: objTextField,
useAsynchForOnChange: false
});
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
function handleTypeAheadTextBoxKeyDown(e_, formId_, typeAheadConfigParams_, handlingEnter_, skipHideDialogOnEnter_) {
var objTypeAheadDiv = getObj('typeAheadDiv');
if (!objTypeAheadDiv.ignoreTextChange) {
objTypeAheadDiv.typeAheadConfigParams = typeAheadConfigParams_;
var theForm = getObj(typeAheadConfigParams_.formId),
objTextField = theForm[typeAheadConfigParams_.textFieldName],
objIdField = theForm[typeAheadConfigParams_.idFieldName];
switch (e_.keyCode) {
case 38:
// up
incrementTypeAheadSelectionIndex(false);
break;
case 40:
// down
if (e_.altKey) {
var strPickerActivation = objTextField.getAttribute('mjttypeaheadactivation');
if (strPickerActivation) {
hideTypeAheadDiv();
eval(strPickerActivation);
}
} else {
incrementTypeAheadSelectionIndex(true);
}
break;
case 33:
// pageup
pageTypeAheadIfNecessary(objTypeAheadDiv, objTextField, true);
return false;
case 34:
// pagedown
pageTypeAheadIfNecessary(objTypeAheadDiv, objTextField, false);
return false;
case 27:
// Escape
if (hideTypeAheadDiv()) {
// The dropdown was showing, so prevent the event from
// propagating so that the only impact will be dismissal of
// the typeahead. (e.g. if this is in a dialog, the dialog
// won't also be dismissed.)
cancelEvent(e_);
return false;
}
break;
case 13:
if (handleExplicitTypeAheadSelection()) {
return false;
} else {
if (!skipHideDialogOnEnter_) {
hideTypeAheadDiv();
}
if (!handlingEnter_) {
// Not handling enter separately, so return false to prevent the beep on IE.
return false;
}
}
break;
case 9: // Tab
break;
case 116: // F5
break;
case 37: // Left
break;
case 39: // Right
break;
case 16: // Shift
break;
case 36: // Home
if (e_.ctrlKey) {
selectFirstLastInTypeahead(objTypeAheadDiv, objTextField, true);
}
break;
case 35: // End
if (e_.ctrlKey) {
selectFirstLastInTypeahead(objTypeAheadDiv, objTextField, false);
}
break;
case 17: // Ctrl
break;
case 18: // Alt
break;
default:
if (e_.altKey) {
break;
}
if (e_.ctrlKey && (65 === e_.keyCode || 82 === e_.keyCode)) {
// Ctrl-A/Ctrl-R ... ignore these...
break;
}
// Whatever key the user hit may invalidate the current selection so clear it now
// (before we fetch new data) so that if the user hits 'enter' before the new data
// arrives they aren't taken to any previously selected item which may not match
// the last keys they entered.
clearSelection();
clearForAndHandleTypeAheadChange({
idField: objIdField,
textField: objTextField,
//Trigger the onchange event async
//Because it is coming from a text change
//that has not yet completed.
//If a synchronous event is fired, then the textbox
//would still have stale text.
useAsynchForOnChange: true
});
break;
}
}
}
//------------------------------------------------------------------------------
//
// Used as the "fnHandleExplicitSelection" parameter when you want to support
// submitting a dialog on enter from a typeahead field, but you don't need to
// take any sort of specific custom action when a typeahead option is explicitly
// selected from the list by clicking or pressing enter.
//
// This function will simply dismiss the typeahead and try to set focus back to
// the underlying text field.
//
// This is used to support the behaviour of hiding the typeahead dropdown if
// is pressed while the dropdown is showing and an item is selected,
// and of allowing the dialog to submit if the dropdown is NOT showing with an
// item selected. (In order to actually see this behaviour, you need to also
// set the "handlingEnter_" flag of the "handleTypeAheadTextBoxKeyDown()"
// function.
//
//------------------------------------------------------------------------------
function typeaheadDivHidingExplicitTypeaheadSelectionHandlerV2(
selectedRowIndex_,
objSelectedRow_,
formId_,
typeAheadConfigParams_,
explicitSelectionFnParameter_,
arrTypeaheadRowVals_) {
var theForm = getObj(formId_),
objTextField = theForm ? theForm[typeAheadConfigParams_.textFieldName] : 0;
hideTypeAheadDiv();
if (objTextField) {
// Asynchronously set focus back to the text field...
setTimeout(
function () {
setFocusIfFocusable(objTextField);
},
0);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
var TypeAheadPopup = function () {
var TYPEAHEAD_ITEM_TEXT_ALL = 'All',
TYPEAHEAD_ITEM_ID_ALL = -1;
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
function pvSearchTermPermitsAllItem(searchTerm_) {
var tmpSearchTermLen = searchTerm_.length,
tmpAllLen = TYPEAHEAD_ITEM_TEXT_ALL.length,
rc = 0;
if (tmpSearchTermLen <= tmpAllLen) {
// Don't even bother with a text comparison unless the length of the
// search term is no longer than the text we're vetting. (If the
// search term is longer than the value we're filtering, there's no
// way it could be a match)
if (!tmpSearchTermLen) {
// Since there is no search term to filter values, the "All"
// item is definitely not being filtered out.
rc = 1;
} else {
rc =
stringStartsWith(
TYPEAHEAD_ITEM_TEXT_ALL, // string_
searchTerm_, // prefix_
1); // ignoreCase_
}
}
return rc;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
function pbPrependAllItemToArray(params_) {
var arrItems = params_.arrItems,
strSearchTerm = params_.searchTerm;
if (arrItems && pvSearchTermPermitsAllItem(strSearchTerm)) {
var objAllItem = {
id: TYPEAHEAD_ITEM_ID_ALL,
name: TYPEAHEAD_ITEM_TEXT_ALL
};
arrItems.unshift(objAllItem);
}
return arrItems;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
function pbBuildStringifiedTypeaheadInfo(params_) {
var objStringifiableParams = params_.stringifiableParams,
rcStringified = JSON.stringify(objStringifiableParams);
return rcStringified;
}
//--------------------------------------------------------------------------
// TypeAheadPopup
//--------------------------------------------------------------------------
return {
prependAllItemToArray: pbPrependAllItemToArray,
buildStringifiedTypeaheadInfo: pbBuildStringifiedTypeaheadInfo,
PROTECTED: {
}
};
}();