[VIVO-1249] Add capability maps for research areas (#35)

* Initial capability map implementation

* Add autocomplete

* Make headers update the info panel

* Ajax retrieval of person details - reduce memory requirements and startup time

* Remove old referer info

* Handle missing labels.

* Fix refresh button; add menu entry for new installations
This commit is contained in:
grahamtriggs 2016-06-20 17:01:51 +01:00 committed by GitHub
parent 1443ff6685
commit ee7b974ac5
21 changed files with 3126 additions and 36 deletions

View file

@ -0,0 +1,284 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap;
import com.google.gson.Gson;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants;
import edu.cornell.mannlib.vitro.webapp.visualization.exceptions.MalformedQueryParametersException;
import edu.cornell.mannlib.vitro.webapp.visualization.model.ConceptLabelMap;
import edu.cornell.mannlib.vitro.webapp.visualization.model.ConceptPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.model.OrganizationPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches;
import edu.cornell.mannlib.vitro.webapp.visualization.visutils.VisualizationRequestHandler;
import org.apache.axis.utils.StringUtils;
import org.apache.commons.logging.Log;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class CapabilityMapRequestHandler implements VisualizationRequestHandler {
@Override
public AuthorizationRequest getRequiredPrivileges() {
return null;
}
@Override
public ResponseValues generateStandardVisualization(VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
return null;
}
@Override
public ResponseValues generateVisualizationForShortURLRequests(Map<String, String> parameters, VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
return prepareMarkup(vitroRequest);
}
@Override
public Object generateAjaxVisualization(VitroRequest vitroRequest, Log log, Dataset dataSource) throws MalformedQueryParametersException {
ConceptLabelMap conceptLabelMap = VisualizationCaches.conceptToLabel.get(vitroRequest.getRDFService());
ConceptPeopleMap conceptPeopleMap = VisualizationCaches.conceptToPeopleMap.get(vitroRequest.getRDFService());
OrganizationPeopleMap organizationPeopleMap = VisualizationCaches.organisationToPeopleMap.get(vitroRequest.getRDFService());
Map<String, String> organizationLabels = VisualizationCaches.organizationLabels.get(vitroRequest.getRDFService());
String data = vitroRequest.getParameter("data");
if (!StringUtils.isEmpty(data)) {
if ("concepts".equalsIgnoreCase(data)) {
Set<String> concepts = new HashSet<String>();
for (String conceptKey : conceptPeopleMap.conceptToPeople.keySet()) {
String label = conceptLabelMap.conceptToLabel.get(conceptKey);
if (!StringUtils.isEmpty(label)) {
concepts.add(conceptLabelMap.conceptToLabel.get(conceptKey));
}
}
Gson gson = new Gson();
return gson.toJson(concepts);
}
return "";
}
String personParam = vitroRequest.getParameter("person");
if (!StringUtils.isEmpty(personParam)) {
CapabilityMapResponse response = new CapabilityMapResponse();
CapabilityMapResult result = new CapabilityMapResult();
fillPersonDetails(vitroRequest.getRDFService(), personParam, result);
if (StringUtils.isEmpty(result.firstName) && StringUtils.isEmpty(result.lastName)) {
result.lastName = "Missing Name";
}
Set<String> concepts = conceptPeopleMap.personToConcepts.get(personParam);
if (concepts != null) {
result.subjectArea = concepts.toArray(new String[concepts.size()]);
}
Set<String> organizations = organizationPeopleMap.organizationToPeople.get(personParam);
if (organizations != null) {
for (String org : organizations) {
result.department = organizationLabels.get(org);
if (!StringUtils.isEmpty(result.department)) {
break;
}
}
}
response.results.add(result);
Gson gson = new Gson();
String callback = vitroRequest.getParameter("callback");
if (!StringUtils.isEmpty(callback)) {
return callback + "(" + gson.toJson(response) + ");";
}
return gson.toJson(response);
}
String query = vitroRequest.getParameter("query");
if (!StringUtils.isEmpty(query)) {
CapabilityMapResponse response = new CapabilityMapResponse();
Set<String> matchedConcepts = conceptLabelMap.lowerLabelToConcepts.get(query.toLowerCase());
Set<String> people = new HashSet<String>();
if (matchedConcepts != null) {
for (String uri : matchedConcepts) {
Set<String> peopleSet = conceptPeopleMap.conceptToPeople.get(uri);
if (peopleSet != null) {
people.addAll(peopleSet);
}
}
}
Set<String> clusterConcepts = new HashSet<String>();
for (String person : people) {
if (conceptPeopleMap.personToConcepts.containsKey(person)) {
clusterConcepts.addAll(conceptPeopleMap.personToConcepts.get(person));
}
}
if (matchedConcepts != null) {
clusterConcepts.removeAll(matchedConcepts);
}
Set<String> clusterLabels = new HashSet<String>();
for (String clusterConcept : clusterConcepts) {
String label = conceptLabelMap.conceptToLabel.get(clusterConcept);
if (!StringUtils.isEmpty(label)) {
clusterLabels.add(label);
}
}
String[] clusters = clusterLabels.toArray(new String[clusterLabels.size()]);
for (String person : people) {
CapabilityMapResult result = new CapabilityMapResult();
result.profileId = person;
result.query = query;
result.clusters = clusters;
response.results.add(result);
}
Gson gson = new Gson();
String callback = vitroRequest.getParameter("callback");
if (!StringUtils.isEmpty(callback)) {
return callback + "(" + gson.toJson(response) + ");";
}
return gson.toJson(response);
}
return "";
}
@Override
public Map<String, String> generateDataVisualization(VitroRequest vitroRequest, Log log, Dataset dataset) throws MalformedQueryParametersException {
return null;
}
private ResponseValues prepareMarkup(VitroRequest vreq) {
String standaloneTemplate = "capabilityMap.ftl";
Map<String, Object> body = new HashMap<String, Object>();
body.put("title", "Capability Map");
body.put("vivoDefaultNamespace", vreq.getWebappDaoFactory().getDefaultNamespace());
return new TemplateResponseValues(standaloneTemplate, body);
}
private void fillPersonDetails(final RDFService rdfService, final String personUri, final CapabilityMapResult result) {
try {
String construct = QueryConstants.getSparqlPrefixQuery() +
"CONSTRUCT {\n" +
" <" + personUri + "> a foaf:Person .\n" +
" <" + personUri + "> foaf:lastName ?lastName .\n" +
" <" + personUri + "> foaf:firstName ?firstName .\n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:familyName ?familyName .\n" +
" ?contactName vcard:givenName ?givenName .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?contactTitleLabel .\n" +
" <" + personUri + "> public:thumbnailImage ?directDownloadUrl .\n" +
"} WHERE {\n" +
" { \n" +
" <" + personUri + "> foaf:lastName ?lastName .\n" +
" } UNION { \n" +
" <" + personUri + "> foaf:firstName ?firstName .\n" +
" } UNION { \n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:familyName ?familyName .\n" +
" } UNION { \n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:givenName ?givenName .\n" +
" } UNION { \n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?contactTitleLabel .\n" +
" } UNION { \n" +
" <" + personUri + "> public:mainImage ?mainImage .\n" +
" ?mainImage public:thumbnailImage ?thumbnailImage .\n" +
" ?thumbnailImage public:downloadLocation ?downloadLocation .\n" +
" ?downloadLocation public:directDownloadUrl ?directDownloadUrl .\n" +
" } \n" +
"}\n";
Model constructedModel = ModelFactory.createDefaultModel();
rdfService.sparqlConstructQuery(construct, constructedModel);
String nameQuery = QueryConstants.getSparqlPrefixQuery() +
"SELECT ?familyName ?givenName ?lastName ?firstName ?title ?thumbnailUrl\n" +
"WHERE\n" +
"{\n" +
" <" + personUri + "> a foaf:Person .\n" + // ?person
" OPTIONAL {\n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" OPTIONAL { ?contactName vcard:familyName ?familyName . }\n" +
" OPTIONAL { ?contactName vcard:givenName ?givenName . }\n" +
" }\n" +
" OPTIONAL {\n" +
" <" + personUri + "> obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?title .\n" +
" }\n" +
" OPTIONAL { <" + personUri + "> foaf:lastName ?lastName . }\n" +
" OPTIONAL { <" + personUri + "> foaf:firstName ?firstName . }\n" +
" OPTIONAL { <" + personUri + "> public:thumbnailImage ?thumbnailUrl . }\n" +
"}\n";
QueryExecution qe = QueryExecutionFactory.create(nameQuery, constructedModel);
try {
new ResultSetConsumer() {
@Override
protected void processQuerySolution(QuerySolution qs) {
result.profileId = personUri;
result.firstName = null;
result.lastName = null;
result.thumbNail = null;
result.preferredTitle = null;
Literal familyNameNode = qs.getLiteral("familyName");
if (familyNameNode != null) {
result.lastName = familyNameNode.getString();
} else {
Literal lastNameNode = qs.getLiteral("lastName");
result.lastName = lastNameNode == null ? null : lastNameNode.getString();
}
Literal givenNameNode = qs.getLiteral("givenName");
if (givenNameNode != null) {
result.firstName = givenNameNode.getString();
} else {
Literal firstNameNode = qs.getLiteral("firstName");
result.firstName = firstNameNode == null ? null : firstNameNode.getString();
}
Literal thumbnailUrlNode = qs.getLiteral("thumbnailUrl");
result.thumbNail = thumbnailUrlNode == null ? null : thumbnailUrlNode.getString();
Literal titleNode = qs.getLiteral("title");
result.preferredTitle = titleNode == null ? null : titleNode.getString();
}
}.processResultSet(qe.execSelect());
} finally {
qe.close();
}
} catch (RDFServiceException e) {
}
}
}

View file

@ -0,0 +1,10 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap;
import java.util.ArrayList;
import java.util.List;
class CapabilityMapResponse {
List<CapabilityMapResult> results = new ArrayList<CapabilityMapResult>();
}

View file

@ -0,0 +1,68 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap;
import com.google.gson.annotations.SerializedName;
class CapabilityMapResult {
String[] clusters;
@SerializedName("md_1")
String profileId;
@SerializedName("md_2")
String description;
@SerializedName("md_3")
String thumbNail;
@SerializedName("md_4")
String department;
@SerializedName("md_5")
String overview;
@SerializedName("md_6")
String geographicFocus;
@SerializedName("md_7")
String geographicLocation;
@SerializedName("md_8")
String[] grants;
@SerializedName("md_A")
String firstName;
@SerializedName("md_B")
String lastName;
@SerializedName("md_F")
String fax;
@SerializedName("md_G")
String email;
@SerializedName("md_H")
String availableForSupervision;
@SerializedName("md_I")
String homepage;
@SerializedName("md_L")
String phoneNumber;
@SerializedName("md_U")
String[] publications;
@SerializedName("md_X")
String[] researchOverview;
@SerializedName("md_Y")
String[] subjectArea;
@SerializedName("md_Z")
String preferredTitle;
String query;
}

View file

@ -39,11 +39,12 @@ public class QueryConstants {
put("wos", "http://vivo.mannlib.cornell.edu/ns/ThomsonWOS/0.1#"); put("wos", "http://vivo.mannlib.cornell.edu/ns/ThomsonWOS/0.1#");
put("core", "http://vivoweb.org/ontology/core#"); put("core", "http://vivoweb.org/ontology/core#");
put("vivo", "http://vivo.library.cornell.edu/ns/0.1#"); put("vivo", "http://vivo.library.cornell.edu/ns/0.1#");
put("j.1", "http://aims.fao.org/aos/geopolitical.owl#"); put("geo", "http://aims.fao.org/aos/geopolitical.owl#");
put("j.2", "http://vitro.mannlib.cornell.edu/ns/vitro/public#"); put("public", "http://vitro.mannlib.cornell.edu/ns/vitro/public#");
put("afn", "http://jena.hpl.hp.com/ARQ/function#"); put("afn", "http://jena.hpl.hp.com/ARQ/function#");
put("vivosocnet", "http://vivo.cns.iu.edu/ns/#"); put("vivosocnet", "http://vivo.cns.iu.edu/ns/#");
put("obo", "http://purl.obolibrary.org/obo/");
put("vcard", "http://www.w3.org/2006/vcard/ns#");
} }; } };
public static String getSparqlPrefixQuery() { public static String getSparqlPrefixQuery() {

View file

@ -15,6 +15,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants;
import edu.cornell.mannlib.vitro.webapp.visualization.model.OrganizationPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches;
import mapping.ScienceMapping; import mapping.ScienceMapping;
@ -252,7 +253,7 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq
} }
Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService); Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService);
Map<String, Set<String>> organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService); OrganizationPeopleMap organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService);
Map<String, Set<String>> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; Map<String, Set<String>> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication;
Map<String, String> publicationToJournalMap = VisualizationCaches.publicationToJournal.get(rdfService); Map<String, String> publicationToJournalMap = VisualizationCaches.publicationToJournal.get(rdfService);
@ -267,7 +268,7 @@ public class MapOfScienceVisualizationRequestHandler implements VisualizationReq
orgPublicationsPeople, orgPublicationsPeople,
subOrgPublicationsMap, subOrgPublicationsMap,
subOrgMap, subOrgMap,
organisationToPeopleMap, organisationToPeopleMap.organizationToPeople,
personToPublicationMap personToPublicationMap
); );

