483 lines
18 KiB
JavaScript
483 lines
18 KiB
JavaScript
/**
|
|
* This Software is the property of Data Development and is protected
|
|
* by copyright law - it is NOT Freeware.
|
|
* Any unauthorized use of this software without a valid license
|
|
* is a violation of the license agreement and will be prosecuted by
|
|
* civil and criminal law.
|
|
* http://www.shopmodule.com
|
|
*
|
|
* @license
|
|
* @copyright (C) D3 Data Development (Inh. Thomas Dartsch)
|
|
* @author D3 Data Development - Daniel Seifert <support@shopmodule.com>
|
|
* @link http://www.oxidmodule.com
|
|
*/
|
|
|
|
$( function() {
|
|
"use strict";
|
|
$.widget( "ui.d3extsearchsuggest", {
|
|
// default options
|
|
options: {
|
|
currentEvent: null,
|
|
oAjaxResponseElement: null,
|
|
isSend: 0,
|
|
coloredId: null,
|
|
oldColoredId: null,
|
|
iActLine: null,
|
|
iCode: null,
|
|
blNavigate: null,
|
|
iRet: null,
|
|
sSelection: null,
|
|
oSelection: null,
|
|
iDelay: 600, // delay before submit to server, if is already submitted
|
|
sD3SearchBoxDefault: "",
|
|
sWaitMessage: "",
|
|
sParentThemeId: "flow",
|
|
sRequestUrl: null,
|
|
sSearchFormId: "search",
|
|
sCloseBtnId: "d3extsearch_suggest_closebtn",
|
|
sResultListId: "#searchItemList",
|
|
sResultItemClass: ".d3QSItem",
|
|
sStartSearchButtonId: "d3extsearch_suggest_startsearch",
|
|
sResponseElementId: "xajax_resp",
|
|
sResponseElementClass: "xajax_resp_cl",
|
|
sWaitMsgIdentificator: "#d3_extsearch_quicksearch.searchWaitMsg",
|
|
sRequestFncName: "getSuggestContent",
|
|
sSearchParamName: "searchParam",
|
|
sActClassName: "item_act",
|
|
sInactClassName: "item_inact",
|
|
|
|
sActiveElementClassName: null, // apply css class
|
|
blSetActiveElementDimensions: true, // use calculated or applied inline formats
|
|
sActiveElementStyleTop: null,
|
|
sActiveElementStyleTopImportant: "", // should "" or "important"
|
|
sActiveElementStyleLeft: null,
|
|
sActiveElementStyleLeftImportant: "", // should "" or "important"
|
|
blAutomatedActiveElementStyleWidth: false,
|
|
sActiveElementStyleWidth: null,
|
|
sActiveElementStyleWidthImportant: "", // should "" or "important"
|
|
|
|
iScrollTopOffset: 29,
|
|
blEnableLeftRightNavigation: true,
|
|
blToggleLeftRightDirection: false
|
|
},
|
|
|
|
_create: function () {
|
|
let self = this;
|
|
let el = this.element;
|
|
|
|
this.addResponseElement();
|
|
el.keyup(function(event) {
|
|
self.keyHandler(event);
|
|
});
|
|
},
|
|
|
|
addResponseElement: function () {
|
|
let options = this.options;
|
|
let ResponseElement = $("#" + options.sResponseElementId);
|
|
if (ResponseElement.length === 0) {
|
|
options.oAjaxResponseElement = $("<div>", {
|
|
"id": options.sResponseElementId,
|
|
"class": options.sResponseElementClass + " xajax_resp_" + options.sParentThemeId
|
|
}).appendTo("body");
|
|
} else {
|
|
options.oAjaxResponseElement = ResponseElement;
|
|
}
|
|
},
|
|
|
|
mouseHandler: function (elementId, iLine) {
|
|
let options = this.options;
|
|
options.oldColoredId = options.coloredId;
|
|
options.coloredId = elementId;
|
|
options.iActLine = iLine;
|
|
options.blNavigate = true;
|
|
this.changeResultItemColor(options.coloredId, options.oldColoredId);
|
|
},
|
|
|
|
keyHandler: function (event) {
|
|
event.preventDefault();
|
|
let options = this.options;
|
|
options.currentEvent = event;
|
|
let isSubmitEvent = event.type.toLowerCase() === "submit";
|
|
let sKey = isSubmitEvent ? "Enter" : event.originalEvent.key;
|
|
|
|
if (isSubmitEvent || sKey.toLowerCase() === "enter" || options.oAjaxResponseElement.is(":visible")) {
|
|
if (sKey.toLowerCase() === "arrowup") {
|
|
this.handleArrowUpKey();
|
|
} else if (
|
|
(sKey.toLowerCase() === "arrowleft" || sKey.toLowerCase() === "pageup") && options.blEnableLeftRightNavigation
|
|
) {
|
|
this.handleArrowLeftKey();
|
|
} else if (
|
|
(sKey.toLowerCase() === "arrowright" || sKey.toLowerCase() === "pagedown") && options.blEnableLeftRightNavigation
|
|
) {
|
|
this.handleArrowRightKey();
|
|
} else if (sKey.toLowerCase() === "arrowdown") {
|
|
this.handleArrowDownKey();
|
|
} else if (sKey.toLowerCase() === "enter") {
|
|
if (options.blNavigate) { // if suggest search window is open
|
|
this.handleEnterKeyOnSelectedItem();
|
|
} else {
|
|
this.handleEnterKeyWithoutSelectedItem();
|
|
}
|
|
} else if (sKey.toLowerCase() === "escape") {
|
|
this.handleEscapeKey();
|
|
} else {
|
|
this.handleOtherKeys();
|
|
}
|
|
} else { // there's no suggest search window
|
|
this.showSuggestWindow();
|
|
}
|
|
},
|
|
|
|
handleArrowUpKey: function () {
|
|
let options = this.options;
|
|
if (options.iActLine > 0) {
|
|
options.iActLine--;
|
|
}
|
|
if (options.coloredId) {
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
options.blNavigate = true;
|
|
options.coloredId = this.getResultItemIdByLine(options.iActLine);
|
|
this.changeResultItemColor(options.coloredId, options.oldColoredId);
|
|
},
|
|
|
|
handleArrowDownKey: function () {
|
|
let options = this.options;
|
|
let iNodesCount = this.getResultItemCount();
|
|
|
|
if (options.iActLine < iNodesCount - 1) {
|
|
options.iActLine++;
|
|
}
|
|
|
|
if (options.coloredId) {
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
options.blNavigate = true;
|
|
options.coloredId = this.getResultItemIdByLine(options.iActLine);
|
|
this.changeResultItemColor(options.coloredId, options.oldColoredId);
|
|
},
|
|
|
|
handleArrowLeftKey: function () {
|
|
let options = this.options;
|
|
let iIndex = null;
|
|
let iDirection = null;
|
|
let aTypes = this.getTypesList();
|
|
|
|
if (options.iActLine < 0) {
|
|
iIndex = 0;
|
|
iDirection = 0;
|
|
} else {
|
|
let sCurrType = $("#" + this.getResultItemIdByLine(options.iActLine)).attr("data-object-type");
|
|
if (sCurrType !== null) {
|
|
if (sCurrType && sCurrType.length && aTypes.length > 1) {
|
|
iIndex = $.inArray(sCurrType, aTypes);
|
|
iDirection = options.blToggleLeftRightDirection ? 1 : -1;
|
|
}
|
|
} else {
|
|
console.error("selected item has no data-object-type attribute, can not switch to next group");
|
|
}
|
|
}
|
|
|
|
if (iIndex !== null) {
|
|
let sNewType = aTypes[iIndex + iDirection];
|
|
let sElementId = this.findFirstElementIdByObjectType(sNewType);
|
|
if (sElementId !== null) {
|
|
if (options.coloredId) {
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
options.coloredId = sElementId;
|
|
this.changeResultItemColor(options.coloredId, options.oldColoredId);
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
}
|
|
},
|
|
|
|
handleArrowRightKey: function () {
|
|
let options = this.options;
|
|
let iIndex = null;
|
|
let iDirection = null;
|
|
let aTypes = this.getTypesList();
|
|
|
|
if (options.iActLine < 0) {
|
|
iIndex = 0;
|
|
iDirection = 0;
|
|
} else {
|
|
let sCurrType = $("#" + this.getResultItemIdByLine(options.iActLine)).attr("data-object-type");
|
|
if (sCurrType) {
|
|
if (sCurrType && sCurrType.length && aTypes.length > 1) {
|
|
iIndex = $.inArray(sCurrType, aTypes);
|
|
iDirection = options.blToggleLeftRightDirection ? -1 : 1;
|
|
}
|
|
} else {
|
|
console.error("selected item has no data-object-type attribute, can not switch to next group");
|
|
}
|
|
}
|
|
|
|
if (iIndex !== null) {
|
|
let sNewType = aTypes[iIndex + iDirection];
|
|
let sElementId = this.findFirstElementIdByObjectType(sNewType);
|
|
if (sElementId) {
|
|
if (options.coloredId) {
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
options.coloredId = sElementId;
|
|
this.changeResultItemColor(options.coloredId, options.oldColoredId);
|
|
options.oldColoredId = options.coloredId;
|
|
}
|
|
}
|
|
},
|
|
|
|
getTypesList: function () {
|
|
let types = [];
|
|
this.getResultItemListElement().each(function () {
|
|
let objecttype = $(this).attr("data-object-type");
|
|
if (objecttype) {
|
|
if (objecttype.length && $.inArray(objecttype, types) < 0) {
|
|
types.push(objecttype);
|
|
}
|
|
} else {
|
|
console.error("no data-object-type attributes for grouping found");
|
|
}
|
|
});
|
|
return types;
|
|
},
|
|
|
|
findFirstElementIdByObjectType: function (sType) {
|
|
let options = this.options;
|
|
let sId = null;
|
|
this.getResultItemListElement().each(function (index) {
|
|
if ($(this).attr("data-object-type") === sType) {
|
|
options.iActLine = index;
|
|
sId = $(this).attr("id");
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return sId;
|
|
},
|
|
|
|
handleEnterKeyOnSelectedItem: function () {
|
|
let options = this.options;
|
|
let element = $("#" + this.getResultItemIdByLine(options.iActLine));
|
|
window.location.href = element.attr("href");
|
|
},
|
|
|
|
handleEnterKeyWithoutSelectedItem: function () {
|
|
let self = this;
|
|
let options = this.options;
|
|
|
|
if (options.isSend) {
|
|
window.clearTimeout(options.isSend);
|
|
self.hideSuggest();
|
|
}
|
|
let searchFormElement = $("#" + options.sSearchFormId);
|
|
searchFormElement.off("submit");
|
|
searchFormElement.submit();
|
|
},
|
|
|
|
handleEscapeKey: function () {
|
|
let self = this;
|
|
self.hideSuggest();
|
|
},
|
|
|
|
handleOtherKeys: function () {
|
|
let options = this.options;
|
|
let event = options.currentEvent;
|
|
let sKey = event.originalEvent.key;
|
|
|
|
if (sKey !== "ArrowLeft" && sKey !== "ArrowRight") {
|
|
this.showSuggestWindow();
|
|
}
|
|
},
|
|
|
|
showSuggestWindow: function () {
|
|
let options = this.options;
|
|
|
|
if (options.currentEvent.originalEvent.key.toLowerCase() === "enter") {
|
|
return;
|
|
}
|
|
|
|
if ($(options.sWaitMsgIdentificator).length === 0) {
|
|
this.showWaitMessage();
|
|
options.isSend = window.setTimeout(this.showResult(), 0);
|
|
} else {
|
|
this.showWaitMessage();
|
|
if (options.isSend) {
|
|
window.clearTimeout(options.isSend);
|
|
}
|
|
options.isSend = window.setTimeout(this.showResult(), options.iDelay);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
showWaitMessage: function () {
|
|
let options = this.options;
|
|
options.oAjaxResponseElement.html($("<div/>").html(options.sWaitMessage).text());
|
|
this.setResponseElementStyle();
|
|
},
|
|
|
|
showResult: function () {
|
|
let options = this.options;
|
|
let el = this.element;
|
|
let self = this;
|
|
// don't use oD3SearchJQ.ajax, because it sends one letter per request
|
|
$.get(options.sRequestUrl + "fnc=" + options.sRequestFncName + "&" + options.sSearchParamName + "=" + el.val(), function (data) {
|
|
// prevent the display of outdated informations
|
|
if (el.val() === data.searchparam) {
|
|
options.oAjaxResponseElement.html(data.content);
|
|
self.setItemsMouseHandler();
|
|
self.setResponseElementStyle();
|
|
self.setStartSearchButtonHandler();
|
|
}
|
|
});
|
|
|
|
options.iActLine = -1;
|
|
options.coloredId = -1;
|
|
options.oldColoredId = -1;
|
|
|
|
options.blNavigate = false;
|
|
},
|
|
|
|
setResponseElementStyle: function () {
|
|
let self = this;
|
|
let options = this.options;
|
|
let el = this.element;
|
|
|
|
function setResponseStyles() {
|
|
if (options.sActiveElementClassName) {
|
|
options.oAjaxResponseElement.addClass(options.sActiveElementClassName);
|
|
}
|
|
|
|
if (options.blSetActiveElementDimensions) {
|
|
options.oAjaxResponseElement.get(0).style.setProperty(
|
|
'top',
|
|
options.sActiveElementStyleTop ?
|
|
options.sActiveElementStyleTop :
|
|
el[0].hasAttribute("suggestTopOffsetPx") ?
|
|
(el.offset().top + (el.height() + 5) + parseInt(el.attr('suggestTopOffsetPx'), 10) + "px"):
|
|
(el.offset().top + (el.height() + 5) + "px"),
|
|
options.sActiveElementStyleTopImportant
|
|
);
|
|
options.oAjaxResponseElement.get(0).style.setProperty(
|
|
'left',
|
|
options.sActiveElementStyleLeft ?
|
|
options.sActiveElementStyleLeft :
|
|
el[0].hasAttribute("suggestLeftOffsetPx") ?
|
|
(el.offset().left + parseInt(el.attr('suggestLeftOffsetPx'), 10)) + "px":
|
|
(el.offset().left + "px"),
|
|
options.sActiveElementStyleLeftImportant
|
|
);
|
|
if (options.blAutomatedActiveElementStyleWidth) {
|
|
options.oAjaxResponseElement.get(0).style.setProperty(
|
|
'width',
|
|
options.sActiveElementStyleWidth ?
|
|
options.sActiveElementStyleWidth :
|
|
(el.width() + "px"),
|
|
options.sActiveElementStyleWidthImportant
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
setResponseStyles();
|
|
this.showSuggest();
|
|
options.oAjaxResponseElement.click(function(event){ // required to prevent close on inside click
|
|
event.stopPropagation();
|
|
});
|
|
this.element.click(function(event) {
|
|
event.stopPropagation();
|
|
});
|
|
$("body").click(function() {
|
|
self.hideSuggest();
|
|
});
|
|
$("#" + options.sCloseBtnId).click(function(event) {
|
|
event.stopPropagation();
|
|
self.hideSuggest();
|
|
});
|
|
},
|
|
|
|
showSuggest: function () {
|
|
this.options.oAjaxResponseElement.addClass('suggestVisible');
|
|
},
|
|
|
|
hideSuggest: function () {
|
|
this.options.oAjaxResponseElement.removeClass('suggestVisible');
|
|
},
|
|
|
|
getResultItemListElement: function () {
|
|
let options = this.options;
|
|
return $(options.sResultListId).find("a" + options.sResultItemClass);
|
|
},
|
|
|
|
getResultItemCount: function () {
|
|
return this.getResultItemListElement().length;
|
|
},
|
|
|
|
getResultItemIdByLine:function (iLine) {
|
|
let iRet = null;
|
|
this.getResultItemListElement().each(function (index) {
|
|
if (index === iLine) {
|
|
iRet = $(this).attr("id");
|
|
}
|
|
});
|
|
|
|
return iRet;
|
|
},
|
|
|
|
changeResultItemColor: function (newId, oldId) {
|
|
let options = this.options;
|
|
|
|
if (oldId !== -1 && oldId !== newId) {
|
|
// don't use toggleClass, because this method is to slow for this case
|
|
$("#" + oldId).addClass(options.sInactClassName).removeClass(options.sActClassName);
|
|
}
|
|
|
|
if (oldId !== newId) {
|
|
$("#" + newId).addClass(options.sActClassName).removeClass(options.sInactClassName);
|
|
}
|
|
|
|
this.scrollToSelectedElement(newId);
|
|
},
|
|
|
|
scrollToSelectedElement: function (elementId) {
|
|
let options = this.options;
|
|
let currentEvent = options.currentEvent;
|
|
let itemElement = $("#" + elementId);
|
|
|
|
// if list doesn't exist or event is mouse event
|
|
if (itemElement.length === 0 || currentEvent.type.toLowerCase().indexOf("mouse") >= 0) {
|
|
return;
|
|
}
|
|
|
|
let listElement = $(options.sResultListId);
|
|
let iPosition = itemElement.position().top + listElement.scrollTop() - options.iScrollTopOffset;
|
|
listElement.stop(true).animate({
|
|
scrollTop: iPosition
|
|
}, "slow");
|
|
},
|
|
|
|
setItemsMouseHandler: function () {
|
|
let self = this;
|
|
let options = this.options;
|
|
this.getResultItemListElement().each(function (index) {
|
|
$(this).mouseover(function(event) {
|
|
options.currentEvent = event;
|
|
let sElementId = self.getResultItemIdByLine(index);
|
|
self.mouseHandler(sElementId, index);
|
|
});
|
|
});
|
|
},
|
|
|
|
setStartSearchButtonHandler: function () {
|
|
let self = this;
|
|
let options = this.options;
|
|
|
|
$("#" + options.sStartSearchButtonId).click(function(event) {
|
|
event.preventDefault();
|
|
self.handleEnterKeyWithoutSelectedItem();
|
|
});
|
|
}
|
|
});
|
|
}(jQuery)); |