You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

533 lines
11 KiB

/*
** Zabbix
** Copyright (C) 2001-2023 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
var LCL_SUGGESTS = [];
function createSuggest(oid) {
var sid = LCL_SUGGESTS.length;
LCL_SUGGESTS[sid] = new CSuggest(sid, oid);
return sid;
}
var CSuggest = function(id, objid) {
this.id = id;
this.cleanCache();
this.dom.input = document.getElementById(objid);
addListener(this.dom.input, 'keyup', this.keyPressed.bindAsEventListener(this));
addListener(this.dom.input, 'blur', this.suggestBlur.bindAsEventListener(this));
addListener(window, 'resize', this.positionSuggests.bindAsEventListener(this));
this.timeoutNeedle = null;
this.userNeedle = this.dom.input.value;
};
CSuggest.prototype = {
// public
'useLocal': true, // use cache to find suggests
'useServer': true, // use server to find suggests
'saveToCache': true, // save results to cache
'cacheTimeOut': 60, // cache timeout (seconds)
'suggestLimit': 15, // suggestion show limit
'searchDelay': 200, // milliseconds
// private
'id': null, // sugg obj identity
'rpcid': 0, // rpc request id
'needles': {}, // searched strings
'userNeedle': '', // userNeedle
'timeoutNeedle': null, // Timeout reference
'cache': {
'time': 0, // cache creation time
'list': {}, // cache by word
'needle': {} // cache by needle
},
'dom': {
'input': null, // DOM node input
'suggest': null, // DOM node suggest div
'sugtab': null // DOM node suggests table
},
'hlIndex': 0, // indicates what row should be highlighted
'suggestCount': 0, // suggests shown
'mouseOverSuggest': false, // indicates if mouse is over suggests
needleChange: function(e) {
this.hlIndex = 0;
this.suggestCount = 0;
clearTimeout(this.timeoutNeedle);
var target = e.target,
needle = target.value.toLowerCase();
if (empty(needle)) {
this.hideSuggests();
return true;
}
this.userNeedle = target.value;
this.needles[needle] = {'needle': needle, 'list': {}};
var found = false;
if (this.useLocal) {
found = this.searchClient(needle);
}
if (!found && this.useServer) {
this.timeoutNeedle = setTimeout(this.searchServer.bind(this, needle), this.searchDelay);
}
},
// search
searchServer: function(needle) {
if (needle !== this.userNeedle.toLowerCase()) {
return;
}
var rpcRequest = {
'method': 'search',
'params': {
'search': needle
},
'onSuccess': this.serverRespond.bind(this, needle),
'onFailure': function() {
throw('Suggest Widget: search request failed.');
}
};
new RPC.Call(rpcRequest);
},
serverRespond: function(needle, respond) {
var params = {
'list': {},
'needle': needle
};
for (var i = 0; i < respond.length; i++) {
if (!isset(i, respond) || empty(respond[i])) {
continue;
}
params.list[i] = respond[i].name;
}
this.needles[params.needle].list = params.list;
if (needle == this.userNeedle.toLowerCase()) {
this.showSuggests();
this.newSugTab(params.needle);
}
if (this.saveToCache) {
this.saveCache(params.needle, params.list);
}
},
searchClient: function(needle) {
var found = false;
if (this.inCache(needle)) {
this.needles[needle].list = this.cache.needle[needle];
found = true;
}
else if (!this.useServer) {
found = this.searchCache(needle);
}
if (found) {
this.showSuggests();
this.newSugTab(needle);
}
return found;
},
// cache
searchCache: function(needle) {
var fkey = needle[0];
if (!isset(fkey, this.cache.list)) {
return false;
}
var found = false,
list = {};
for (var key in this.cache.list[fkey]) {
var value = this.cache.list[fkey][key];
if (empty(value)) {
continue;
}
if (key.indexOf(needle) === 0) {
list[value] = value;
found = true;
}
}
this.needles[needle].list = list;
if (this.saveToCache) {
this.saveCache(needle, list);
}
return found;
},
inCache: function(needle) {
if (this.useServer) {
var dd = new Date();
if ((this.cache.time + (this.cacheTimeOut * 1000)) < dd.getTime()) {
this.cleanCache();
}
}
return isset(needle, this.cache.needle);
},
saveCache: function(needle, list) {
if (this.useServer) {
var dd = new Date();
if ((this.cache.time + (this.cacheTimeOut * 1000)) < dd.getTime()) {
this.cleanCache();
}
}
// needles
if (!is_null(needle)) {
this.cache.needle[needle] = list;
}
// list
for (var key in list) {
if (empty(list[key])) {
continue;
}
var word = list[key],
lWord = word.toLowerCase(),
fkey = lWord[0];
// Indexing by first letter.
if (!isset(fkey, this.cache.list)) {
this.cache.list[fkey] = {};
}
this.cache.list[fkey][lWord] = word;
}
},
cleanCache: function() {
var time = new Date();
this.cache = {
'time': time.getTime(),
'list': {},
'needle': {}
}
},
// events
onSelect: function(selection) {
return true;
},
// keyboard
searchFocus: function(e) {
if (!e) {
e = window.event;
}
var elem = e.element();
if (elem.match('input[type=text]') || elem.match('textarea') || elem.match('select')) {
return true;
}
var key = e.keyCode;
if (key == 47) {
e.stop();
$(this.dom.input).focus();
return void(0);
}
},
keyPressed: function(e) {
if (!e) {
e = window.event;
}
var key = e.keyCode;
switch (true) {
// escape
case (key == 27):
this.hlIndex = 0;
this.suggestCount = 0;
this.removeHighLight(e);
this.setNeedleByHighLight(e);
this.hideSuggests(e);
break;
// enter
case (key == 13):
if (this.dom.input.value.trim() !== '') {
this.needleChange(e);
this.selectSuggest(e);
}
break;
// left, right, tab
case (key == 37 || key == 39 || key == 9):
break;
// up
case (key == 38):
this.keyUp(e);
break;
// down
case (key == 40):
this.keyDown(e);
break;
default:
this.needleChange(e);
}
e.preventDefault();
e.stopPropagation();
},
keyUp: function(e) {
if (this.hlIndex == 0) {
this.hlIndex = this.suggestCount;
}
else {
this.hlIndex--;
}
this.removeHighLight(e);
this.highLightSuggest(e);
this.setNeedleByHighLight(e);
},
keyDown: function(e) {
if (is_null(this.dom.suggest) || (this.dom.suggest.style.display === 'none')) {
this.needleChange(e);
return true;
}
if (this.hlIndex == this.suggestCount) {
this.hlIndex = 0;
}
else {
this.hlIndex++;
}
this.removeHighLight(e);
this.highLightSuggest(e);
this.setNeedleByHighLight(e);
},
mouseOver: function(e) {
this.mouseOverSuggest = true;
var row = e.target;
if (is_null(row) || (row.tagName.toLowerCase() !== 'li') || !isset('id',row)) {
return true;
}
var tmp = row.id.split('_');
if (tmp.length != 2) {
return true;
}
this.hlIndex = parseInt(tmp[1], 10);
this.removeHighLight(e);
this.highLightSuggest(e);
},
mouseOut: function(e) {
this.mouseOverSuggest = false;
},
suggestBlur: function(e) {
if (this.mouseOverSuggest) {
e.preventDefault();
e.stopPropagation();
}
else {
this.hideSuggests(e);
}
},
// highLight
removeHighLight: function() {
jQuery('li.suggest-hover').each(function() {
this.className = '';
});
},
highLightSuggest: function() {
var row = document.getElementById('line_' + this.hlIndex);
if (!is_null(row)) {
row.className = 'suggest-hover';
}
},
setNeedleByHighLight: function() {
if (this.hlIndex == 0) {
this.dom.input.value = this.userNeedle;
}
else {
this.dom.input.value = document.getElementById('line_' + this.hlIndex).getAttribute('needle');
}
},
selectSuggest: function(e) {
this.setNeedleByHighLight(e);
this.hideSuggests();
if (this.onSelect(this.dom.input.value)) {
this.dom.input.form.submit();
}
},
// DOM creation
showSuggests: function() {
if (is_null(this.dom.suggest)) {
this.dom.suggest = document.createElement('ul');
var doc_body = document.getElementsByTagName('body')[0];
if (empty(doc_body)) {
return false;
}
doc_body.appendChild(this.dom.suggest);
this.dom.suggest.className = 'search-suggest';
this.positionSuggests();
// Insert just after input.
this.dom.input.parentNode.insertBefore(this.dom.suggest, this.dom.input.nextSibling);
}
this.dom.suggest.style.display = 'block';
},
hideSuggests: function() {
if (!is_null(this.dom.suggest)) {
this.dom.suggest.style.display = 'none';
}
},
positionSuggests: function() {
if (is_null(this.dom.suggest)) {
return true;
}
var dims = getDimensions(this.dom.input);
this.dom.suggest.style.top = dims.height + 'px';
},
newSugTab: function(needle) {
var list = this.needles[needle].list,
sugTab = document.createElement('div'),
count = 0;
needle = needle.toLowerCase();
for (var key in list) {
if (empty(list[key])) {
continue;
}
count++;
var li = document.createElement('li'),
text = list[key].toLowerCase(),
start = 0,
end = 0;
li.setAttribute('id', 'line_' + count);
li.setAttribute('needle', list[key]);
while (text.indexOf(needle, end) > -1) {
end = text.indexOf(needle, end);
if (end > start) {
li.appendChild(document.createTextNode(list[key].substring(start, end)));
}
var bold = document.createElement('span');
bold.appendChild(document.createTextNode(list[key].substring(end, end + needle.length)));
bold.setAttribute('class', 'suggest-found');
li.appendChild(bold);
end += needle.length;
start = end;
}
if (end < list[key].length) {
li.appendChild(document.createTextNode(list[key].substring(end, list[key].length)));
}
addListener(li, 'mouseover', this.mouseOver.bindAsEventListener(this), true);
addListener(li, 'mouseup', this.selectSuggest.bindAsEventListener(this), true);
addListener(li, 'mouseout', this.mouseOut.bindAsEventListener(this), true);
sugTab.appendChild(li);
if (count >= this.suggestLimit) {
break;
}
}
this.dom.suggest.appendChild(sugTab);
if (!is_null(this.dom.sugtab)) {
this.dom.sugtab.remove();
}
this.dom.sugtab = sugTab;
this.dom.suggest.appendChild(this.dom.sugtab);
if (count == 0) {
this.hideSuggests();
}
this.suggestCount = count;
}
};