View file

@ -0,0 +1,12 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ConceptLabelMap {
public final Map<String, String> conceptToLabel = new HashMap<String, String>();
public final Map<String, Set<String>> lowerLabelToConcepts = new HashMap<String, Set<String>>();
}

View file

@ -0,0 +1,12 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class ConceptPeopleMap {
public final Map<String, Set<String>> conceptToPeople = new HashMap<String, Set<String>>();
public final Map<String, Set<String>> personToConcepts = new HashMap<String, Set<String>>();
}

View file

@ -0,0 +1,12 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.model;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class OrganizationPeopleMap {
public final Map<String, Set<String>> organizationToPeople = new HashMap<String, Set<String>>();
public final Map<String, Set<String>> personToOrganizations = new HashMap<String, Set<String>>();
}

View file

@ -0,0 +1,11 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.model;
public class Person {
public String uri;
public String preferredTitle;
public String firstName;
public String lastName;
public String thumbnailUrl;
}

View file

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.visualization.model.OrganizationPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches;
@ -152,7 +153,7 @@ public class TemporalGrantVisualizationRequestHandler implements
} }
Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService); Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService);
Map<String, Set<String>> organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService); OrganizationPeopleMap organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService);
Map<String, String> orgMostSpecificLabelMap = VisualizationCaches.organizationToMostSpecificLabel.get(rdfService); Map<String, String> orgMostSpecificLabelMap = VisualizationCaches.organizationToMostSpecificLabel.get(rdfService);
Map<String, String> personMostSpecificLabelMap = VisualizationCaches.personToMostSpecificLabel.get(rdfService); Map<String, String> personMostSpecificLabelMap = VisualizationCaches.personToMostSpecificLabel.get(rdfService);
Map<String, Set<String>> personToGrantMap = VisualizationCaches.personToGrant.get(rdfService); Map<String, Set<String>> personToGrantMap = VisualizationCaches.personToGrant.get(rdfService);
@ -169,7 +170,7 @@ public class TemporalGrantVisualizationRequestHandler implements
orgGrantsPeople, orgGrantsPeople,
subOrgGrantsMap, subOrgGrantsMap,
subOrgMap, subOrgMap,
organisationToPeopleMap, organisationToPeopleMap.organizationToPeople,
personToGrantMap personToGrantMap
); );

