webvowl / src /app /js /menu /searchMenu.js
taferh's picture
add application files
fe66731
/**
* Contains the search "engine"
*
* @param graph the associated webvowl graph
* @returns {{}}
*/
module.exports = function ( graph ){
var searchMenu = {},
dictionary = [],
entryNames = [],
searchLineEdit,
mergedStringsList,
mergedIdList,
maxEntries = 6,
dictionaryUpdateRequired = true,
labelDictionary,
inputText,
viewStatusOfSearchEntries = false;
var results = [];
var resultID = [];
var c_locate = d3.select("#locateSearchResult");
var c_search = d3.select("#c_search");
var m_search = d3.select("#m_search"); // << dropdown container;
String.prototype.beginsWith = function ( string ){
return (this.indexOf(string) === 0);
};
searchMenu.requestDictionaryUpdate = function (){
dictionaryUpdateRequired = true;
// clear possible pre searched entries
var htmlCollection = m_search.node().children;
var numEntries = htmlCollection.length;
for ( var i = 0; i < numEntries; i++ )
htmlCollection[0].remove();
searchLineEdit.node().value = "";
};
function updateSearchDictionary(){
labelDictionary = graph.getUpdateDictionary();
dictionaryUpdateRequired = false;
dictionary = [];
entryNames = [];
var idList = [];
var stringList = [];
var i;
for ( i = 0; i < labelDictionary.length; i++ ) {
var lEntry = labelDictionary[i].labelForCurrentLanguage();
idList.push(labelDictionary[i].id());
stringList.push(lEntry);
// add all equivalents to the search space;
if ( labelDictionary[i].equivalents && labelDictionary[i].equivalents().length > 0 ) {
var eqs = labelDictionary[i].equivalentsString();
var eqsLabels = eqs.split(", ");
for ( var e = 0; e < eqsLabels.length; e++ ) {
idList.push(labelDictionary[i].id());
stringList.push(eqsLabels[e]);
}
}
}
mergedStringsList = [];
mergedIdList = [];
var indexInStringList = -1;
var currentString;
var currentObjectId;
for ( i = 0; i < stringList.length; i++ ) {
if ( i === 0 ) {
// just add the elements
mergedStringsList.push(stringList[i]);
mergedIdList.push([]);
mergedIdList[0].push(idList[i]);
continue;
}
else {
currentString = stringList[i];
currentObjectId = idList[i];
indexInStringList = mergedStringsList.indexOf(currentString);
}
if ( indexInStringList === -1 ) {
mergedStringsList.push(stringList[i]);
mergedIdList.push([]);
var lastEntry = mergedIdList.length;
mergedIdList[lastEntry - 1].push(currentObjectId);
} else {
mergedIdList[indexInStringList].push(currentObjectId);
}
}
for ( i = 0; i < mergedStringsList.length; i++ ) {
var aString = mergedStringsList[i];
var correspondingIdList = mergedIdList[i];
var idListResult = "[ ";
for ( var j = 0; j < correspondingIdList.length; j++ ) {
idListResult = idListResult + correspondingIdList[j].toString();
idListResult = idListResult + ", ";
}
idListResult = idListResult.substring(0, idListResult.length - 2);
idListResult = idListResult + " ]";
dictionary.push(aString);
entryNames.push(aString);
}
}
searchMenu.setup = function (){
// clear dictionary;
dictionary = [];
searchLineEdit = d3.select("#search-input-text");
searchLineEdit.on("input", userInput);
searchLineEdit.on("keydown", userNavigation);
searchLineEdit.on("click", toggleSearchEntryView);
searchLineEdit.on("mouseover", hoverSearchEntryView);
c_locate.on("click", function (){
graph.locateSearchResult();
});
c_locate.on("mouseover", function (){
searchMenu.hideSearchEntries();
});
};
function hoverSearchEntryView(){
updateSelectionStatusFlags();
searchMenu.showSearchEntries();
}
function toggleSearchEntryView(){
if ( viewStatusOfSearchEntries ) {
searchMenu.hideSearchEntries();
} else {
searchMenu.showSearchEntries();
}
}
searchMenu.hideSearchEntries = function (){
m_search.style("display", "none");
viewStatusOfSearchEntries = false;
};
searchMenu.showSearchEntries = function (){
m_search.style("display", "block");
viewStatusOfSearchEntries = true;
};
function ValidURL( str ){
var urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
return urlregex.test(str);
}
function updateSelectionStatusFlags(){
if ( searchLineEdit.node().value.length === 0 ) {
createSearchEntries();
return;
}
handleAutoCompletion();
}
function userNavigation(){
if ( dictionaryUpdateRequired ) {
updateSearchDictionary();
}
var htmlCollection = m_search.node().children;
var numEntries = htmlCollection.length;
var move = 0;
var i;
var selectedEntry = -1;
for ( i = 0; i < numEntries; i++ ) {
var atr = htmlCollection[i].getAttribute('class');
if ( atr === "dbEntrySelected" ) {
selectedEntry = i;
}
}
if ( d3.event.keyCode === 13 ) {
if ( selectedEntry >= 0 && selectedEntry < numEntries ) {
// simulate onClick event
htmlCollection[selectedEntry].onclick();
searchMenu.hideSearchEntries();
}
else if ( numEntries === 0 ) {
inputText = searchLineEdit.node().value;
// check if input text ends or begins with with space
// remove first spaces
var clearedText = inputText.replace(/%20/g, " ");
while ( clearedText.beginsWith(" ") ) {
clearedText = clearedText.substr(1, clearedText.length);
}
// remove ending spaces
while ( clearedText.endsWith(" ") ) {
clearedText = clearedText.substr(0, clearedText.length - 1);
}
var iri = clearedText.replace(/ /g, "%20");
var valid = ValidURL(iri);
// validate url:
if ( valid ) {
var ontM = graph.options().ontologyMenu();
ontM.setIriText(iri);
searchLineEdit.node().value = "";
}
else {
console.log(iri + " is not a valid URL!");
}
}
}
if ( d3.event.keyCode === 38 ) {
move = -1;
searchMenu.showSearchEntries();
}
if ( d3.event.keyCode === 40 ) {
move = +1;
searchMenu.showSearchEntries();
}
var newSelection = selectedEntry + move;
if ( newSelection !== selectedEntry ) {
if ( newSelection < 0 && selectedEntry <= 0 ) {
htmlCollection[0].setAttribute('class', "dbEntrySelected");
}
if ( newSelection >= numEntries ) {
htmlCollection[selectedEntry].setAttribute('class', "dbEntrySelected");
}
if ( newSelection >= 0 && newSelection < numEntries ) {
htmlCollection[newSelection].setAttribute('class', "dbEntrySelected");
if ( selectedEntry >= 0 )
htmlCollection[selectedEntry].setAttribute('class', "dbEntry");
}
}
}
searchMenu.getSearchString = function (){
return searchLineEdit.node().value;
};
function clearSearchEntries(){
var htmlCollection = m_search.node().children;
var numEntries = htmlCollection.length;
for ( var i = 0; i < numEntries; i++ ) {
htmlCollection[0].remove();
}
results = [];
resultID = [];
}
function createSearchEntries(){
inputText = searchLineEdit.node().value;
var i;
var lc_text = inputText.toLowerCase();
var token;
for ( i = 0; i < dictionary.length; i++ ) {
var tokenElement = dictionary[i];
if ( tokenElement === undefined ) {
//@WORKAROUND : nodes with undefined labels are skipped
//@FIX: these nodes are now not added to the dictionary
continue;
}
token = dictionary[i].toLowerCase();
if ( token.indexOf(lc_text) > -1 ) {
results.push(dictionary[i]);
resultID.push(i);
}
}
}
function measureTextWidth( text, textStyle ){
// Set a default value
if ( !textStyle ) {
textStyle = "text";
}
var d = d3.select("body")
.append("div")
.attr("class", textStyle)
.attr("id", "width-test") // tag this element to identify it
.attr("style", "position:absolute; float:left; white-space:nowrap; visibility:hidden;")
.text(text),
w = document.getElementById("width-test").offsetWidth;
d.remove();
return w;
}
function cropText( input ){
var maxWidth = 250;
var textStyle = "dbEntry";
var truncatedText = input;
var textWidth;
var ratio;
var newTruncatedTextLength;
while ( true ) {
textWidth = measureTextWidth(truncatedText, textStyle);
if ( textWidth <= maxWidth ) {
break;
}
ratio = textWidth / maxWidth;
newTruncatedTextLength = Math.floor(truncatedText.length / ratio);
// detect if nothing changes
if ( truncatedText.length === newTruncatedTextLength ) {
break;
}
truncatedText = truncatedText.substring(0, newTruncatedTextLength);
}
if ( input.length > truncatedText.length ) {
return input.substring(0, truncatedText.length - 6);
}
return input;
}
function createDropDownElements(){
var numEntries;
var copyRes = results;
var i;
var token;
var newResults = [];
var newResultsIds = [];
var lc_text = searchLineEdit.node().value.toLowerCase();
// set the number of shown results to be maxEntries or less;
numEntries = results.length;
if ( numEntries > maxEntries )
numEntries = maxEntries;
for ( i = 0; i < numEntries; i++ ) {
// search for the best entry
var indexElement = 1000000;
var lengthElement = 1000000;
var bestElement = -1;
for ( var j = 0; j < copyRes.length; j++ ) {
token = copyRes[j].toLowerCase();
var tIe = token.indexOf(lc_text);
var tLe = token.length;
if ( tIe > -1 && tIe <= indexElement && tLe <= lengthElement ) {
bestElement = j;
indexElement = tIe;
lengthElement = tLe;
}
}
newResults.push(copyRes[bestElement]);
newResultsIds.push(resultID[bestElement]);
copyRes[bestElement] = "";
}
// add the results to the entry menu
//******************************************
numEntries = results.length;
if ( numEntries > maxEntries )
numEntries = maxEntries;
var filteredOutElements = 0;
for ( i = 0; i < numEntries; i++ ) {
//add results to the dropdown menu
var testEntry = document.createElement('li');
testEntry.setAttribute('elementID', newResultsIds[i]);
testEntry.onclick = handleClick(newResultsIds[i]);
testEntry.setAttribute('class', "dbEntry");
var entries = mergedIdList[newResultsIds[i]];
var eLen = entries.length;
var croppedText = cropText(newResults[i]);
var el0 = entries[0];
var allSame = true;
var nodeMap = graph.getNodeMapForSearch();
var visible = eLen;
if ( eLen > 1 ) {
for ( var q = 0; q < eLen; q++ ) {
if ( nodeMap[entries[q]] === undefined ) {
visible--;
}
}
}
for ( var a = 0; a < eLen; a++ ) {
if ( el0 !== entries[a] ) {
allSame = false;
}
}
if ( croppedText !== newResults[i] ) {
// append ...(#numElements) if needed
if ( eLen > 1 && allSame === false ) {
if ( eLen !== visible )
croppedText += "... (" + visible + "/" + eLen + ")";
}
else {
croppedText += "...";
}
testEntry.title = newResults[i];
}
else {
if ( eLen > 1 && allSame === false ) {
if ( eLen !== visible )
croppedText += " (" + visible + "/" + eLen + ")";
else
croppedText += " (" + eLen + ")";
}
}
var searchEntryNode = d3.select(testEntry);
if ( eLen === 1 || allSame === true ) {
if ( nodeMap[entries[0]] === undefined ) {
searchEntryNode.style("color", "#979797");
testEntry.title = newResults[i] + "\nElement is filtered out.";
testEntry.onclick = function (){
};
d3.select(testEntry).style("cursor", "default");
filteredOutElements++;
}
} else {
if ( visible < 1 ) {
searchEntryNode.style("color", "#979797");
testEntry.onclick = function (){
};
testEntry.title = newResults[i] + "\nAll elements are filtered out.";
d3.select(testEntry).style("cursor", "default");
filteredOutElements++;
} else {
searchEntryNode.style("color", "");
}
if ( visible < eLen && visible > 1 ) {
testEntry.title = newResults[i] + "\n" + visible + "/" + eLen + " elements are visible.";
}
}
searchEntryNode.node().innerHTML = croppedText;
m_search.node().appendChild(testEntry);
}
}
function handleAutoCompletion(){
/** pre condition: autoCompletion has already a valid text**/
clearSearchEntries();
createSearchEntries();
createDropDownElements();
}
function userInput(){
c_locate.classed("highlighted", false);
c_locate.node().title = "Nothing to locate";
if ( dictionaryUpdateRequired ) {
updateSearchDictionary();
}
graph.resetSearchHighlight();
if ( dictionary.length === 0 ) {
console.log("dictionary is empty");
return;
}
inputText = searchLineEdit.node().value;
clearSearchEntries();
if ( inputText.length !== 0 ) {
createSearchEntries();
createDropDownElements();
}
searchMenu.showSearchEntries();
}
function handleClick( elementId ){
return function (){
var id = elementId;
var correspondingIds = mergedIdList[id];
// autoComplete the text for the user
var autoComStr = entryNames[id];
searchLineEdit.node().value = autoComStr;
graph.resetSearchHighlight();
graph.highLightNodes(correspondingIds);
c_locate.node().title = "Locate search term";
if ( autoComStr !== inputText ) {
handleAutoCompletion();
}
searchMenu.hideSearchEntries();
};
}
searchMenu.clearText = function (){
searchLineEdit.node().value = "";
c_locate.classed("highlighted", false);
c_locate.node().title = "Nothing to locate";
var htmlCollection = m_search.node().children;
var numEntries = htmlCollection.length;
for ( var i = 0; i < numEntries; i++ ) {
htmlCollection[0].remove();
}
};
return searchMenu;
};