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
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;
|
|
}
|
|
};
|