View file

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.visualization.model.OrganizationPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.CounterUtils;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.OrgUtils;
import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches; import edu.cornell.mannlib.vitro.webapp.visualization.utilities.VisualizationCaches;
@ -91,7 +92,7 @@ public class TemporalPublicationVisualizationRequestHandler implements
Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService); Map<String, Set<String>> subOrgMap = VisualizationCaches.organizationSubOrgs.get(rdfService);
Map<String, String> orgMostSpecificLabelMap = VisualizationCaches.organizationToMostSpecificLabel.get(rdfService); Map<String, String> orgMostSpecificLabelMap = VisualizationCaches.organizationToMostSpecificLabel.get(rdfService);
Map<String, String> personMostSpecificLabelMap = VisualizationCaches.personToMostSpecificLabel.get(rdfService); Map<String, String> personMostSpecificLabelMap = VisualizationCaches.personToMostSpecificLabel.get(rdfService);
Map<String, Set<String>> organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService); OrganizationPeopleMap organisationToPeopleMap = VisualizationCaches.organisationToPeopleMap.get(rdfService);
Map<String, Set<String>> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication; Map<String, Set<String>> personToPublicationMap = VisualizationCaches.personToPublication.get(rdfService).personToPublication;
Map<String, String> publicationToYearMap = VisualizationCaches.publicationToYear.get(rdfService); Map<String, String> publicationToYearMap = VisualizationCaches.publicationToYear.get(rdfService);
@ -106,7 +107,7 @@ public class TemporalPublicationVisualizationRequestHandler implements
orgPublicationsPeople, orgPublicationsPeople,
subOrgPublicationsMap, subOrgPublicationsMap,
subOrgMap, subOrgMap,
organisationToPeopleMap, organisationToPeopleMap.organizationToPeople,
personToPublicationMap personToPublicationMap
); );

View file

