updates for concept search service, adding LCSH search capability
This commit is contained in:
parent
e032ceeca4
commit
ba1c6c7075
10 changed files with 1009 additions and 135 deletions
|
@ -1,6 +1,6 @@
|
|||
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||
<#import "lib-vivo-form.ftl" as lvf>
|
||||
|
||||
<#include "addAssociatedConceptVocabSpecificDisplay.ftl" >
|
||||
<#assign existingConcepts = editConfiguration.pageData.existingConcepts/>
|
||||
<#assign userDefinedConceptUrl = editConfiguration.pageData.userDefinedConceptUrl/>
|
||||
<#assign sources = editConfiguration.pageData.searchServices/>
|
||||
|
@ -35,29 +35,44 @@
|
|||
|
||||
|
||||
|
||||
<ul id="existingConcepts" >
|
||||
<ul id="existingConcepts">
|
||||
|
||||
<script type="text/javascript">
|
||||
var existingConceptsData = [];
|
||||
</script>
|
||||
<#if (existingConcepts?size > 0)>
|
||||
<li class="conceptHeadings conceptsListContainer">
|
||||
<div class="row">
|
||||
<div class="column conceptLabelInfo">
|
||||
<h4>Concept (Type)</h4>
|
||||
</div>
|
||||
<div class="column conceptVocabSource">
|
||||
<h4>Vocabulary Source</h4>
|
||||
</div>
|
||||
<div class="column">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</#if>
|
||||
|
||||
<#list existingConcepts as existingConcept>
|
||||
<li class="existingConcept">
|
||||
|
||||
<span class="concept">
|
||||
|
||||
<span class="conceptWrapper">
|
||||
<span class="conceptLabel"> ${existingConcept.conceptLabel}
|
||||
<#if existingConcept.vocabURI?has_content && existingConcept.vocabLabel?has_content>
|
||||
(${existingConcept.vocabLabel})
|
||||
</#if>
|
||||
<li class="existingConcept conceptsListContainer">
|
||||
<div class="row">
|
||||
<div class="column conceptLabelInfo"> ${existingConcept.conceptLabel}
|
||||
<#if existingConcept.conceptSemanticTypeLabel?has_content>
|
||||
${existingConcept.conceptSemanticTypeLabel}
|
||||
(${existingConcept.conceptSemanticTypeLabel})
|
||||
</#if>
|
||||
</span>
|
||||
</span>
|
||||
<a href="${urls.base}/edit/primitiveRdfEdit" class="remove" title="${i18n().remove_capitalized}">${i18n().remove_capitalized}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="column conceptVocabSource">
|
||||
<#if existingConcept.vocabURI?has_content && existingConcept.vocabLabel?has_content>
|
||||
${existingConcept.vocabLabel}
|
||||
</#if>
|
||||
</div>
|
||||
<div class="column">
|
||||
<a href="${urls.base}/edit/primitiveRdfEdit" class="remove" title="${i18n().remove_capitalized}">${i18n().remove_capitalized}</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -86,7 +101,7 @@
|
|||
<form id="addConceptForm" class="customForm" action="${submitUrl}">
|
||||
<#assign checkedSource = false />
|
||||
<h4 class="services">${i18n().external_vocabulary_services}</h4>
|
||||
<#list sources?keys as sourceUri>
|
||||
<#list sources?keys?sort as sourceUri>
|
||||
<#assign thisSource = sources[sourceUri]/>
|
||||
<input type="radio" name="source" value="${sourceUri}" role="radio" <#if checkedSource = false><#assign checkedSource = true/>checked="checked"</#if>>
|
||||
<label class="inline" for="${thisSource.label}"> <a href="${thisSource.url}">${thisSource.label}</a> (${thisSource.description})</label>
|
||||
|
@ -101,12 +116,21 @@
|
|||
<input type="hidden" id="conceptSource" name="conceptSource" value="" /> <!-- Field value populated by JavaScript -->
|
||||
<input type="hidden" id="conceptSemanticTypeURI" name="conceptSemanticTypeURI" value="" /> <!-- Field value populated by JavaScript -->
|
||||
<input type="hidden" id="conceptSemanticTypeLabel" name="conceptSemanticTypeLabel" value="" /> <!-- Field value populated by JavaScript -->
|
||||
|
||||
<input type="hidden" id="conceptBroaderURI" name="conceptBroaderURI" value=""/><!-- Field value populated by JavaScript -->
|
||||
<input type="hidden" id="conceptNarrowerURI" name="conceptNarrowerURI" value=""/><!-- Field value populated by JavaScript -->
|
||||
<div id="indicator" class="hidden">
|
||||
<img id="loadingIndicator" class="indicator" src="${urls.base}/images/indicatorWhite.gif" alt="${i18n().processing_indicator}"/>
|
||||
</div>
|
||||
<div id="selectedConcept" name="selectedConcept" class="acSelection">
|
||||
<p class="inline">
|
||||
|
||||
</p>
|
||||
|
||||
<!-- Search results populated by JavaScript -->
|
||||
</div>
|
||||
<div id="showHideResults" name="showHideResults">
|
||||
<a href="#" id="showHideLink">Results</a>
|
||||
</div>
|
||||
<div id="errors" name="errors"></div>
|
||||
|
||||
<input type="hidden" name="editKey" id="editKey" value="${editKey}"/>
|
||||
|
@ -133,16 +157,21 @@ var customFormData = {
|
|||
predicateUri: '${editConfiguration.predicateUri}',
|
||||
inversePredicateUri: '${inversePredicate}'
|
||||
};
|
||||
var vocabSpecificDisplay = {};
|
||||
<#list vocabSpecificDisplay?keys as vocab>
|
||||
vocabSpecificDisplay["${vocab}"] = "${vocabSpecificDisplay[vocab]}";
|
||||
</#list>
|
||||
var i18nStrings = {
|
||||
vocServiceUnavailable: '${i18n().vocabulary_service_unavailable}',
|
||||
noResultsFound: '${i18n().no_serch_results_found}',
|
||||
labelTypeString: '${i18n().label_type}',
|
||||
defaultLabelTypeString: '${i18n().label_type}',
|
||||
definitionString: '${i18n().definition_capitalized}',
|
||||
bestMatchString: '${i18n().best_match}',
|
||||
selectTermFromResults: '${i18n().select_term_from_results}',
|
||||
selectVocSource: '${i18n().select_vocabulary_source_to_search}',
|
||||
confirmTermDelete: '${i18n().confirm_term_deletion}',
|
||||
errorTernNotRemoved: '${i18n().error_term_not_deleted}'
|
||||
errorTernNotRemoved: '${i18n().error_term_not_deleted}',
|
||||
vocabSpecificLabels: vocabSpecificDisplay
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||
|
||||
<#--The original concept javascript is service independent, i.e. all vocabulary service information is returned from the servlet
|
||||
and the template itself generates the same display for all the services. Right now we would like to show a different label
|
||||
in the search results based on the service. I am storing that information here and later we can consider how the display
|
||||
can return to being independent of vocabulary service-specific display options.
|
||||
These values will be passed to the javascript-->
|
||||
|
||||
<#assign vocabSpecificDisplay = {
|
||||
"http://link.informatics.stonybrook.edu/umls":"${i18n().label_type}",
|
||||
"http://aims.fao.org/aos/agrovoc/agrovocScheme":"${i18n().label_altLabels}",
|
||||
"http://www.eionet.europa.eu/gemet/gemetThesaurus":"${i18n().label_type}",
|
||||
"http://id.loc.gov/authorities/subjects":"${i18n().label_altLabels}"
|
||||
}/>
|
|
@ -1,5 +1,9 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
.conceptsListContainer {
|
||||
overflow:hidden;
|
||||
width:100%;
|
||||
}
|
||||
.concepts .column {
|
||||
float:left;
|
||||
padding-right:3px;
|
||||
|
@ -44,4 +48,29 @@ form#addConceptForm {
|
|||
form#addConceptForm span#createOwnOne{
|
||||
float:left;
|
||||
margin-top:24px
|
||||
}
|
||||
|
||||
/*For existing concepts, display will also be tabular with columns*/
|
||||
|
||||
.existingConcept .row, .conceptHeadings .row {
|
||||
clear:both;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.existingConcept .column , .conceptHeadings .column {
|
||||
float:left;
|
||||
padding-right:3px;
|
||||
clear:none !important; /*Overriding customFor div's clearing*/
|
||||
}
|
||||
/*label and semantic type if it exists*/
|
||||
.existingConcept .conceptLabelInfo, .conceptHeadings .conceptLabelInfo {
|
||||
width:220px;
|
||||
}
|
||||
|
||||
.existingConcept .conceptVocabSource, .conceptHeadings .conceptVocabSource {
|
||||
width:220px;
|
||||
}
|
||||
|
||||
.conceptHeadings .row {
|
||||
border-bottom: 1px solid #5F6464;
|
||||
}
|
|
@ -55,12 +55,19 @@ var addConceptForm = {
|
|||
this.externalConceptLabel = $('#conceptLabel');
|
||||
this.externalConceptSource = $('#conceptSource');
|
||||
this.externalConceptSemanticTypeLabel = $("#conceptSemanticTypeLabel");
|
||||
this.externalConceptBroaderUris = $("#conceptBroaderURI");
|
||||
this.externalConceptNarrowerUris = $("#conceptNarrowerURI");
|
||||
//remove links
|
||||
this.removeConceptLinks = $('a.remove');
|
||||
this.errors = $('#errors');
|
||||
this.createOwn1 = $('#createOwnOne');
|
||||
this.createOwn2 = $('#createOwnTwo');
|
||||
this.orSpan = $('span.or')
|
||||
this.loadingIndicator = $("#indicator");
|
||||
this.showHideSearchResults = $("#showHideResults");
|
||||
//Value we are setting to cut off length of alternate labels string
|
||||
this.maxNumberAlternateLabels = 4;
|
||||
this.numberOfMaxInitialSearchResults = 7;
|
||||
},
|
||||
|
||||
initPage: function() {
|
||||
|
@ -87,6 +94,10 @@ var addConceptForm = {
|
|||
addConceptForm.removeExistingConcept(this);
|
||||
return false;
|
||||
});
|
||||
this.showHideSearchResults.find("a#showHideLink").click(function() {
|
||||
addConceptForm.showHideMultipleSearchResults(this);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
initForm: function() {
|
||||
// Hide the button that shows the form
|
||||
|
@ -99,7 +110,9 @@ var addConceptForm = {
|
|||
//Also clear the search input
|
||||
this.searchTerm.val("");
|
||||
this.cancel.unbind('click');
|
||||
|
||||
//make sure results loading indicator is hidden
|
||||
this.loadingIndicator.addClass("hidden");
|
||||
this.showHideSearchResults.hide();
|
||||
// Show the form
|
||||
this.form.show();
|
||||
},
|
||||
|
@ -114,6 +127,8 @@ var addConceptForm = {
|
|||
},
|
||||
clearSearchResults:function() {
|
||||
$('#selectedConcept').empty();
|
||||
//Hide the indicator icon if still there
|
||||
$("#indicator").addClass("hidden");
|
||||
},
|
||||
clearErrors:function() {
|
||||
addConceptForm.errors.empty();
|
||||
|
@ -134,6 +149,25 @@ var addConceptForm = {
|
|||
this.hideForm();
|
||||
this.showFormButtonWrapper.show();
|
||||
},
|
||||
showHideMultipleSearchResults: function(link) {
|
||||
if($(link).hasClass("showmore")) {
|
||||
//if clicking and already says show more then need to show the rest of the results
|
||||
$("li.concepts").show(); //show everything
|
||||
$(link).html("Show fewer results");
|
||||
$(link).removeClass("showmore");
|
||||
} else {
|
||||
//if clicking and does not say show more than need to show less
|
||||
$("li.concepts").slice(addConceptForm.numberOfMaxInitialSearchResults).hide();
|
||||
$(link).html("Show more results");
|
||||
$(link).addClass("showmore");
|
||||
}
|
||||
},
|
||||
//reset this to default, which is hidden with show more link
|
||||
resetShowHideMultipleSearchResults: function() {
|
||||
addConceptForm.showHideSearchResults.hide();
|
||||
addConceptForm.showHideSearchResults.find("a#showHideLink").html("Show more results");
|
||||
addConceptForm.showHideSearchResults.find("a#showHideLink").addClass("showmore");
|
||||
},
|
||||
submitSearchTerm: function() {
|
||||
//Get value of search term
|
||||
var searchValue = this.searchTerm.val();
|
||||
|
@ -145,7 +179,11 @@ var addConceptForm = {
|
|||
}
|
||||
var vocabSourceValue = checkedVocabSource.val();
|
||||
var dataServiceUrl = addConceptForm.dataServiceUrl + "?searchTerm=" + encodeURIComponent(searchValue) + "&source=" + encodeURIComponent(vocabSourceValue);
|
||||
//This should return an object including the concept list or any errors if there are any
|
||||
//Show the loading icon until the results appear
|
||||
addConceptForm.loadingIndicator.removeClass("hidden");
|
||||
//Hide and reset the show more button
|
||||
addConceptForm.resetShowHideMultipleSearchResults();
|
||||
//This should return an object including the concept list or any errors if there are any
|
||||
$.getJSON(dataServiceUrl, function(results) {
|
||||
var htmlAdd = "";
|
||||
var vocabUnavailable = "<p>" + addConceptForm.vocServiceUnavailable + "</p>";
|
||||
|
@ -166,7 +204,7 @@ var addConceptForm = {
|
|||
//For each result, display
|
||||
if(numberTotalMatches > 0) {
|
||||
htmlAdd = "<ul class='dd' id='concepts' name='concepts'>";
|
||||
htmlAdd+= addConceptForm.addResultsHeader();
|
||||
htmlAdd+= addConceptForm.addResultsHeader(vocabSourceValue);
|
||||
//Show best matches first
|
||||
for(i = 0; i < numberBestMatches; i++) {
|
||||
var conceptResult = bestMatchResults[i];
|
||||
|
@ -184,6 +222,8 @@ var addConceptForm = {
|
|||
|
||||
}
|
||||
if(htmlAdd.length) {
|
||||
//hide the loading icon again
|
||||
addConceptForm.loadingIndicator.addClass("hidden");
|
||||
$('#selectedConcept').html(htmlAdd);
|
||||
if (htmlAdd.indexOf("No search results") >= 0) {
|
||||
addConceptForm.showHiddenElements(hasResults);
|
||||
|
@ -191,6 +231,8 @@ var addConceptForm = {
|
|||
else {
|
||||
hasResults = true;
|
||||
addConceptForm.showHiddenElements(hasResults);
|
||||
//Here, tweak the display based on the number of results
|
||||
addConceptForm.displayUptoMaxResults();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -203,9 +245,12 @@ var addConceptForm = {
|
|||
var definedBy = conceptResult.definedBy;
|
||||
var type = conceptResult.type;
|
||||
var uri = conceptResult.uri;
|
||||
//also adding broader and narrower uris wherever they exist
|
||||
var broaderUris = conceptResult.broaderURIList;
|
||||
var narrowerUris = conceptResult.narrowerURIList;
|
||||
//this will be null if there are no alternate labels
|
||||
var altLabels = conceptResult.altLabelList;
|
||||
return addConceptForm.generateIndividualConceptDisplay(uri, label, altLabels, definition, type, definedBy, isBestMatch);
|
||||
return addConceptForm.generateIndividualConceptDisplay(uri, label, altLabels, definition, type, definedBy, isBestMatch, broaderUris, narrowerUris);
|
||||
},
|
||||
//This should now return all best matches in one array and other results in another
|
||||
parseResults:function(resultsArray) {
|
||||
|
@ -225,10 +270,19 @@ var addConceptForm = {
|
|||
}
|
||||
return {"bestMatch":bestMatchResults, "alternate":alternateResults};
|
||||
},
|
||||
addResultsHeader:function() {
|
||||
var htmlAdd = "<li class='concepts'><div class='row'><span class='column conceptLabel'>" + addConceptForm.labelTypeString + " </span><span class='column conceptDefinition'>" + addConceptForm.definitionString + "</span><span class='column'>" + addConceptForm.bestMatchString + "</span></div></li>";
|
||||
addResultsHeader:function(vocabSourceValue) {
|
||||
var htmlAdd = "<li class='concepts'><div class='row'><span class='column conceptLabel'>" +
|
||||
addConceptForm.getVocabSpecificColumnLabel(vocabSourceValue) + " </span><span class='column conceptDefinition'>" + addConceptForm.definitionString + "</span><span class='column'>" + addConceptForm.bestMatchString + "</span></div></li>";
|
||||
return htmlAdd;
|
||||
},
|
||||
//currently just the first column label depends on which service has been utilized
|
||||
getVocabSpecificColumnLabel: function(vocabSourceValue) {
|
||||
var columnLabel = addConceptForm.vocabSpecificLabels[vocabSourceValue];
|
||||
if(columnLabel == undefined) {
|
||||
columnLabel = addConceptForm.defaultLabelTypeString;
|
||||
}
|
||||
return columnLabel;
|
||||
},
|
||||
hideSearchResults:function() {
|
||||
this.selectedConcept.hide();
|
||||
},
|
||||
|
@ -239,11 +293,14 @@ var addConceptForm = {
|
|||
}
|
||||
var i;
|
||||
var len = checkedElements.length;
|
||||
var checkedConcept, checkedConceptElement, conceptLabel, conceptSource, conceptSemanticType;
|
||||
var checkedConcept, checkedConceptElement, conceptLabel, conceptSource, conceptSemanticType,
|
||||
conceptBroaderUri, conceptNarrowerUri;
|
||||
var conceptNodes = [];
|
||||
var conceptLabels = [];
|
||||
var conceptSources = [];
|
||||
var conceptSemanticTypes = [];
|
||||
var conceptBroaderUris = []; //each array element can be a string which is comma delimited for multiple uris
|
||||
var conceptNarrowerUris = [];//same as above
|
||||
|
||||
checkedElements.each(function() {
|
||||
checkedConceptElement = $(this);
|
||||
|
@ -251,22 +308,29 @@ var addConceptForm = {
|
|||
conceptLabel = checkedConceptElement.attr("label");
|
||||
conceptSource = checkedConceptElement.attr("conceptDefinedBy");
|
||||
conceptSemanticType = checkedConceptElement.attr("conceptType");
|
||||
conceptBroaderUri = checkedConceptElement.attr("broaderUris");
|
||||
conceptNarrowerUri = checkedConceptElement.attr("narrowerUris");
|
||||
conceptNodes.push(checkedConcept);
|
||||
conceptLabels.push(conceptLabel);
|
||||
conceptSources.push(conceptSource);
|
||||
conceptSemanticTypes.push(conceptSemanticType);
|
||||
conceptBroaderUris.push(conceptBroaderUri);
|
||||
conceptNarrowerUris.push(conceptNarrowerUri);
|
||||
});
|
||||
this.externalConceptURI.val(conceptNodes);
|
||||
this.externalConceptLabel.val(conceptLabels);
|
||||
this.externalConceptSource.val(conceptSources);
|
||||
this.externalConceptSemanticTypeLabel.val(conceptSemanticTypes);
|
||||
this.externalConceptBroaderUris.val(conceptBroaderUris);
|
||||
this.externalConceptNarrowerUris.val(conceptNarrowerUris);
|
||||
|
||||
return true;
|
||||
},
|
||||
generateIndividualConceptDisplay: function(cuiURI, label, altLabels, definition, type, definedBy, isBestMatch) {
|
||||
generateIndividualConceptDisplay: function(cuiURI, label, altLabels, definition, type, definedBy, isBestMatch, broaderUris, narrowerUris) {
|
||||
var htmlAdd = "<li class='concepts'>" +
|
||||
"<div class='row'>" +
|
||||
"<div class='column conceptLabel'>" +
|
||||
addConceptForm.generateIndividualCUIInput(cuiURI, label, type, definedBy) +
|
||||
addConceptForm.generateIndividualCUIInput(cuiURI, label, type, definedBy, broaderUris, narrowerUris) +
|
||||
addConceptForm.generateIndividualLabelsDisplay(label, altLabels) + addConceptForm.generateIndividualTypeDisplay(type) + "</div>" +
|
||||
addConceptForm.generateIndividualDefinitionDisplay(definition) +
|
||||
addConceptForm.generateBestOrAlternate(isBestMatch) +
|
||||
|
@ -274,14 +338,23 @@ var addConceptForm = {
|
|||
"</li>";
|
||||
return htmlAdd;
|
||||
},
|
||||
generateIndividualCUIInput:function(cuiURI, label, type, definedBy) {
|
||||
return "<input type='checkbox' name='CUI' value='" + cuiURI + "' label='" + label + "' conceptType='" + type + "' conceptDefinedBy='" + definedBy + "'/>";
|
||||
generateIndividualCUIInput:function(cuiURI, label, type, definedBy, broaderUris, narrowerUris) {
|
||||
return "<input type='checkbox' name='CUI' value='" + cuiURI + "' label='" +
|
||||
label + "' conceptType='" + type + "' conceptDefinedBy='" + definedBy + "' " +
|
||||
"broaderUris='" + broaderUris + "' narrowerUris='" + narrowerUris + "'/>";
|
||||
},
|
||||
//In case there are multiple labels display those
|
||||
generateIndividualLabelsDisplay:function(label, altLabels) {
|
||||
var labelDisplay = label;
|
||||
var displayAltLabels = altLabels;
|
||||
if(altLabels != null && altLabels.length > 0) {
|
||||
labelDisplay += "<br> [" + altLabels + "]";
|
||||
//Certain vocabulary services might return a long list of alternate labels, in which case we will show fewer
|
||||
//display only upto a certain number of alternate labels and use an ellipsis to signify there
|
||||
//are additional terms
|
||||
if(altLabels.length > addConceptForm.maxNumberAlternateLabels) {
|
||||
displayAltLabels = altLabels.slice(0, addConceptForm.maxNumberAlternateLabels) + ",...";
|
||||
}
|
||||
labelDisplay += "<br> (" + displayAltLabels + ")";
|
||||
}
|
||||
return labelDisplay;
|
||||
},
|
||||
|
@ -307,6 +380,18 @@ var addConceptForm = {
|
|||
}
|
||||
return "<div class='column'><div class='" + className + "'> </div></div>";
|
||||
},
|
||||
//Certain vocabulary services return a great number of results, we would like the ability to show more or less of those results
|
||||
displayUptoMaxResults:function() {
|
||||
var numberConcepts = $("li.concepts").length;
|
||||
if(numberConcepts > addConceptForm.numberOfMaxInitialSearchResults) {
|
||||
$("li.concepts").slice(addConceptForm.numberOfMaxInitialSearchResults).hide();
|
||||
//Hide the link for showing/hiding search results
|
||||
addConceptForm.showHideSearchResults.show();
|
||||
addConceptForm.showHideSearchResults.find("a#showHideLink").html("Show more results");
|
||||
addConceptForm.showHideSearchResults.find("a#showHideLink").addClass("showmore");
|
||||
}
|
||||
|
||||
},
|
||||
validateConceptSelection:function(checkedElements) {
|
||||
var numberElements = checkedElements.length;
|
||||
if(numberElements < 1) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue