Commits (23)
This source diff could not be displayed because it is too large. You can view the blob instead.
window.nomadEnv = {
host: "https://labdev-nomad.esc.rzg.mpg.de/dev/nomad/encyclopedia-api/",
path: "api/encyclopedia/",
userCookieDomain: ".esc.rzg.mpg.de",
//host: "/",
//path: "",
//userCookieDomain: ".localhost",
//apiRoot: "https://labdev-nomad.esc.rzg.mpg.de/dev/nomad/encyclopedia-api/",
//guiRoot: "https://labdev-nomad.esc.rzg.mpg.de/dev/nomad/encyclopedia-api/",
//userCookieDomain: ".esc.rzg.mpg.de",
//userCookieDomain: ".nomad-lab.eu",
//apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/",
//guiRoot: "https://nomad-lab.eu/prod/rae/encyclopedia/",
guiRoot: "http://localhost:3000/gui/",
apiRoot: "http://localhost:3000/",
userCookieDomain: ".localhost",
guestUserToken: 'eyJhbGciOiJIUzI1NiIsImlhdCI6MTUyMzg4MDE1OSwiZXhwIjoxNjgxNTYwMTU5fQ.ey'+
'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4'
'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4',
keycloakBase: 'https://nomad-lab.eu/fairdi/keycloak/auth/',
keycloakRealm: 'fairdi_nomad_test',
keycloakClientId: 'nomad_gui_dev'
};
......@@ -187,8 +187,27 @@ body { font-family: 'Arimo', sans-serif;
header{ background-color: #23356d; }
#logo-header {
display: flex;
}
#auth-panel {
width: 25%;
text-align: right;
padding-top:5px;
}
#logo-panel {
width: 25%;
text-align: right;
padding-top:5px;
}
#spacer {
flex-grow: 1;
}
div#logo-header{
div#logo-header {
width: 1200px; margin: 0 auto;
/* padding-top: 10px;padding-bottom: 56px;*/
padding: 3px 0;
......
......@@ -11,6 +11,7 @@
<link rel="stylesheet" href="css/styles.css"/>
<script defer src="conf.js"></script>
<script defer src="keycloak.min.js"></script>
<script defer src="lib/3d-viewers/three.min.js"></script>
<script defer src="lib/3d-viewers/orthographiccontrols.js"></script>
<script defer src="lib/3d-viewers/matviewer.min.js"></script>
......@@ -49,50 +50,29 @@
<header>
<div id="logo-header" >
<div style="float: left; width: 20%;padding-top:9px">
<!--<a href="#/search/new"> NOMAD Encyclopedia</a>-->
<a href="https://nomad-coe.eu/index.php?page=materials-encyclopedia" target="_blank">
Introduction to NOMAD Encyclopedia
<div "logo-panel">
<a href="https://nomad-lab.eu/" target="_blank">
<img src="img/nomad-logo.png" style="margin-right: 1rem">
The NOMAD Laboratory
</a>
</div>
<div style="float: right; width: 16%;text-align: right; padding-top:5px; ">
<div id="spacer"></div>
<div id="auth-panel">
<span style="vertical-align: middle;">
<span id="guest-user">Guest
<a style="text-decoration: underline">(LOGIN)</a>
</span>
<span id="auth-user" style="display: none">
<span id="auth-user">
<span id="user-name"></span>
<a id="logout-button" style="text-decoration: underline; cursor: pointer" >
(logout)</a>
<span id="login-button" style="display: none; text-decoration: underline; cursor: pointer">(LOGIN)</span>
<span id="logout-button" style="display: none; text-decoration: underline; cursor: pointer">(LOGOUT)</span>
</span>
&nbsp;&nbsp;
</span>
<img src="img/user.png" />
</div>
<div style="float: right; text-align: right; width: 40%;" >
<a href="https://nomad-coe.eu/" target="_blank">
The NOMAD Laboratory
<img src="img/nomad-logo.png" /> <!--style="padding-top:10px"-->
</a>
</div>
<div style="clear: both;"></div>
</div>
<!--</div>-->
</header>
<div id="second-header">
<div style="float: left; width: 60%;">
<a href=""> <img src="img/NOMADEncyclopedia.png" /></a>
</div>
<div style="float: right; width: 30%;text-align: right">
......@@ -109,6 +89,5 @@
<div id="content">
</div>
<!-- <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script>-->
</body>
</html>
This diff is collapsed.
<!doctype html>
<html>
<body>
<script>
parent.postMessage(location.href, location.origin)
</script>
</body>
</html>
......@@ -38,6 +38,8 @@ flaggingFormPopup.innerHTML =`
<div class="form-wrapper">
<div class="popup-title"> Error reporting</div>
<br>
<div> Material: <span id="error-material-id"></span></div>
<select id="flagging-category" name="category">
<option value="">Select a category *</option>
<option value="structure">Structure</option>
......@@ -51,7 +53,7 @@ flaggingFormPopup.innerHTML =`
</select>
<textarea id="subject" name="subject" style="height:200px"
placeholder="Write a short explanation about the error detected" ></textarea>
placeholder="Write a short explanation about the error" ></textarea>
<div id="form-validation-msg"> </div>
......@@ -62,29 +64,25 @@ flaggingFormPopup.innerHTML =`
</div>
`;
// Form elements
let categoryField = flaggingFormPopup.querySelector('#flagging-category');
let eStructOption = categoryField.querySelector('option[value="electronicstruct"]');
let thermalOption = categoryField.querySelector('option[value="thermalprops"]');
let subcategoryField = flaggingFormPopup.querySelector('#flagging-subcategory');
let closeButton= flaggingFormPopup.querySelector('img');
let closeButton = flaggingFormPopup.querySelector('img');
let validationMsg = flaggingFormPopup.querySelector('#form-validation-msg');
let sendButton= flaggingFormPopup.querySelector('button');
let treeLeaf = null;
let sendButton = flaggingFormPopup.querySelector('button');
let materialIdField = flaggingFormPopup.querySelector('#error-material-id');
let overviewEStructCalcs = null;
function _setCurrentPage(pageId){
function _setCurrentPage(pageId) {
subcategoryField.innerHTML = '';
let materialId = DataStore.getMaterialData().material_id;
materialIdField.textContent = materialId;
if (pageId === null){
if (pageId === null) {
categoryField.disabled = false;
subcategoryField.style.display = 'none';
}else{
} else {
categoryField.disabled = true;
subcategoryField.style.display = 'block';
subcategoryField.appendChild(createOption('Choose the subcategory *', ''));
......@@ -123,18 +121,14 @@ function _setCurrentPage(pageId){
break;
}
}
}
} // function _setCurrentPage
function show(pageStatus){
//console.log('pageStatus : ',pageStatus);
treeLeaf = pageStatus.markedLeaf;
function show(pageStatus) {
overviewEStructCalcs = pageStatus.eStructCalcs;
// Show/hide some dropdown list options
eStructOption.style.display = (DataStore.hasElecStructureData ? 'block' : 'none');
thermalOption.style.display = (DataStore.hasThermalData ? 'block' : 'none');
eStructOption.style.display = (DataStore.hasElecStructureData() ? 'block' : 'none');
thermalOption.style.display = (DataStore.hasThermalData() ? 'block' : 'none');
_setCurrentPage(pageStatus.pageId);
......@@ -148,7 +142,6 @@ function show(pageStatus){
flaggingPopupBackground.style.visibility = 'visible';
}
function hide(){
flaggingPopupBackground.style.visibility = 'hidden';
flaggingFormPopup.style.visibility = 'hidden';
......@@ -159,7 +152,6 @@ function hide(){
validationMsg.innerHTML = '';
}
function createOption(text, value){
let opt = document.createElement('option');
opt.value = (value === undefined ? text : value);
......@@ -167,72 +159,67 @@ function createOption(text, value){
return opt;
}
closeButton.addEventListener('click', e => {
hide();
});
sendButton.addEventListener('click', e => {
// First double-check that the authentication is still valid.
let categoryChosen = categoryField.options[categoryField.selectedIndex];
var keycloak = window.keycloak;
if (!keycloak.authenticated) {
validationMsg.innerHTML = "Your authentication has expired. Please login again.";
return;
}
if (!categoryField.disabled && categoryChosen.value === ''){ // Overview case
// Check fields
if (!categoryField.disabled && categoryChosen.value === '') { // Overview case
validationMsg.innerHTML = 'The category fields must be set';
}else if (categoryField.disabled && subcategoryField.value === '' // Detaisl pages case
&& categoryChosen.value !== util.MAT_VIEW.methodology){
return;
} else if (categoryField.disabled && subcategoryField.value === '' // Details pages case
&& categoryChosen.value !== util.MAT_VIEW.methodology) {
validationMsg.innerHTML = 'The subcategory fields must be set';
}else{
validationMsg.innerHTML = 'Sending report...';
let textareaText = flaggingFormPopup.querySelector('textarea').value;
let materialId = DataStore.getMaterialData().material_id;
let userdata = util.getUserData();
let titleText = 'User issue | Material '+materialId;
let descriptionText = '**Server:** '+util.getServerLocation()+
'\\n\\n**User:** '+userdata.username+', '+userdata.email;
// Overview page
if ( !categoryField.disabled){
descriptionText += '\\n\\n**Category:** Overview / '+categoryChosen.text;
if (categoryChosen.value === util.MAT_VIEW.electronicstruct
&& overviewEStructCalcs !== null)
descriptionText += '\\n\\n**Chosen calculations:** '+
(overviewEStructCalcs.bs === null ? '' : 'BS calculation '+overviewEStructCalcs.bs)+
(overviewEStructCalcs.dos === null ? '' : ' DOS calculation '+overviewEStructCalcs.dos);
}else{ // Details pages
descriptionText += '\\n\\n**Category:** '+categoryChosen.text;
if (categoryChosen.value !== util.MAT_VIEW.methodology){
descriptionText += '\\n\\n**Subcategory:** '+
subcategoryField.options[subcategoryField.selectedIndex].text+
'\\n\\n**Calculation/group marked on the tree:** '+treeLeaf;
return;
}
// Send report
validationMsg.innerHTML = 'Sending report...';
let textareaText = flaggingFormPopup.querySelector('textarea').value;
let materialId = DataStore.getMaterialData().material_id;
keycloak.loadUserProfile()
.then(function(profile) {
let report = {};
report.server = util.getServerLocation();
report.username = profile.username;
report.email = profile.email;
report.first_name = profile.firstName;
report.last_name = profile.lastName;
report.message = textareaText;
report.category = categoryChosen.text;
// Overview page
if (!categoryField.disabled) {
report.representatives = DataStore.getRepresentatives();
} else { // Details pages
report.subcategory = subcategoryField.options[subcategoryField.selectedIndex].text;
}
}
descriptionText += '\\n\\n**User text:** '+textareaText;
let queryJson =`{
"title": "${titleText}",
"description": "${descriptionText}"}`;
console.log('Flagging POST request Json: ',queryJson);//, util.getFlaggingURL());
util.serverReqPOST(util.getFlaggingURL(), queryJson, e => {
console.log('response',e);
if (e.target.status === 200) hide();
let token = keycloak.token;
util.serverReqPOST(util.getReportURL(materialId), JSON.stringify(report), e => {
if (e.target.status === 204) {
hide();
} else {
validationMsg.innerHTML = "Could not connect to the service. Please check your connection and try again later.";
}
}, token);
})
.catch(error => {
console.log(error);
validationMsg.innerHTML = "Could not connect to the service. Please check your connection and try again later.";
});
}
});
// EXPORTS
module.exports = { show, hide };
......@@ -30,6 +30,9 @@ let loadingPopup = document.querySelector('#loading-popup');
let loadSet = new Set();
function show(id) {
if (!window.allowNewLoadPopup) {
return;
}
loadSet.add(id, true);
let ttRect = loadingPopup.getBoundingClientRect();
let leftPos = (window.innerWidth - ttRect.width)/2;
......@@ -46,5 +49,10 @@ function hide(id) {
}
}
function reset() {
loadSet = new Set();
loadingPopup.style.visibility = 'hidden';
}
// EXPORTS
module.exports = { show, hide };
module.exports = {show, hide, reset};
......@@ -25,6 +25,8 @@
"use strict";
let LoadingPopup = require('../common/LoadingPopup.js');
let routes = new Map();
......@@ -35,23 +37,46 @@ function add(route, func){
window.addEventListener("hashchange", route);
function route(){
let hashPath= document.location.hash.substring(2);
var hashHistory = [window.location.hash];
var historyLength = window.history.length;
function route() {
var hash = window.location.hash, length = window.history.length;
if (hashHistory.length && historyLength == length) {
if (hashHistory[hashHistory.length - 2] == hash) {
hashHistory = hashHistory.slice(0, -1);
LoadingPopup.reset();
window.allowNewLoadPopup = false;
} else {
window.allowNewLoadPopup = true;
hashHistory.push(hash);
}
} else {
window.allowNewLoadPopup = true;
hashHistory.push(hash);
historyLength = length;
}
let hashPath = document.location.hash.substring(2);
let command, param, subparam;
// remove the ending /
// Remove the ending /
if (hashPath.lastIndexOf('/') === (hashPath.length-1))
hashPath = hashPath.substring(0,hashPath.length-1);
if (hashPath.indexOf('/') >0){
// Remove state parameters from authentication
let stateIndex = hashPath.indexOf('&state');
if (stateIndex != -1) {
hashPath = hashPath.substring(0, stateIndex);
}
if (hashPath.indexOf('/') > 0){
let a= hashPath.split('/');
command= a[0];
param= a[1];
subparam= a[2];
}
else command= hashPath;
//console.log("hashPath: " + hashPath+' command: '+command+' param: '+param+' subparam: '+subparam);
else command = hashPath;
if (routes.has(command)) {
routes.get(command)(param, subparam);
......
......@@ -32,9 +32,6 @@
"use strict";
let DataStore = require('../material-mod/DataStore.js');
// global state vars
let materialId = null;
let searchResults = false;
......@@ -67,9 +64,7 @@ let ELEMENTS = [
];
// API URL and user cookie domain configuration
const API_BASE_URL = window.nomadEnv.host + window.nomadEnv.path;
document.querySelector('#guest-user a').href = API_BASE_URL+'saml/?sso2';
const API_BASE_URL = window.nomadEnv.apiRoot;
// Mockup URLs
//const FERMI_SURFACE_URL= HOST+'files/fermi/'+
......@@ -137,7 +132,7 @@ function getShortCode(id) {
function setAuthRequestHeader(userDataP, value){
if (value === undefined){// default value
if (value === undefined) { // default value
authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER;
userData = null;
//console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue);
......@@ -160,13 +155,16 @@ function serverReq(url, callback){
}
function serverReqPOST(url, data, callback){
function serverReqPOST(url, data, callback, token){
var oReq = new XMLHttpRequest();
oReq.addEventListener('load', callback);
oReq.open('POST', url);
oReq.setRequestHeader('Content-Type', 'application/json');
//console.log('authRequestHeaderValue: ',authRequestHeaderValue);
oReq.setRequestHeader('Authorization', authRequestHeaderValue);
if (token !== null) {
oReq.setRequestHeader('Authorization', 'Bearer ' + token);
} else {
oReq.setRequestHeader('Authorization', authRequestHeaderValue);
}
oReq.send(data);
return oReq;
}
......@@ -217,8 +215,8 @@ function getCalcEnergiesURL(matId,calcId){
return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+'/energies';//'/materials/calculations';//
}
function getFlaggingURL(){
return API_BASE_URL+'flagme';
function getReportURL(matId) {
return API_BASE_URL+'materials/'+matId+'/reports';
}
......@@ -345,85 +343,37 @@ function getAverage(array){
}
function getQuantityStatsMap(calcs) {
// Determine which statistics to build based on system type
let quantities;
let materialType = "";
let labelMap = {
volume: 'Volume (ų)',
atomic_density: 'Atomic density (Å⁻³)',
mass_density: 'Mass density (kg/m³)',
lattice_a: 'a (Å)',
lattice_b: 'b (Å)',
lattice_c: 'c (Å)'
};
if (materialType == "bulk") {
quantities = ['volume', 'atomic_density', 'mass_density', 'lattice_a', 'lattice_b', 'lattice_c'];
} else {
quantities = ['lattice_a', 'lattice_b', 'lattice_c'];
}
let quantitiesMap = new Map();
// Request quantity statistics from the server. The statistics are calculated
// on the server to keep the GUI responsive in case of large number of
// calculations.
let matId = DataStore.getMaterialData().material_id;
let query = JSON.stringify({calculations: calcs});
serverReqPOST(getMaterialStatsURL(matId), query, e3 => {
let results = JSON.parse(e3.target.response);
console.log(results);
console.log(e3);
});
//serverReqPOST(getMaterialStatsURL(matId), {calculations: calcs}, e3 => {
//let results = JSON.parse(e3.target.response).results;
//console.log(e3);
//console.log(results);
//});
return quantitiesMap;
//function getQuantityStatsMap(calcs) {
// OLD
//if (calcs.values().next().value.cell_volume === null){ // not bulk type volume of a calc null
//// Determine which statistics to build based on system type
//let quantities;
//let materialType = "";
//let labelMap = {
//volume: 'Volume (ų)',
//atomic_density: 'Atomic density (Å⁻³)',
//mass_density: 'Mass density (kg/m³)',
//lattice_a: 'a (Å)',
//lattice_b: 'b (Å)',
//lattice_c: 'c (Å)'
//};
//if (materialType == "bulk") {
//quantities = ['volume', 'atomic_density', 'mass_density', 'lattice_a', 'lattice_b', 'lattice_c'];
//} else {
//quantities = ['lattice_a', 'lattice_b', 'lattice_c'];
//labels = ['a (Å)', 'b (Å)', 'c (Å)'];
//}
//quantities.forEach( (quantity, index) => {
//let array= [];
//calcs.forEach( calc => {
//let value;
//if (quantity === 'volume') value = calc.cell_volume/1e-30;
//else if (quantity === 'atomic_density') value = calc.atomic_density*1e-30;
//else if (quantity === 'mass_density') value = calc.mass_density;
//else if (quantity.indexOf('lattice') >= 0){
//let tempLattParams= getNumberArray(calc.lattice_parameters);
//if (quantity === 'lattice_a') value = tempLattParams[0]/1e-10;
//else if (quantity === 'lattice_b') value = tempLattParams[1]/1e-10;
//else if (quantity === 'lattice_c') value = tempLattParams[2]/1e-10;
//}
//array.push(value);
//});
//let stats = {};
//stats.data = array;
//stats.min = Math.min.apply(null, array);
//stats.max = Math.max.apply(null, array);
//stats.equal = (stats.min === stats.max);
//let lls = labels[index].split(':');
//stats.label = lls[0];
//if (lls.length === 2) stats.units = lls[1];
//else stats.units = '';
//let decimals = 3;
//if (quantity === 'mass_density') decimals = 1;
//stats.html = getAverage(stats.data).toFixed(decimals)+
//' &nbsp; <span style="font-size: 0.9em">['+stats.min.toFixed(decimals)
//+' , '+stats.max.toFixed(decimals)+']</span>';
//quantitiesMap.set(quantity, stats);
//let quantitiesMap = new Map();
//// Request quantity statistics from the server. The statistics are calculated
//// on the server to keep the GUI responsive in case of large number of
//// calculations.
//let matId = DataStore.getMaterialData().material_id;
//let query = JSON.stringify({calculations: calcs});
//serverReqPOST(getMaterialStatsURL(matId), query, e3 => {
//let results = JSON.parse(e3.target.response);
//});
//return quantitiesMap;
}
//}
function toAngstromMinus3(density){
......@@ -453,9 +403,8 @@ function getMinMaxHTML(calcs,prop){
}
function generateDiagramSteps(maxVal){
function generateDiagramSteps(maxVal, d=4){
let d = 4; // generates 0 and 4 more points
let exp = -Math.floor(Math.log(maxVal/d) * Math.LOG10E);
let factor = Math.pow(10,exp);//100;
......@@ -470,7 +419,12 @@ function generateDiagramSteps(maxVal){
return [stepArray, exp];
}
function getDefault(value, fallback="unavailable") {
if (value === undefined || value === null) {
return fallback;
}
return value;
}
/*
function addBandGapData(calcJson, bsData){
......@@ -483,15 +437,6 @@ function addBandGapData(calcJson, bsData){
}
}*/
/*
function is2DSystem_temporary_patch(){
//console.log('TEMPORARY PATCH is2DSystem:', DataStore.getMaterialData());
return DataStore.getMaterialData().system_type === '2D';
}
*/
module.exports = {
searchResults,
materialId,
......@@ -514,7 +459,7 @@ module.exports = {
getMaterialStatsURL: getMaterialStatsURL,
getMaterialXsURL: getMaterialXsURL,
getCalcEnergiesURL: getCalcEnergiesURL,
getFlaggingURL,
getReportURL,
setBrowserHashPath: setBrowserHashPath,
loadLib: loadLib,
getNumberArray: getNumberArray,
......@@ -526,7 +471,6 @@ module.exports = {
getLatticeAnglesValues: getLatticeAnglesValues,
rad2degree: rad2degree,
m3ToAngstrom3: m3ToAngstrom3,
getQuantityStatsMap: getQuantityStatsMap,
toAngstromMinus3,
getMaterialTitle,
getMinMaxHTML: getMinMaxHTML,
......@@ -534,6 +478,7 @@ module.exports = {
getAverage,
generateDiagramSteps,
getCalcMapByFunctional,
getDefault,
//is2DSystem_temporary_patch
//addBandGapData
};
......@@ -40,23 +40,78 @@ let DataStore = require('./material-mod/DataStore.js');
// main DOM elements
let contentElement = document.getElementById('content');
let titleElement = document.querySelector('title');
window.allowNewLoadPopup = true;
// As of 0.8.3 nomad-FAIR is using KeyCloak 7.0.0, which does
// not support the "silentCheckSsoRedirectUri" option. This option enables a
// silent login check that does not enforce reloads. In order to do such silent
// login, the Javascript adapter for KeyCloak 8.0.0 is used instead. This is
// against the best practice of directly downloading the Javascript adapter
// from the authentication server (window.nomadEnv.keycloakBase +
// "js/keycloak.min.js"), but is in this case acceptable as it result is a much
// smoother user experience.
PubSub.subscribe('authenticated', data => {
let hashPath = document.location.hash.substring(2);
if (hashPath.lastIndexOf('/') === (hashPath.length-1))
hashPath = hashPath.substring(0,hashPath.length-1);
if (hashPath.indexOf('/') > 0){
let command = hashPath.split('/')[0];
if (command === "material") {
flaggingTab.style.visibility = 'visible';
}
}
});
var keycloak = new Keycloak({
url: window.nomadEnv.keycloakBase,
realm: window.nomadEnv.keycloakRealm,
clientId: window.nomadEnv.keycloakClientId
});
window.keycloak = keycloak;
let loginButton = document.querySelector('#login-button');
let logoutButton = document.querySelector('#logout-button');
let userName = document.querySelector('#user-name');
keycloak.init({
onLoad: "check-sso",
silentCheckSsoRedirectUri: `${window.nomadEnv.guiRoot}silent-check-sso.html`,
promiseType: "native",
}).then((authenticated) => {
if (authenticated) {
keycloak.loadUserProfile()
.then(function(profile) {
userName.textContent = `${profile.firstName} ${profile.lastName}`;
loginButton.style.display = 'none';
logoutButton.style.display = 'inline';
PubSub.publish('authenticated');
}).catch(function() {
console.log('Failed to load user profile.');
});
//util.setAuthRequestHeader(data.user, data.token.data);
} else {
loginButton.style.display = 'inline';
logoutButton.style.display = 'none';
userName.textContent = "Guest";
}
});
loginButton.onclick = () => {
keycloak.login({redirectUri: `${window.nomadEnv.guiRoot}#/search`})
.catch(() => {console.log("Authentication error.");});
};
logoutButton.onclick = () => {
keycloak.logout();
};
/********* User flagging side tab ****************/
/* This side vertical tab is hidden initially
but it has to be set up when the app starts */
let flaggingTab = document.getElementById('calc-flagging-tab');
flaggingTab.style.top = (window.innerHeight/2)+'px';
flaggingTab.addEventListener('click',e => {
flaggingTab.addEventListener('click', e => {
FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus());
});
/*********** App Breadcrumb component definition ***************/
class Breadcrumb {
......@@ -104,10 +159,10 @@ class Breadcrumb {
let self = this;
function adjustDropdownOptions() {
let esOption = self.detailsDropDown.querySelector('option[value="electronicstruct"]');
if (!DataStore.hasElecStructureData) self.detailsDropDown.removeChild(esOption);
if (!DataStore.hasElecStructureData()) self.detailsDropDown.removeChild(esOption);
let thOption = self.detailsDropDown.querySelector('option[value="thermalprops"]');
if (!DataStore.hasThermalData) self.detailsDropDown.removeChild(thOption);
if (!DataStore.hasThermalData()) self.detailsDropDown.removeChild(thOption);
// Remove because we want it's executed once
self.detailsDropDown.removeEventListener('focus', adjustDropdownOptions);
}
......@@ -188,28 +243,29 @@ PubSub.subscribe('show-material', data => {
// In case the app comes from the search module through the url (back button)
UserGuidance.show(false);
//console.log('User data:',util.getUserData());
if (util.getUserData() !== null) flaggingTab.style.visibility = 'visible';
// When a logged user comes to a material page, the error reporting tab is shown.
if (keycloak.authenticated) {
flaggingTab.style.visibility = 'visible';
}
});
PubSub.subscribe('show-search', search => {
console.log('Handling event show-search: '+search);
// When a logged user comes to a material page, the error reporting tab is shown.
flaggingTab.style.visibility = 'hidden';
titleElement.innerHTML = 'NOMAD Encyclopedia - Search';
breadcrumb.setState('search',search);
breadcrumb.setState('search', search);
if (search === undefined){
if (search === undefined) {
//LoadingPopup.reset();
searchMod.showSearchPage();
LoadingPopup.hide(); // In case it comes from the result page
}else if (search === 'results')
} else if (search === 'results') {
searchMod.showResultsPage();
}
showModuleDOM(searchMod.element);
if (flaggingTab.style.visibility !== 'hidden')
flaggingTab.style.visibility = 'hidden';
});
......@@ -225,29 +281,6 @@ Router.route();
/********* User authentication ***********/
let userNameElement = document.querySelector('#user-name');
let logoutButton = document.querySelector('#logout-button');
function setAppAuthenticated(data){
if (data.status === 'Authenticated'){
userNameElement.innerHTML = data.user.username;
document.querySelector('#guest-user').style.display = 'none';
document.querySelector('#auth-user').style.display = 'inline';
util.setAuthRequestHeader(data.user, data.token.data);
if (currentModule === materialModDOM) flaggingTab.style.visibility = 'visible';
}
}
function setAppLoggedOut(){
userNameElement.innerHTML = '';
document.querySelector('#guest-user').style.display = 'inline';
document.querySelector('#auth-user').style.display = 'none';
util.setAuthRequestHeader();
if (flaggingTab.style.visibility !== 'hidden')
flaggingTab.style.visibility = 'hidden';
}
function getCookie(name) {
let value = "; " + document.cookie;
......@@ -255,29 +288,14 @@ function getCookie(name) {
if (parts.length === 2) return parts.pop().split(";").shift();
}
function parseCookie(userData) {
return userData.substring(1, userData.length-1).replace(/\\054/g,',').replace(/\\/g,'');
}
let userInfoCookie = getCookie('user_info');
//console.log('Cookies: ', document.cookie, userInfoCookie);
if (userInfoCookie !== undefined){
let userInfoData = JSON.parse(parseCookie(userInfoCookie));
//console.log('userInfoData: ', userInfoData);
setAppAuthenticated(userInfoData);
}
// Logout
logoutButton.addEventListener( "click", e => {
document.cookie='user_info=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain='+
window.nomadEnv.userCookieDomain+'; path=/';
//console.log('Logging out document.cookie ',document.cookie);
setAppLoggedOut();
//console.log('Logging out ',userNameElement.innerHTML);
});
......@@ -34,7 +34,7 @@ class BSDOSPlotter{
this.element.setAttribute('style','margin: 0 auto');
this.parentElement= null;
this.bsPlotter= new BSPlotter();
this.dosPlotter= new DOSPlotter({left: 4, right: 16, top: 0, bottom: 30});
this.dosPlotter= new DOSPlotter({left: 4, right: 20, top: 0, bottom: 30});
this.dosYAxisLabeled = false;
}
......@@ -67,8 +67,8 @@ class BSDOSPlotter{
if (this.dosYAxisLabeled !== newDosYAxisLabeled){
this.element.removeChild(this.dosPlotter.svg);
let newLeftMargin = (newDosYAxisLabeled ? 40 : 4)
this.dosPlotter= new DOSPlotter({left: newLeftMargin, right: 16, top: 0, bottom: 30});
let newLeftMargin = (newDosYAxisLabeled ? 40 : 4);
this.dosPlotter= new DOSPlotter({left: newLeftMargin, right: 20, top: 0, bottom: 30});
let width = this.height/2 + newLeftMargin;
this.dosPlotter.attach(this.element, width, this.height);
}
......
......@@ -31,7 +31,7 @@ let util = require('../common/util.js');
const E_MIN = -6;
const E_MAX = 11;
const E_FACTOR = 1.602176565e-19;
const DOSVALUE_FACTOR = 1.602176565e-49;
const DOSVALUE_FACTOR = 1.602176565e-19;
class DOSPlotter extends InteractivePlotterBase {
......@@ -78,7 +78,7 @@ class DOSPlotter extends InteractivePlotterBase {
let minEnergyVal = Math.min.apply(null, pointsYInPlotRange);
// x axis steps generation
let t = util.generateDiagramSteps(maxDosVal);
let t = util.generateDiagramSteps(maxDosVal, 3);
let xSteps = t[0], exp = t[1];
this.setAxisRangeAndLabels(null, 0, xSteps[xSteps.length-1], 'Energy (eV)',
......@@ -91,8 +91,10 @@ class DOSPlotter extends InteractivePlotterBase {
for (let i = 0; i < xSteps.length; i++) {
let stepX = (this.plotRangeX*xSteps[i])/xSteps[xSteps.length-1];
svg.addLine(this.axisGroup, stepX, 0, stepX, 3, 1);
let x = xSteps[i];
x = (Math.abs(x) >= 10000 || Math.abs(x) <= 0.0001) ? x.toExponential(1) : parseFloat(x.toFixed(3));
svg.addText(this.axisGroup, stepX, 13,
(i === 0 ? '0' : xSteps[i].toFixed(exp)),'middle', 'axis-steps-smaller');
(i === 0 ? '0' : x), 'middle', 'axis-steps-smaller');
}
this.repaint();
......
......@@ -21,9 +21,18 @@
It stores the data loaded from the backend (immutable).
In addition it creates app life-time entities for convenience
*/
let util = require('../common/util.js');
let materialData;
let groups;
let calcsInGroups;
let calcs;
let representatives;
let idealizedStructure;
let calcMap = new Map();
let ready = false;
let hasThermal;
let hasElecStructure;
function setMaterialData(dataFromAPI){
materialData = dataFromAPI;
......@@ -33,17 +42,36 @@ function getMaterialData(){
return materialData;
}
function setIdealizedStructure(structure){
idealizedStructure = structure;
}
let calcs;
let representatives;
let calcMap = new Map();
function getIdealizedStructure(){
return idealizedStructure;
}
function setCalculations(calcsFromAPI){
// Clean up null values already here.
calcs = calcsFromAPI.results;
calcs.forEach(function(calc, index) {
let functional_type = util.getDefault(calc.functional_type);
let core_electron_treatment = util.getDefault(calc.core_electron_treatment);
this[index].functional_type = functional_type;
this[index].core_electron_treatment = core_electron_treatment;
}, calcs);
representatives = calcsFromAPI.representatives;
for (let i = 0; i < calcs.length; i++) {
calcMap.set(calcs[i].calc_id, calcs[i]);
}
// Check what type of information is available
let calcWithBS = representatives.electronic_band_structure;
let calcWithDOS = representatives.electronic_dos;
let calcWithHeat = representatives.thermodynamical_properties;
hasElecStructure = (calcWithBS !== undefined || calcWithDOS !== undefined);
hasThermal = calcWithHeat !== undefined;
}
function getRepresentatives() {
......@@ -58,9 +86,6 @@ function getCalc(calcId){
return calcMap.get(calcId);
}
let groups;
let calcsInGroups;
/**
* Stores the group information from API into an easily accessible format.
*/
......@@ -102,6 +127,28 @@ function getGroupId(leafId) {
return leafId.substring(4);
}
function isReady(matId) {
if (materialData !== undefined) {
if (idealizedStructure !== undefined) {
if (calcs !== undefined) {
if (groups !== undefined) {
if (matId === materialData.material_id) {
return true;
}
}
}
}
}
return false;
}
function clear() {
materialData = undefined;
calcs = undefined;
groups = undefined;
idealizedStructure = undefined;
}
function isInAnyGroup(calcId){
return calcsInGroups.has(calcId);
}
......@@ -118,30 +165,36 @@ function getGroupLeafId(calcId){
return leafId;
}
let hasThermalData, hasElecStructureData;
/*
function hasThermalData(bool){
hasThermalData = bool;
}
function setHasThermalData(bool){
hasThermalData = bool;
function hasThermalData(bool) {
return hasThermal;
}
function hasElecStructureData(bool){
hasThermalData = bool;
return hasElecStructure;
}
function setHasElecStructureData(bool){
hasElecStructureData = bool;
}*/
// EXPORTS
module.exports = { getRepresentatives, setMaterialData, getMaterialData, getCalculations, getCalc,
setCalculations, getGroups, getGroupId, setGroups, isGroup, getGroupType,
getReprCalc, isInAnyGroup, isInAnyNotDisabledGroup, getGroupLeafId,
hasThermalData, hasElecStructureData};
module.exports = {
getRepresentatives,
setMaterialData,
getMaterialData,
getCalculations,
setCalculations,
getCalc,
getGroups,
setGroups,
getGroupId,
isGroup,
getGroupType,
getReprCalc,
isInAnyGroup,
isInAnyNotDisabledGroup,
getGroupLeafId,
isReady,
clear,
setIdealizedStructure,
getIdealizedStructure,
hasThermalData,
hasElecStructureData
};
......@@ -158,7 +158,6 @@ class ElectronicStructDetails extends DetailsViewBase {
this.summaryByFunctionals = null;
this.summaryBox = this.element.querySelector('.summary-box');
this.summaryBox.style.visibility = "hidden";
this.summaryByFunctionals = new SummaryByFunctionalsComponent(this.summaryBox);
this.bsDosPlotter = new BSDOSPlotter();
......@@ -378,8 +377,7 @@ class SummaryByFunctionalsComponent{
this.bandgapField = this.hostElement.querySelector('.summary-bandgap-field');
this.statsViewer = new StatsViewer();
let chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder');
this.statsViewer.attach(chartsPlaceholder, 250, 150);
this.chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder');
this.chartTab.addEventListener( "click", e => {
this.chartTab.style.fill = '#777';
......@@ -398,7 +396,7 @@ class SummaryByFunctionalsComponent{
});
this.functionalTabs.addEventListener( "click", e => {
if (e.target.className === 'tab'){
if (e.target.className === 'tab') {
this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]')
.className = 'tab';
this.functional = e.target.getAttribute('data-tab');
......@@ -426,10 +424,14 @@ class SummaryByFunctionalsComponent{
let results = JSON.parse(e3.target.response);
let stats = results.band_gap;
if (stats === undefined) {
this.hostElement.style.visibility = "hidden";
this.bandgapField.innerHTML = "No band gap data available";
this.statsViewer.clear();
this.chartsPlaceholder.textContent = "No band gap data available";
return;
} else {
this.chartsPlaceholder.textContent = "";
this.statsViewer.attach(this.chartsPlaceholder, 250, 150);
}
this.hostElement.style.visibility = "visible";
// Unit conversion
stats.min *= 6.241509e18;
......
......@@ -40,10 +40,6 @@ let DataStore = require('./DataStore.js');
let LoadingPopup = require('../common/LoadingPopup.js');
// Store material data at this level (material model) ?
let materialData = {};
let materialCalcsData = null;
// Store the default marked tree leafs
let markedTreeLeafs = { eStruct: null, thermalProps: null };
......@@ -147,55 +143,52 @@ class MaterialMod {
this._setView(view);
let show = () => {
// Cell viewer needs to be set only after page is visible so that it is
// resized correctly.
if (this.currentDetailView !== null) {
this.currentDetailView.load();
if (view === util.MAT_VIEW.structure)
this.structureViewer.load(materialData.idealized_structure);
this._setCellViewer(this.structureDetails.vizBox);
if (view === util.MAT_VIEW.methodology) {
this.methodologyDetails.updateSelection();
}
} else {
document.querySelector('title').innerHTML =
'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false);
this.overview.setMaterialData();
let name = (materialData.material_name === null ? materialData.formula : materialData.material_name);
this.overview.setCalcsData(markedTreeLeafs);
this.structureViewer.load(materialData.idealized_structure);
this._setCellViewer(this.overview.vizBox);
let materialData = DataStore.getMaterialData();
let idealizedStructure = DataStore.getIdealizedStructure();
// Cell viewer needs to be set only after page is visible so that it is
// resized correctly.
if (this.currentDetailView !== null) {
this.currentDetailView.load();
if (view === util.MAT_VIEW.structure) {
this._setCellViewer(this.structureDetails.vizBox);
}
if (view === util.MAT_VIEW.methodology) {
this.methodologyDetails.updateSelection();
}
} else {
document.querySelector('title').innerHTML =
'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false);
this.overview.setMaterialData();
let name = (materialData.material_name === null ? materialData.formula : materialData.material_name);
this.overview.setCalcsData(markedTreeLeafs);
this._setCellViewer(this.overview.vizBox);
}
};
let isReady = () => {
let materialData = DataStore.getMaterialData();
let calcs = DataStore.getCalculations();
let groups = DataStore.getGroups();
if (materialData !== undefined) {
if (materialData.idealized_structure !== undefined) {
if (calcs !== undefined) {
if (groups !== undefined) {
if (matId === materialData.material_id) {
show();
return true;
}
}
}
}
let ready = DataStore.isReady(matId);
if (ready) {
show();
}
return false;
return ready;
};
// If material is already loaded, nothing fetched.
if (!isReady()) {
DataStore.clear();
LoadingPopup.reset();
this.structureViewer.axisCheckbox.checked = true;
this.structureViewer.bondsCheckbox.checked = true;
document.getElementById('methodology-ov').style.visibility = 'hidden';
document.getElementById('structure-ov').style.visibility = 'hidden';
document.getElementById('e-structure-ov').style.display = 'none';
document.getElementById('e-structure-ov').style.visibility = 'hidden';
document.getElementById('thermal-props-ov').style.visibility = 'hidden';
// Request basic material data
LoadingPopup.show("load_basic");
util.serverReq(util.getMaterialURL(matId), e1 => {
let basicMaterialData = JSON.parse(e1.target.response);
Object.assign(materialData, basicMaterialData);
let materialData = JSON.parse(e1.target.response);
DataStore.setMaterialData(materialData);
util.materialId = materialData.material_id;
isReady();
LoadingPopup.hide("load_basic");
......@@ -214,7 +207,11 @@ class MaterialMod {
let query = JSON.stringify({properties: ["idealized_structure"]});
LoadingPopup.show("load_idealized");
util.serverReqPOST(util.getMaterialCalcURL(matId, idealId), query, e2 => {
materialData.idealized_structure = JSON.parse(e2.target.response).idealized_structure;
let struct = JSON.parse(e2.target.response).idealized_structure;
DataStore.setIdealizedStructure(struct);
this.structureViewer.load(struct);
document.getElementById('structure-ov').style.visibility = 'visible';
document.getElementById('methodology-ov').style.visibility = 'visible';
isReady();
LoadingPopup.hide("load_idealized");
});
......@@ -226,7 +223,6 @@ class MaterialMod {
util.serverReq(util.getMaterialXsURL('groups', matId), e5 => {
let groups = JSON.parse(e5.target.response);
DataStore.setGroups(groups);
DataStore.setMaterialData(materialData);
isReady();
LoadingPopup.hide("load_groups");
});
......@@ -247,7 +243,7 @@ class StructureViewerWrapper {
view: {
autoResize: false,
autoFit: true,
fitMargin: 0.3,
fitMargin: 0.5,
},
structure: {
createLegend: false,
......
......@@ -135,8 +135,8 @@ class FilterInGroupsComponent {
calcs.forEach( c => {
if (!lCalcTypesMap.has(c.type))
lCalcTypesMap.set(c.type, calcTypesMap.get(c.type));
if (!lDensityFunctionalMap.has(c.functional))
lDensityFunctionalMap.set(c.functional, c.functional);
if (!lDensityFunctionalMap.has(c.functional_type))
lDensityFunctionalMap.set(c.functional_type, c.functional_type);
if (!lCodeMap.has(c.code))
lCodeMap.set(c.code, c.code);
if (!lPotentialMap.has(c.core_electron_treatment))
......@@ -247,16 +247,14 @@ class MethodologyDetails extends DetailsViewBase {
this.moreInfoRow.className= 'moreinfo';
this.moreInfoCalcId = null;
// filtering feature initialitation
// Filtering feature initialization
this.filterComponent = new FilterInGroupsComponent('meth-filter-component');
this.element.querySelector('.filter-placeholder').
appendChild(this.filterComponent.element);
this.filterComponent.setItemListener( propsSel => {
// console.log('FILTERING propsSel: ',propsSel);
this.sortedCalcs.forEach( rowCalcData => { //leafId => {
let calcProps = rowCalcData.dataCalcProps.split(',');
//console.log('FILTERING calcProps: ',calcProps);
let propsPresent = true;
calcProps.forEach( e => {
if (propsSel.indexOf(e) < 0) propsPresent = false;
......@@ -267,16 +265,16 @@ class MethodologyDetails extends DetailsViewBase {
this._render();
});
// row sorting feature initialitation
// Row sorting feature initialization
this.sortingButtonWrappers =
this.dataTableWrapper.querySelectorAll('.sorting-button');
this.sortingButtons = [];
let sortingButtonsMap = new Map([
[ 'id', undefined ],
[ 'calc_id', undefined ],
[ 'type', undefined ],
[ 'functional', undefined ],
[ 'functional_type', undefined ],
[ 'code', undefined ],
[ 'core_electron_treatment', undefined ],
[ 'basis_set_type', undefined ] ]);
......@@ -289,7 +287,6 @@ class MethodologyDetails extends DetailsViewBase {
this.sortingButtons.push(component);
component.setListener( (descendingOrder, field) => {
//console.log(descendingOrder, field);
this.sortingButtons.forEach( el => {
if (el !== component) el.init();
});
......@@ -403,7 +400,7 @@ class MethodologyDetails extends DetailsViewBase {
this.sortedCalcs.push({
calc_id: c.calc_id,
type: calcType,
functional: c.functional_type,
functional_type: c.functional_type,
code: c.code_name,
core_electron_treatment: c.core_electron_treatment,
basis_set_type: c.basis_set_type,
......@@ -415,7 +412,7 @@ class MethodologyDetails extends DetailsViewBase {
this.filterComponent.addGroupsItems(this.sortedCalcs);
this._sortRowsCalcDataBy(true, 'id');
this._sortRowsCalcDataBy(true, 'calc_id');
this._render();
}
......@@ -424,28 +421,24 @@ class MethodologyDetails extends DetailsViewBase {
_sortRowsCalcDataBy(descendingOrder, field){
this.sortedCalcs.sort( (a, b) => {
if(a[field] < b[field]) return (descendingOrder ? -1 : 1);
if(a[field] > b[field]) return (descendingOrder ? 1 : -1);
return 0;
let order = descendingOrder ? -1 : 1;
return a[field] < b[field] ? order : -order;
});
}
updateMarkedLeaf(leafId){ }
_render(){
let html = '';
this.sortedCalcs.forEach( rowCalcData => { //leafId => {
//html+= getRowHtml(leafId, calc, calcType);
if (rowCalcData.visible) html+= getRowHtml(rowCalcData);
});
this.tbody.innerHTML = html;
InfoSys.addToInfoSystem(this.tbody);
function getRowHtml(rowCalcData/*leafId, calc, calcType*/){
function getRowHtml(rowCalcData/*leafId, calc, calcType*/) {
let calc = DataStore.getCalc( /*DataStore.getCalcReprIntId(*/rowCalcData.calc_id);
let calcType = rowCalcData.type;
......@@ -469,8 +462,8 @@ class MethodologyDetails extends DetailsViewBase {
${calcType}</span>
</td>
<td>
<span info-sys-data="functional-type.value:${calc.functional_type}">
${calc.functional_type}</span>
<span info-sys-data="functional-type.value:${rowCalcData.functional_type}">
${rowCalcData.functional_type}</span>
${getOptValue(calc.functional_long_name)}
</td>
<td>
......@@ -480,8 +473,8 @@ class MethodologyDetails extends DetailsViewBase {
</td>