@ -2,11 +2,21 @@
package edu.cornell.mannlib.vitro.webapp.visualization.utilities; package edu.cornell.mannlib.vitro.webapp.visualization.utilities;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.Resource;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer; import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants; import edu.cornell.mannlib.vitro.webapp.visualization.constants.QueryConstants;
import edu.cornell.mannlib.vitro.webapp.visualization.model.ConceptLabelMap;
import edu.cornell.mannlib.vitro.webapp.visualization.model.ConceptPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.model.OrganizationPeopleMap;
import edu.cornell.mannlib.vitro.webapp.visualization.model.Person;
import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions; import edu.cornell.mannlib.vitro.webapp.visualization.visutils.UtilityFunctions;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -17,6 +27,11 @@ import java.util.Set;
/** /**
* Holder for the caches we are using in the visualizations * Holder for the caches we are using in the visualizations
*
* String.intern() was a problem pre-Java 7, but has greater utility now.
* Please see the following guide for information on the implementation of String.intern()
*
* http://java-performance.info/string-intern-in-java-6-7-8/
*/ */
final public class VisualizationCaches { final public class VisualizationCaches {
// Affinity object to ensure that only one background thread can be running at once when updating the caches // Affinity object to ensure that only one background thread can be running at once when updating the caches
@ -32,6 +47,8 @@ final public class VisualizationCaches {
* @param rdfService if not null, use this service in foreground, otherwise may use the background thread * @param rdfService if not null, use this service in foreground, otherwise may use the background thread
*/ */
public static void rebuildAll(RDFService rdfService) { public static void rebuildAll(RDFService rdfService) {
conceptToLabel.build(rdfService);
conceptToPeopleMap.build(rdfService);
organizationLabels.build(rdfService); organizationLabels.build(rdfService);
organizationSubOrgs.build(rdfService); organizationSubOrgs.build(rdfService);
organizationToMostSpecificLabel.build(rdfService); organizationToMostSpecificLabel.build(rdfService);
@ -43,9 +60,12 @@ final public class VisualizationCaches {
publicationToYear.build(rdfService); publicationToYear.build(rdfService);
personToGrant.build(rdfService); personToGrant.build(rdfService);
grantToYear.build(rdfService); grantToYear.build(rdfService);
// people.build(rdfService);
} }
public static void buildMissing() { public static void buildMissing() {
if (!conceptToLabel.isCached()) { conceptToLabel.build(null); }
if (!conceptToPeopleMap.isCached()) { conceptToPeopleMap.build(null); }
if (!organizationLabels.isCached()) { organizationLabels.build(null); } if (!organizationLabels.isCached()) { organizationLabels.build(null); }
if (!organizationSubOrgs.isCached()) { organizationSubOrgs.build(null); } if (!organizationSubOrgs.isCached()) { organizationSubOrgs.build(null); }
if (!organizationToMostSpecificLabel.isCached()) { organizationToMostSpecificLabel.build(null); } if (!organizationToMostSpecificLabel.isCached()) { organizationToMostSpecificLabel.build(null); }
@ -57,6 +77,7 @@ final public class VisualizationCaches {
if (!publicationToYear.isCached()) { publicationToYear.build(null); } if (!publicationToYear.isCached()) { publicationToYear.build(null); }
if (!personToGrant.isCached()) { personToGrant.build(null); } if (!personToGrant.isCached()) { personToGrant.build(null); }
if (!grantToYear.isCached()) { grantToYear.build(null); } if (!grantToYear.isCached()) { grantToYear.build(null); }
// if (!people.isCached()) { people.build(null); }
} }
/** /**
@ -71,6 +92,139 @@ final public class VisualizationCaches {
} }
} }
/**
* Cache of people
*/
public static final CachingRDFServiceExecutor<Map<String, Person>> people =
new CachingRDFServiceExecutor<>(
new CachingRDFServiceExecutor.RDFServiceCallable<Map<String, Person>>(visualizationAffinity) {
@Override
protected Map<String, Person> callWithService(RDFService rdfService) throws Exception {
final Map<String, Person> map = new HashMap<String, Person>();
String construct = QueryConstants.getSparqlPrefixQuery() +
"CONSTRUCT {\n" +
" ?person a foaf:Person .\n" +
" ?person foaf:lastName ?lastName .\n" +
" ?person foaf:firstName ?firstName .\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:familyName ?familyName .\n" +
" ?contactName vcard:givenName ?givenName .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?contactTitleLabel .\n" +
" ?person public:thumbnailImage ?directDownloadUrl .\n" +
"} WHERE {\n" +
" { \n" +
" ?person a foaf:Person .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person foaf:lastName ?lastName .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person foaf:firstName ?firstName .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:familyName ?familyName .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" ?contactName vcard:givenName ?givenName .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?contactTitleLabel .\n" +
" } UNION { \n" +
" ?person a foaf:Person .\n" +
" ?person public:mainImage ?mainImage .\n" +
" ?mainImage public:thumbnailImage ?thumbnailImage .\n" +
" ?thumbnailImage public:downloadLocation ?downloadLocation .\n" +
" ?downloadLocation public:directDownloadUrl ?directDownloadUrl .\n" +
" } \n" +
"}\n";
Model constructedModel = ModelFactory.createDefaultModel();
rdfService.sparqlConstructQuery(construct, constructedModel);
String nameQuery = QueryConstants.getSparqlPrefixQuery() +
"SELECT ?person ?familyName ?givenName ?lastName ?firstName ?title ?thumbnailUrl\n" +
"WHERE\n" +
"{\n" +
" ?person a foaf:Person .\n" +
" OPTIONAL {\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasName ?contactName .\n" +
" OPTIONAL { ?contactName vcard:familyName ?familyName . }\n" +
" OPTIONAL { ?contactName vcard:givenName ?givenName . }\n" +
" }\n" +
" OPTIONAL {\n" +
" ?person obo:ARG_2000028 ?contactInfo .\n" +
" ?contactInfo vcard:hasTitle ?contactTitle .\n" +
" ?contactTitle vcard:title ?title .\n" +
" }\n" +
" OPTIONAL { ?person foaf:lastName ?lastName . }\n" +
" OPTIONAL { ?person foaf:firstName ?firstName . }\n" +
" OPTIONAL { ?person public:thumbnailImage ?thumbnailUrl . }\n" +
"}\n";
QueryExecution qe = QueryExecutionFactory.create(nameQuery, constructedModel);
try {
new ResultSetConsumer() {
@Override
protected void processQuerySolution(QuerySolution qs) {
String personUri = qs.getResource("person").getURI();
String familyName = null;
String givenName = null;
String thumbnailUrl = null;
String title = null;
Literal familyNameNode = qs.getLiteral("familyName");
if (familyNameNode != null) {
familyName = familyNameNode.getString();
} else {
Literal lastNameNode = qs.getLiteral("lastName");
familyName = lastNameNode == null ? null : lastNameNode.getString();
}
Literal givenNameNode = qs.getLiteral("givenName");
if (givenNameNode != null) {
givenName = givenNameNode.getString();
} else {
Literal firstNameNode = qs.getLiteral("firstName");
givenName = firstNameNode == null ? null : firstNameNode.getString();
}
Literal thumbnailUrlNode = qs.getLiteral("thumbnailUrl");
thumbnailUrl = thumbnailUrlNode == null ? null : thumbnailUrlNode.getString();
Literal titleNode = qs.getLiteral("title");
title = titleNode == null ? null : titleNode.getString();
Person person = map.get(personUri);
if (person == null) {
person = new Person();
map.put(personUri.intern(), person);
}
person.firstName = givenName == null ? null : givenName.intern();
person.lastName = familyName == null ? null : familyName.intern();
person.preferredTitle = title == null ? null : title.intern();
person.thumbnailUrl = thumbnailUrl;
}
}.processResultSet(qe.execSelect());
} finally {
qe.close();
}
return map;
}
}
);
/** /**
* Cache of organization labels (uri -> label) * Cache of organization labels (uri -> label)
*/ */
@ -95,7 +249,7 @@ final public class VisualizationCaches {
String org = qs.getResource("org").getURI(); String org = qs.getResource("org").getURI();
String orgLabel = qs.getLiteral("orgLabel").getString(); String orgLabel = qs.getLiteral("orgLabel").getString();
map.put(org, orgLabel); map.put(org.intern(), orgLabel.intern());
} }
}); });
@ -131,10 +285,10 @@ final public class VisualizationCaches {
Set<String> subOrgs = map.get(org); Set<String> subOrgs = map.get(org);
if (subOrgs == null) { if (subOrgs == null) {
subOrgs = new HashSet<String>(); subOrgs = new HashSet<String>();
subOrgs.add(subOrg); subOrgs.add(subOrg.intern());
map.put(org, subOrgs); map.put(org.intern(), subOrgs);
} else { } else {
subOrgs.add(subOrg); subOrgs.add(subOrg.intern());
} }
} }
}); });
@ -168,7 +322,7 @@ final public class VisualizationCaches {
protected void processQuerySolution(QuerySolution qs) { protected void processQuerySolution(QuerySolution qs) {
String org = qs.getResource("org").getURI(); String org = qs.getResource("org").getURI();
String typeLabel = qs.getLiteral("typeLabel").getString(); String typeLabel = qs.getLiteral("typeLabel").getString();
map.put(org, String.valueOf(typeLabel)); map.put(org.intern(), typeLabel.intern());
} }
}); });
@ -180,11 +334,11 @@ final public class VisualizationCaches {
/** /**
* Map of people within an organisation (org uri -> list of person uri) * Map of people within an organisation (org uri -> list of person uri)
*/ */
public static final CachingRDFServiceExecutor<Map<String, Set<String>>> organisationToPeopleMap = public static final CachingRDFServiceExecutor<OrganizationPeopleMap> organisationToPeopleMap =
new CachingRDFServiceExecutor<Map<String, Set<String>>>( new CachingRDFServiceExecutor<OrganizationPeopleMap>(
new CachingRDFServiceExecutor.RDFServiceCallable<Map<String, Set<String>>>(visualizationAffinity) { new CachingRDFServiceExecutor.RDFServiceCallable<OrganizationPeopleMap>(visualizationAffinity) {
@Override @Override
protected Map<String, Set<String>> callWithService(RDFService rdfService) throws Exception { protected OrganizationPeopleMap callWithService(RDFService rdfService) throws Exception {
String query = QueryConstants.getSparqlPrefixQuery() + String query = QueryConstants.getSparqlPrefixQuery() +
"SELECT ?organisation ?person\n" + "SELECT ?organisation ?person\n" +
"WHERE\n" + "WHERE\n" +
@ -196,22 +350,31 @@ final public class VisualizationCaches {
" ?person a foaf:Person .\n" + " ?person a foaf:Person .\n" +
"}\n"; "}\n";
final Map<String, Set<String>> orgToPeopleMap = new HashMap<String, Set<String>>(); final OrganizationPeopleMap orgToPeopleMap = new OrganizationPeopleMap();
rdfService.sparqlSelectQuery(query, new ResultSetConsumer() { rdfService.sparqlSelectQuery(query, new ResultSetConsumer() {
@Override @Override
protected void processQuerySolution(QuerySolution qs) { protected void processQuerySolution(QuerySolution qs) {
String org = qs.getResource("organisation").getURI(); String org = qs.getResource("organisation").getURI().intern();
String person = qs.getResource("person").getURI(); String person = qs.getResource("person").getURI().intern();
Set<String> people = orgToPeopleMap.get(org); Set<String> people = orgToPeopleMap.organizationToPeople.get(org);
if (people == null) { if (people == null) {
people = new HashSet<String>(); people = new HashSet<String>();
people.add(person); people.add(person);
orgToPeopleMap.put(org, people); orgToPeopleMap.organizationToPeople.put(org, people);
} else { } else {
people.add(person); people.add(person);
} }
Set<String> organizations = orgToPeopleMap.personToOrganizations.get(org);
if (organizations == null) {
organizations = new HashSet<String>();
organizations.add(org);
orgToPeopleMap.organizationToPeople.put(person, organizations);
} else {
organizations.add(org);
}
} }
}); });
@ -220,6 +383,103 @@ final public class VisualizationCaches {
} }
); );
/**
* Concept to label
*/
public static final CachingRDFServiceExecutor<ConceptLabelMap> conceptToLabel =
new CachingRDFServiceExecutor<>(
new CachingRDFServiceExecutor.RDFServiceCallable<ConceptLabelMap>() {
@Override
protected ConceptLabelMap callWithService(RDFService rdfService) throws Exception {
String query = QueryConstants.getSparqlPrefixQuery() +
"SELECT ?concept ?label\n" +
"WHERE\n" +
"{\n" +
" ?person a foaf:Person .\n" +
" ?person core:hasResearchArea ?concept .\n" +
" ?concept a skos:Concept .\n" +
" ?concept rdfs:label ?label .\n" +
"}\n";
// final Map<String, String> map = new HashMap<>();
final ConceptLabelMap map = new ConceptLabelMap();
rdfService.sparqlSelectQuery(query, new ResultSetConsumer() {
@Override
protected void processQuerySolution(QuerySolution qs) {
String conceptURI = qs.getResource("concept").getURI().intern();
String label = qs.getLiteral("label").getString().intern();
String labelLower = label.toLowerCase().intern();
map.conceptToLabel.put(conceptURI, label);
Set<String> conceptSet = map.lowerLabelToConcepts.get(labelLower);
if (conceptSet == null) {
conceptSet = new HashSet<String>();
conceptSet.add(conceptURI);
map.lowerLabelToConcepts.put(labelLower, conceptSet);
} else {
conceptSet.add(conceptURI);
}
}
});
return map;
}
}
);
/**
* Map of people associated with a concept
*/
public static final CachingRDFServiceExecutor<ConceptPeopleMap> conceptToPeopleMap =
new CachingRDFServiceExecutor<ConceptPeopleMap>(
new CachingRDFServiceExecutor.RDFServiceCallable<ConceptPeopleMap>(visualizationAffinity) {
@Override
protected ConceptPeopleMap callWithService(RDFService rdfService) throws Exception {
String query = QueryConstants.getSparqlPrefixQuery() +
"SELECT ?person ?concept\n" +
"WHERE\n" +
"{\n" +
" ?person a foaf:Person .\n" +
" ?person core:hasResearchArea ?concept .\n" +
" ?concept a skos:Concept .\n" +
"}\n";
final ConceptPeopleMap conceptPeopleMap = new ConceptPeopleMap();
rdfService.sparqlSelectQuery(query, new ResultSetConsumer() {
@Override
protected void processQuerySolution(QuerySolution qs) {
String concept = qs.getResource("concept").getURI().intern();
String person = qs.getResource("person").getURI().intern();
Set<String> people = conceptPeopleMap.conceptToPeople.get(concept);
if (people == null) {
people = new HashSet<String>();
people.add(person);
conceptPeopleMap.conceptToPeople.put(concept, people);
} else {
people.add(person);
}
Set<String> concepts = conceptPeopleMap.personToConcepts.get(person);
if (concepts == null) {
concepts = new HashSet<String>();
concepts.add(concept);
conceptPeopleMap.personToConcepts.put(person, concepts);
} else {
concepts.add(concept);
}
}
});
return conceptPeopleMap;
}
}
);
/** /**
* Display labels for people (uri -> label) * Display labels for people (uri -> label)
*/ */
@ -244,7 +504,7 @@ final public class VisualizationCaches {
String person = qs.getResource("person").getURI(); String person = qs.getResource("person").getURI();
String personLabel = qs.getLiteral("personLabel").getString(); String personLabel = qs.getLiteral("personLabel").getString();
map.put(person, personLabel); map.put(person.intern(), personLabel.intern());
} }
}); });
@ -277,7 +537,7 @@ final public class VisualizationCaches {
protected void processQuerySolution(QuerySolution qs) { protected void processQuerySolution(QuerySolution qs) {
String person = qs.getResource("person").getURI(); String person = qs.getResource("person").getURI();
String typeLabel = qs.getLiteral("typeLabel").getString(); String typeLabel = qs.getLiteral("typeLabel").getString();
map.put(person, String.valueOf(typeLabel)); map.put(person.intern(), String.valueOf(typeLabel).intern());
} }
}); });
@ -317,7 +577,7 @@ final public class VisualizationCaches {
String personURI = person.getURI(); String personURI = person.getURI();
String documentURI = document.getURI(); String documentURI = document.getURI();
map.put(personURI, documentURI); map.put(personURI.intern(), documentURI.intern());
} }
} }
}); });
@ -352,7 +612,7 @@ final public class VisualizationCaches {
String document = qs.getResource("document").getURI(); String document = qs.getResource("document").getURI();
String journalLabel = qs.getLiteral("journalLabel").getString(); String journalLabel = qs.getLiteral("journalLabel").getString();
map.put(document, journalLabel); map.put(document.intern(), journalLabel.intern());
} }
}); });
@ -390,7 +650,7 @@ final public class VisualizationCaches {
.getValidParsedDateTimeObject(pubDate); .getValidParsedDateTimeObject(pubDate);
if (validParsedDateTimeObject != null) { if (validParsedDateTimeObject != null) {
map.put(document, String.valueOf(validParsedDateTimeObject.getYear())); map.put(document.intern(), String.valueOf(validParsedDateTimeObject.getYear()).intern());
} }
} }
} }
@ -434,10 +694,10 @@ final public class VisualizationCaches {
Set<String> documents = map.get(personURI); Set<String> documents = map.get(personURI);
if (documents == null) { if (documents == null) {
documents = new HashSet<String>(); documents = new HashSet<String>();
documents.add(grant.getURI()); documents.add(grant.getURI().intern());
map.put(personURI, documents); map.put(personURI.intern(), documents);
} else { } else {
documents.add(grant.getURI()); documents.add(grant.getURI().intern());
} }
} }
} }
@ -478,7 +738,7 @@ final public class VisualizationCaches {
.getValidParsedDateTimeObject(startDate); .getValidParsedDateTimeObject(startDate);
if (validParsedDateTimeObject != null) { if (validParsedDateTimeObject != null) {
map.put(grant, String.valueOf(validParsedDateTimeObject.getYear())); map.put(grant.intern(), String.valueOf(validParsedDateTimeObject.getYear()).intern());
} }
} }
} }
@ -520,7 +780,7 @@ final public class VisualizationCaches {
.getValidParsedDateTimeObject(startDate); .getValidParsedDateTimeObject(startDate);
if (validParsedDateTimeObject != null) { if (validParsedDateTimeObject != null) {
map.put(grant, String.valueOf(validParsedDateTimeObject.getYear())); map.put(grant.intern(), String.valueOf(validParsedDateTimeObject.getYear()).intern());
} }
} }
} }

