|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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"); |
|
|
|
|
|
|
|
|
String.prototype.beginsWith = function ( string ){ |
|
|
return (this.indexOf(string) === 0); |
|
|
}; |
|
|
|
|
|
searchMenu.requestDictionaryUpdate = function (){ |
|
|
dictionaryUpdateRequired = true; |
|
|
|
|
|
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); |
|
|
|
|
|
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 ) { |
|
|
|
|
|
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 (){ |
|
|
|
|
|
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 ) { |
|
|
|
|
|
htmlCollection[selectedEntry].onclick(); |
|
|
searchMenu.hideSearchEntries(); |
|
|
} |
|
|
else if ( numEntries === 0 ) { |
|
|
inputText = searchLineEdit.node().value; |
|
|
|
|
|
|
|
|
var clearedText = inputText.replace(/%20/g, " "); |
|
|
while ( clearedText.beginsWith(" ") ) { |
|
|
clearedText = clearedText.substr(1, clearedText.length); |
|
|
} |
|
|
|
|
|
while ( clearedText.endsWith(" ") ) { |
|
|
clearedText = clearedText.substr(0, clearedText.length - 1); |
|
|
} |
|
|
var iri = clearedText.replace(/ /g, "%20"); |
|
|
|
|
|
var valid = ValidURL(iri); |
|
|
|
|
|
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 ) { |
|
|
|
|
|
|
|
|
continue; |
|
|
} |
|
|
token = dictionary[i].toLowerCase(); |
|
|
if ( token.indexOf(lc_text) > -1 ) { |
|
|
results.push(dictionary[i]); |
|
|
resultID.push(i); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function measureTextWidth( text, textStyle ){ |
|
|
|
|
|
if ( !textStyle ) { |
|
|
textStyle = "text"; |
|
|
} |
|
|
var d = d3.select("body") |
|
|
.append("div") |
|
|
.attr("class", textStyle) |
|
|
.attr("id", "width-test") |
|
|
.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); |
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
numEntries = results.length; |
|
|
if ( numEntries > maxEntries ) |
|
|
numEntries = maxEntries; |
|
|
|
|
|
|
|
|
for ( i = 0; i < numEntries; i++ ) { |
|
|
|
|
|
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] = ""; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
numEntries = results.length; |
|
|
if ( numEntries > maxEntries ) |
|
|
numEntries = maxEntries; |
|
|
|
|
|
var filteredOutElements = 0; |
|
|
for ( i = 0; i < numEntries; i++ ) { |
|
|
|
|
|
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] ) { |
|
|
|
|
|
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(){ |
|
|
|
|
|
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]; |
|
|
|
|
|
|
|
|
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; |
|
|
}; |
|
|
|