View file

@ -19,7 +19,8 @@ display:DefaultMenu
display:hasElement display:HomeMenuItem ; display:hasElement display:HomeMenuItem ;
display:hasElement display:OrganizationsMenuItem ; display:hasElement display:OrganizationsMenuItem ;
display:hasElement display:PeopleMenuItem ; display:hasElement display:PeopleMenuItem ;
display:hasElement display:ResearchMenuItem . display:hasElement display:ResearchMenuItem ;
display:hasElement display:CapabilityMapMenuItem .
#### Menu Items for Default Menu #### #### Menu Items for Default Menu ####
@ -53,6 +54,12 @@ display:EventsMenuItem
display:linkText "Events"; display:linkText "Events";
display:toPage display:Events . display:toPage display:Events .
display:CapabilityMapMenuItem
a display:NavigationElement ;
display:menuPosition 6;
display:linkText "Capability Map";
display:toPage display:CapabilityMap .
display:Home display:Home
a display:HomePage ; a display:HomePage ;
a display:Page ; a display:Page ;
@ -93,7 +100,11 @@ display:Research
display:urlMapping "/research" ; display:urlMapping "/research" ;
display:hasDataGetter display:researchDataGetter . display:hasDataGetter display:researchDataGetter .
display:CapabilityMap
a display:Page ;
display:title "Capability Map" ;
display:urlMapping "/vis/capabilitymap" ;
display:hasDataGetter display:capabilityMapDataGetter .
#The data getter objects used above #The data getter objects used above
display:peopleDataGetter display:peopleDataGetter
@ -118,3 +129,8 @@ display:eventsDataGetter
display:homeDataGetter display:homeDataGetter
a <java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.BrowseDataGetter> . a <java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.BrowseDataGetter> .
display:capabilityMapDataGetter
a <java:edu.cornell.mannlib.vitro.webapp.utils.dataGetter.FixedHTMLDataGetter>;
display:htmlValue """Capability Map"""@en ;
display:saveToVar "capabilityMap" .

View file

@ -9,6 +9,8 @@
http://www.springframework.org/schema/context http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"> http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="capability_map"
class="edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap.CapabilityMapRequestHandler" />
<bean id="person_pub_count" <bean id="person_pub_count"
class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.PersonPublicationCountRequestHandler" /> class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.PersonPublicationCountRequestHandler" />
@ -47,6 +49,10 @@
class="edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationInjector"> class="edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationInjector">
<property name="visualizations"> <property name="visualizations">
<map> <map>
<entry key="capabilitymap">
<ref bean="capability_map"></ref>
</entry>
<entry key="person_pub_count"> <entry key="person_pub_count">
<ref bean="person_pub_count"></ref> <ref bean="person_pub_count"></ref>
</entry> </entry>

View file

@ -0,0 +1,355 @@
body {
background-position-y:60px;
}
.text {
margin: 7px;
}
.alert {
color: red;
font-size: large;
}
#queryform button, #queryform input[type=submit] {
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-o-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-webkit-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-moz-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-o-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
background-color: #8BAB2E;
// background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#446791), to(#164682));
// background-image: -moz-linear-gradient(top, #446791, #164682);
width: auto;
padding: 9px 18px 8px;
// margin: 10px 10px 0 0;
color: #fff;
border: 0;
vertical-align: middle
}
#content {
width: 1200px;
max-width: 1200px;
}
/* Contains all the graphics. */
#container {
/*width: 1200px;*/width:940px;
height: auto;
/* margin:0 auto; */
/*margin-left:-130px;*/
position:relative;
/*box-shadow:0px 0px 20px -6px #000000;*/
margin-top:15px;
}
#left-container,
#right-container,
#center-container {
height:600px;
position:absolute;
top:0;
}
ul#log_printout {
display: block;
height: 330px;
overflow: scroll
}
#left-container, #right-container {
width: 300px;
color:#686c70;
text-align: left;
/*overflow: auto;*/
background-color:#fff;
background-repeat:no-repeat;
/*border-bottom:1px solid #ddd;*/
}
.result_body {
overflow:auto;
height:570px;
}
#left-container {
left:0;
}
#right-container {
right:0;
border-left:1px solid #ddd;
}
#right-container h4{
text-indent:8px;
}
#center-container {
width: 900px;
background-color:#FFFFFF;
color:#ccc;
}
.result_section {
/* width: 100%;*/
font-size: 12px;
list-style:none;
margin:7px;
}
#log {
position: absolute;
top: 10px;
right:10px;
font-size:1.0em;
}
#infovis {
width:100%;
height:600px;
/*margin:auto;*/
overflow:hidden;
}
span.close {
color:#FF5555;
cursor:pointer;
font-weight:bold;
margin-left:3px;
}
span.name {
cursor: pointer;
}
/*TOOLTIPS*/
.tip {
color: #111;
width: 139px;
background-color: white;
border:1px solid #ccc;
-moz-box-shadow:#555 2px 2px 8px;
-webkit-box-shadow:#555 2px 2px 8px;
-o-box-shadow:#555 2px 2px 8px;
box-shadow:#555 2px 2px 8px;
opacity:0.9;
filter:alpha(opacity=90);
font-size:10px;
font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;
padding:7px;
text-align: left;
width:auto;
max-width:500px;
}
/* LIST OF EXPERTS */
.field {
font-style: italic;
margin: .4ex 1ex;
}
#graphDetails {
margin-bottom:0px;
margin-top:5px;
}
input[disabled] {
opacity:0.5;
box-shadow:none;
}
#resetButton {
background-color: #933;
background-image: -webkit-gradient(linear,0 0,0 100%,from(#B55),to(#933));
background-image: -moz-linear-gradient(center top , #B55, #933);
}
#helptext {
width:630px;
/*padding:5px;*/
}
#center-container {
display:none;
}
.bar {
height:15px;
display:block;
background-color:#EEE;
border:1px solid #CCC;
float:left;
margin-bottom:1px;
}
.barchart {
line-height:18px;
padding-top:5px;
}
h2 {
font-size:23px;
margin-top:0px;
padding-top:0px;
line-height:1.3em;
margin-bottom:20px;
}
.result_section h2 {
font-size:16px;
padding:10px 0px;
margin:0px;
}
.result_section ul li {
margin-top:3px;
}
li input[type=checkbox] {
vertical-align:middle;
}
.orange-square, .blue-circle {
display:block;
float:left;
height:15px;
width:15px;
margin-right:3px;
}
.orange-square {
background-color:#ffa500;
}
.blue-circle {
background-color:#23A4FF;
border-radius:7px;
}
ul.visible {
margin-bottom:1.3em;
margin-left:20px;
list-style-type:disc;
}
#cutofflabel {
display:inline-block;
width:0px;
overflow:visible;
position:relative;
left:10px;
top:2px;
foont-size: 14px;
}
#queryCutoff {
background:transparent;
text-align:right;
padding-left:40px;
}
#cutofflabel img {
vertical-align:middle;
position:relative;
top:-2px;
}
.progress, .progress div {
height:10px;
margin-bottom:-10px;
}
.progress {
position:absolute;
top:0px;
left:0px;
width:100%;
}
.progress div {
background-color:#9999FF;
width:0%;
white-space:nowrap;
color:#FFF;
font-size:10px;
line-height:10px;
}
hr {
background:#efefef;
}
.tabs {
width: auto;
margin-bottom: 15px;
border: 0;
z-index: 10;
}
.tabs ul.titles li {
background: transparent;
/*padding-right: 1px;*/
line-height: normal;
display:inline;
}
.tabs ul {
padding-left:0px;
}
.tabs ul.titles {
list-style: none;
height:30px;
color: #5e6363;
font-size: 13px;
}
.tabs ul.titles li a {
text-decoration: none;
background-color: #eeeeee;
outline: none;
display:block;
float:left;
// width:100px;
width:150px;
height:30px;
line-height:30px;
text-align:center;
color: #5e6363;
}
.tabs ul.titles li a:hover {
background-color: #dddddd;
color: #002b44;
}
.tabs ul.titles li.activeTab a,
.tabs ul.titles li.activeTab a:hover {
background-color: #fff;
}
.smalllogo {
margin-left:-5px;
vertical-align:middle;
}
.advanced label, input {
display:inline;
}
.advanced input[type=text] {
width:50px;
}
.advanced {
display:none;
}
.person_details {
margin-top:5px;
}
.person_details p {
margin-bottom:0px;
}
.person_details ul {
list-style:disc;
margin-left:20px;
}
.person_details li {
margin:0px;
margin-bottom:3px;
}
.ack {
padding-top:20px;
/*text-align:center;*/
color:#999;
margin:0px 20px;
/*font-size:8pt;*/
}
footer {
margin:0px 20px;
width:auto;
}
footer p.copyright {
margin-left:0px;
padding-left:0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,663 @@
/*!
* jQuery Color Animations v@VERSION
* https://github.com/jquery/jquery-color
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* Date: @DATE
*/
(function( jQuery, undefined ) {
var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
// plusequals test for += 100 -= 100
rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
// a set of RE's that can match strings and generate color tuples.
stringParsers = [{
re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ],
execResult[ 3 ],
execResult[ 4 ]
];
}
}, {
re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ] * 2.55,
execResult[ 2 ] * 2.55,
execResult[ 3 ] * 2.55,
execResult[ 4 ]
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ], 16 )
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
];
}
}, {
re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
space: "hsla",
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ] / 100,
execResult[ 3 ] / 100,
execResult[ 4 ]
];
}
}],
// jQuery.Color( )
color = jQuery.Color = function( color, green, blue, alpha ) {
return new jQuery.Color.fn.parse( color, green, blue, alpha );
},
spaces = {
rgba: {
props: {
red: {
idx: 0,
type: "byte"
},
green: {
idx: 1,
type: "byte"
},
blue: {
idx: 2,
type: "byte"
}
}
},
hsla: {
props: {
hue: {
idx: 0,
type: "degrees"
},
saturation: {
idx: 1,
type: "percent"
},
lightness: {
idx: 2,
type: "percent"
}
}
}
},
propTypes = {
"byte": {
floor: true,
max: 255
},
"percent": {
max: 1
},
"degrees": {
mod: 360,
floor: true
}
},
support = color.support = {},
// element for support tests
supportElem = jQuery( "<p>" )[ 0 ],
// colors = jQuery.Color.names
colors,
// local aliases of functions called often
each = jQuery.each;
// determine rgba support immediately
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
// define cache name and alpha properties
// for rgba and hsla spaces
each( spaces, function( spaceName, space ) {
space.cache = "_" + spaceName;
space.props.alpha = {
idx: 3,
type: "percent",
def: 1
};
});
function clamp( value, prop, allowEmpty ) {
var type = propTypes[ prop.type ] || {};
if ( value == null ) {
return (allowEmpty || !prop.def) ? null : prop.def;
}
// ~~ is an short way of doing floor for positive numbers
value = type.floor ? ~~value : parseFloat( value );
// IE will pass in empty strings as value for alpha,
// which will hit this case
if ( isNaN( value ) ) {
return prop.def;
}
if ( type.mod ) {
// we add mod before modding to make sure that negatives values
// get converted properly: -10 -> 350
return (value + type.mod) % type.mod;
}
// for now all property types without mod have min and max
return 0 > value ? 0 : type.max < value ? type.max : value;
}
function stringParse( string ) {
var inst = color(),
rgba = inst._rgba = [];
string = string.toLowerCase();
each( stringParsers, function( i, parser ) {
var parsed,
match = parser.re.exec( string ),
values = match && parser.parse( match ),
spaceName = parser.space || "rgba";
if ( values ) {
parsed = inst[ spaceName ]( values );
// if this was an rgba parse the assignment might happen twice
// oh well....
inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
rgba = inst._rgba = parsed._rgba;
// exit each( stringParsers ) here because we matched
return false;
}
});
// Found a stringParser that handled it
if ( rgba.length ) {
// if this came from a parsed string, force "transparent" when alpha is 0
// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
if ( rgba.join() === "0,0,0,0" ) {
jQuery.extend( rgba, colors.transparent );
}
return inst;
}
// named colors
return colors[ string ];
}
color.fn = jQuery.extend( color.prototype, {
parse: function( red, green, blue, alpha ) {
if ( red === undefined ) {
this._rgba = [ null, null, null, null ];
return this;
}
if ( red.jquery || red.nodeType ) {
red = jQuery( red ).css( green );
green = undefined;
}
var inst = this,
type = jQuery.type( red ),
rgba = this._rgba = [];
// more than 1 argument specified - assume ( red, green, blue, alpha )
if ( green !== undefined ) {
red = [ red, green, blue, alpha ];
type = "array";
}
if ( type === "string" ) {
return this.parse( stringParse( red ) || colors._default );
}
if ( type === "array" ) {
each( spaces.rgba.props, function( key, prop ) {
rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
});
return this;
}
if ( type === "object" ) {
if ( red instanceof color ) {
each( spaces, function( spaceName, space ) {
if ( red[ space.cache ] ) {
inst[ space.cache ] = red[ space.cache ].slice();
}
});
} else {
each( spaces, function( spaceName, space ) {
var cache = space.cache;
each( space.props, function( key, prop ) {
// if the cache doesn't exist, and we know how to convert
if ( !inst[ cache ] && space.to ) {
// if the value was null, we don't need to copy it
// if the key was alpha, we don't need to copy it either
if ( key === "alpha" || red[ key ] == null ) {
return;
}
inst[ cache ] = space.to( inst._rgba );
}
// this is the only case where we allow nulls for ALL properties.
// call clamp with alwaysAllowEmpty
inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
});
// everything defined but alpha?
if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
// use the default of 1
inst[ cache ][ 3 ] = 1;
if ( space.from ) {
inst._rgba = space.from( inst[ cache ] );
}
}
});
}
return this;
}
},
is: function( compare ) {
var is = color( compare ),
same = true,
inst = this;
each( spaces, function( _, space ) {
var localCache,
isCache = is[ space.cache ];
if (isCache) {
localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
each( space.props, function( _, prop ) {
if ( isCache[ prop.idx ] != null ) {
same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
return same;
}
});
}
return same;
});
return same;
},
_space: function() {
var used = [],
inst = this;
each( spaces, function( spaceName, space ) {
if ( inst[ space.cache ] ) {
used.push( spaceName );
}
});
return used.pop();
},
transition: function( other, distance ) {
var end = color( other ),
spaceName = end._space(),
space = spaces[ spaceName ],
startColor = this.alpha() === 0 ? color( "transparent" ) : this,
start = startColor[ space.cache ] || space.to( startColor._rgba ),
result = start.slice();
end = end[ space.cache ];
each( space.props, function( key, prop ) {
var index = prop.idx,
startValue = start[ index ],
endValue = end[ index ],
type = propTypes[ prop.type ] || {};
// if null, don't override start value
if ( endValue === null ) {
return;
}
// if null - use end
if ( startValue === null ) {
result[ index ] = endValue;
} else {
if ( type.mod ) {
if ( endValue - startValue > type.mod / 2 ) {
startValue += type.mod;
} else if ( startValue - endValue > type.mod / 2 ) {
startValue -= type.mod;
}
}
result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
}
});
return this[ spaceName ]( result );
},
blend: function( opaque ) {
// if we are already opaque - return ourself
if ( this._rgba[ 3 ] === 1 ) {
return this;
}
var rgb = this._rgba.slice(),
a = rgb.pop(),
blend = color( opaque )._rgba;
return color( jQuery.map( rgb, function( v, i ) {
return ( 1 - a ) * blend[ i ] + a * v;
}));
},
toRgbaString: function() {
var prefix = "rgba(",
rgba = jQuery.map( this._rgba, function( v, i ) {
return v == null ? ( i > 2 ? 1 : 0 ) : v;
});
if ( rgba[ 3 ] === 1 ) {
rgba.pop();
prefix = "rgb(";
}
return prefix + rgba.join() + ")";
},
toHslaString: function() {
var prefix = "hsla(",
hsla = jQuery.map( this.hsla(), function( v, i ) {
if ( v == null ) {
v = i > 2 ? 1 : 0;
}
// catch 1 and 2
if ( i && i < 3 ) {
v = Math.round( v * 100 ) + "%";
}
return v;
});
if ( hsla[ 3 ] === 1 ) {
hsla.pop();
prefix = "hsl(";
}
return prefix + hsla.join() + ")";
},
toHexString: function( includeAlpha ) {
var rgba = this._rgba.slice(),
alpha = rgba.pop();
if ( includeAlpha ) {
rgba.push( ~~( alpha * 255 ) );
}
return "#" + jQuery.map( rgba, function( v ) {
// default to 0 when nulls exist
v = ( v || 0 ).toString( 16 );
return v.length === 1 ? "0" + v : v;
}).join("");
},
toString: function() {
return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
}
});
color.fn.parse.prototype = color.fn;
// hsla conversions adapted from:
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
function hue2rgb( p, q, h ) {
h = ( h + 1 ) % 1;
if ( h * 6 < 1 ) {
return p + (q - p) * h * 6;
}
if ( h * 2 < 1) {
return q;
}
if ( h * 3 < 2 ) {
return p + (q - p) * ((2/3) - h) * 6;
}
return p;
}
spaces.hsla.to = function ( rgba ) {
if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
return [ null, null, null, rgba[ 3 ] ];
}
var r = rgba[ 0 ] / 255,
g = rgba[ 1 ] / 255,
b = rgba[ 2 ] / 255,
a = rgba[ 3 ],
max = Math.max( r, g, b ),
min = Math.min( r, g, b ),
diff = max - min,
add = max + min,
l = add * 0.5,
h, s;
if ( min === max ) {
h = 0;
} else if ( r === max ) {
h = ( 60 * ( g - b ) / diff ) + 360;
} else if ( g === max ) {
h = ( 60 * ( b - r ) / diff ) + 120;
} else {
h = ( 60 * ( r - g ) / diff ) + 240;
}
// chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
if ( diff === 0 ) {
s = 0;
} else if ( l <= 0.5 ) {
s = diff / add;
} else {
s = diff / ( 2 - add );
}
return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
};
spaces.hsla.from = function ( hsla ) {
if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
return [ null, null, null, hsla[ 3 ] ];
}
var h = hsla[ 0 ] / 360,
s = hsla[ 1 ],
l = hsla[ 2 ],
a = hsla[ 3 ],
q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
p = 2 * l - q;
return [
Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
Math.round( hue2rgb( p, q, h ) * 255 ),
Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
a
];
};
each( spaces, function( spaceName, space ) {
var props = space.props,
cache = space.cache,
to = space.to,
from = space.from;
// makes rgba() and hsla()
color.fn[ spaceName ] = function( value ) {
// generate a cache for this space if it doesn't exist
if ( to && !this[ cache ] ) {
this[ cache ] = to( this._rgba );
}
if ( value === undefined ) {
return this[ cache ].slice();
}
var ret,
type = jQuery.type( value ),
arr = ( type === "array" || type === "object" ) ? value : arguments,
local = this[ cache ].slice();
each( props, function( key, prop ) {
var val = arr[ type === "object" ? key : prop.idx ];
if ( val == null ) {
val = local[ prop.idx ];
}
local[ prop.idx ] = clamp( val, prop );
});
if ( from ) {
ret = color( from( local ) );
ret[ cache ] = local;
return ret;
} else {
return color( local );
}
};
// makes red() green() blue() alpha() hue() saturation() lightness()
each( props, function( key, prop ) {
// alpha is included in more than one space
if ( color.fn[ key ] ) {
return;
}
color.fn[ key ] = function( value ) {
var vtype = jQuery.type( value ),
fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
local = this[ fn ](),
cur = local[ prop.idx ],
match;
if ( vtype === "undefined" ) {
return cur;
}
if ( vtype === "function" ) {
value = value.call( this, cur );
vtype = jQuery.type( value );
}
if ( value == null && prop.empty ) {
return this;
}
if ( vtype === "string" ) {
match = rplusequals.exec( value );
if ( match ) {
value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
}
}
local[ prop.idx ] = value;
return this[ fn ]( local );
};
});
});
// add cssHook and .fx.step function for each named hook.
// accept a space separated string of properties
color.hook = function( hook ) {
var hooks = hook.split( " " );
each( hooks, function( i, hook ) {
jQuery.cssHooks[ hook ] = {
set: function( elem, value ) {
var parsed, curElem,
backgroundColor = "";
if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
value = color( parsed || value );
if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
curElem = hook === "backgroundColor" ? elem.parentNode : elem;
while (
(backgroundColor === "" || backgroundColor === "transparent") &&
curElem && curElem.style
) {
try {
backgroundColor = jQuery.css( curElem, "backgroundColor" );
curElem = curElem.parentNode;
} catch ( e ) {
}
}
value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
backgroundColor :
"_default" );
}
value = value.toRgbaString();
}
try {
elem.style[ hook ] = value;
} catch( e ) {
// wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
}
}
};
jQuery.fx.step[ hook ] = function( fx ) {
if ( !fx.colorInit ) {
fx.start = color( fx.elem, hook );
fx.end = color( fx.end );
fx.colorInit = true;
}
jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
};
});
};
color.hook( stepHooks );
jQuery.cssHooks.borderColor = {
expand: function( value ) {
var expanded = {};
each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
expanded[ "border" + part + "Color" ] = value;
});
return expanded;
}
};
// Basic color names only.
// Usage of any of the other color names requires adding yourself or including
// jquery.color.svg-names.js.
colors = jQuery.Color.names = {
// 4.1. Basic color keywords
aqua: "#00ffff",
black: "#000000",
blue: "#0000ff",
fuchsia: "#ff00ff",
gray: "#808080",
green: "#008000",
lime: "#00ff00",
maroon: "#800000",
navy: "#000080",
olive: "#808000",
purple: "#800080",
red: "#ff0000",
silver: "#c0c0c0",
teal: "#008080",
white: "#ffffff",
yellow: "#ffff00",
// 4.2.3. "transparent" color keyword
transparent: [ null, null, null, 0 ],
_default: "#ffffff"
};
})( jQuery );

View file

@ -0,0 +1,82 @@
// jsr_class.js
//
// JSONscriptRequest -- a simple class for making HTTP requests
// using dynamically generated script tags and JSON
//
// Author: Jason Levitt
// Date: December 7th, 2005
//
// A SECURITY WARNING FROM DOUGLAS CROCKFORD:
// "The dynamic <script> tag hack suffers from a problem. It allows a page
// to access data from any server in the web, which is really useful.
// Unfortunately, the data is returned in the form of a script. That script
// can deliver the data, but it runs with the same authority as scripts on
// the base page, so it is able to steal cookies or misuse the authorization
// of the user with the server. A rogue script can do destructive things to
// the relationship between the user and the base server."
//
// So, be extremely cautious in your use of this script.
//
//
// Sample Usage:
//
// <script type="text/javascript" src="jsr_class.js"></script>
//
// function callbackfunc(jsonData) {
// alert('Latitude = ' + jsonData.ResultSet.Result[0].Latitude +
// ' Longitude = ' + jsonData.ResultSet.Result[0].Longitude);
// aObj.removeScriptTag();
// }
//
// request = 'http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&
// output=json&callback=callbackfunc&location=78704';
// aObj = new JSONscriptRequest(request);
// aObj.buildScriptTag();
// aObj.addScriptTag();
//
//
// Constructor -- pass a REST request URL to the constructor
//
function JSONscriptRequest(fullUrl) {
// REST request path
this.fullUrl = fullUrl;
// Keep IE from caching requests
this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
// Get the DOM location to put the script tag
this.headLoc = document.getElementsByTagName("head").item(0);
// Generate a unique script tag id
this.scriptId = 'JscriptId' + JSONscriptRequest.scriptCounter++;
}
// Static script ID counter
JSONscriptRequest.scriptCounter = 1;
// buildScriptTag method
//
JSONscriptRequest.prototype.buildScriptTag = function () {
// Create the script tag
this.scriptObj = document.createElement("script");
// Add script object attributes
this.scriptObj.setAttribute("type", "text/javascript");
this.scriptObj.setAttribute("charset", "utf-8");
this.scriptObj.setAttribute("src", this.fullUrl + this.noCacheIE);
this.scriptObj.setAttribute("id", this.scriptId);
};
// removeScriptTag method
//
JSONscriptRequest.prototype.removeScriptTag = function () {
// Destroy the script tag
this.headLoc.removeChild(this.scriptObj);
};
// addScriptTag method
//
JSONscriptRequest.prototype.addScriptTag = function () {
// Create the script tag
this.headLoc.appendChild(this.scriptObj);
};

View file

@ -0,0 +1,167 @@
${scripts.add(
'<script type="text/javascript" src="${urls.base}/js/d3.min.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/jquery.color.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/jsr_class.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/graph_new.js"></script>'
)}
${stylesheets.add(
'<link rel="stylesheet" href="${urls.base}/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/templates/freemarker/edit/forms/css/autocomplete.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/personlevel/page.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/visualization.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/capabilitymap/graph.css" />'
)}
<script language="JavaScript" type="text/javascript">
var contextPath = "${urls.base}";
$(document).ready(function() {
var loadedConcepts = $.ajax({
url: contextPath + "/visualizationAjax?vis=capabilitymap&data=concepts",
type: "GET",
async: false,
success: function(result) {
return result;
}
});
var conceptArray = $.parseJSON(loadedConcepts.responseText);
$("#query").autocomplete({
source: conceptArray
});
});
</script>
<div class="main" id="main-content" role="main">
<div class="col-8">
<h2>Capability Map</h2>
<p>Build a &lsquo;first pass&rsquo; capability map by typing in a<!-- set of--> search term<!--s--> that could be said to represent a broad research capability.</p>
</div>
<div id="queryform">
<p>
<span>
<input name="query" id="query" size="34" value="" onfocus="" accesskey="q" onblur="" type="text" onkeydown="queryKeyDown(event);">
<label id="cutofflabel" for="queryCutoff">Cutoff:</label>
<input id="queryCutoff" name="queryCutoff" type="text" title="Cutoff" size="4" value="10">
<input type="submit" value="Search" id="add" type="button" onclick="addKwd();">
<input value="Search and Expand" type="submit" id="sExpand" onclick="expandLastQuery = 1; addKwd();">
<input value="Reset" id="resetButton" type="submit" onclick="reset()" disabled>
<!-- a style="display:inline-block; float:right; line-height:32px; height:32px; cursor:pointer" onclick="showhideadvanced(this)">Show advanced</a -->
</span>
</p>
</div>
<hr style="clear:both;">
<div id="container">
<div id="helptext">
<p>
Welcome to the Capability Mapping tool.
This tool visualises how researchers relate to other
researchers via search terms.
</p>
<h3>Getting Started</h3>
<p>
Enter a research area into the search field above and press Search.
The resulting diagram displays the search term, rendered in orange,
connected to the blue group of researchers that are active in that area.
Enter another search term to see how researchers from both searches relate.
Keep adding search terms to build a capability map.
</p>
<p>
Tip: you can expand a broad search term into smaller concepts
by clicking &lsquo;search and expand&rsquo;.
</p>
<h3>Interacting with the visualisation</h3>
<p>
By clicking on any node in the visualisation,
additional information can be viewed in the
Info tab on the right-hand side.
For groups of people, the participants in the group
and their information can be viewed,
and individual researchers can be removed from the graph.
Selecting a search term will display all attached groups.
Under each group full information for each person is retrieved,
and the number of matching grants and publications
for each researcher within the mapped capabilities is shown.
Clicking on a researcher's name will lead to the original search
results.
</p>
<h4>Visual cues</h4>
<p>
To make the visualisation easier to read,
search terms and groups are scaled according
to the number of results returned.
Groups are also given different shades
according to the number of connected search terms.
The darker the shade, the more search terms a group is connected to.
</p>
<h3>Advanced features</h3>
<h4>Changing the cutoff value</h4>
<p>
The amount of researchers retrieved for each search term
for is limited by the cutoff value in the search form
(10 by default).
Increasing this cutoff will increase the likelihood
of an intersection between different search terms.
This will also increase the complexity of the graph,
however, and may make it difficult to identify patterns.
</p>
</div>
<div id="center-container">
<div id="log"></div>
<div id="infovis"></div>
<div class="progress"><div id="progressbar"></div></div>
</div>
<div id="right-container">
<div class="tabs">
<ul class="titles">
<li><a href="#demo">Search terms</a></li>
<li><a href="#logg">Info</a></li>
<!-- li><a href="#extractData">Data</a></li -->
</ul>
<div class="result_body">
<div class="result_section" id="demo">
<h2>Current search terms</h2>
<ul id="log_printout">
<li>This panel displays a list of the search terms currently
on the graph. Search for something to begin.</li>
</ul>
<p style="position:absolute; bottom:10px">
<img src="${urls.base}/images/visualization/capabilitymap/key.png" alt="Key">
</p>
</div>
<div class="result_section" id="logg">
<div id="inner-details">
<p>
This panel displays information about individual
search terms and groups. Click on a group to display
its information.
</p>
</div>
</div>
<!-- div class="result_section" id="extractData">
<h2>Extract Data</h2>
<p>
Import:
<button disabled>SVG</button>
<button onclick="importGraphDetails();">JSON</button>
<br>
Export:
<button onclick="generateGraphSVG();">SVG</button>
<button onclick="download(g.export(), 'json')">JSON</button>
<button onclick="download(g.toDOT(), 'gv')">DOT</button>
<button onclick="generateGraphPersonList();">MRW</button>
</p>
<textarea id="graphDetails" style="width:99%; height:450px; border:1px solid #EEE; padding:0px;"></textarea>
</div -->
</div>
</div>
</div>
</div>
</div>