From 318b396affcf5dbdc0ebb4282ce203c840fe3a45 Mon Sep 17 00:00:00 2001 From: Iker Hurtado <iker@ikerhurtado.com> Date: Sun, 11 Nov 2018 20:03:58 +0100 Subject: [PATCH] Search by Material name last improvements --- client/bundle.js | 18204 ++++++++++++------------ client/src/search-mod/NewSearchMod.js | 62 +- 2 files changed, 9167 insertions(+), 9099 deletions(-) diff --git a/client/bundle.js b/client/bundle.js index 44b65420..9c7c32e5 100644 --- a/client/bundle.js +++ b/client/bundle.js @@ -1,9917 +1,9977 @@ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; - +/******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { - +/******/ /******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) +/******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; - +/******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} /******/ }; - +/******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - +/******/ /******/ // Flag the module as loaded -/******/ module.loaded = true; - +/******/ module.l = true; +/******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } - - +/******/ +/******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; - +/******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; - +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; - +/******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(0); +/******/ return __webpack_require__(__webpack_require__.s = 16); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file is the application entry point. - It defines some app level components (Breadcrumb) and - initializes several more (app level events, app routing, authentication) - */ - - - "use strict"; - - let util = __webpack_require__(1); - let LoadingPopup = __webpack_require__(4); - let FlaggingFormPopup = __webpack_require__(5); - let PubSub = __webpack_require__(6); - let Router = __webpack_require__(7); - let MaterialMod = __webpack_require__(8); - let SearchModule = __webpack_require__(32); - let UserGuidance = __webpack_require__(33); - let DataStore = __webpack_require__(2); - - - // main DOM elements - let contentElement = document.getElementById('content'); - let titleElement = document.querySelector('title'); - - - /********* 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 => { - FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus()); - }); - - - - /*********** App Breadcrumb component definition ***************/ - - class Breadcrumb { - - constructor() { - - this.element = document.querySelector('#breadcrumb-placeholder'); - this.element.innerHTML = ` - <span class="goto-page Search">Search</span> - <span class="goto-page Overview"> > <span>Overview</span></span> - <span class="Details"> - > - <select class="details-dropdown" > - <option value="structure">Structure</option> - <option value="electronicstruct">Electronic structure</option> - <option value="methodology">Methodology</option> - <option value="thermalprops">Thermal Properties</option> - <!-- elasticconst--> - </select> - </span> - `; - this.overviewSel = this.element.querySelector('.Overview'); - this.detailsSel = this.element.querySelector('.Details'); - this.detailsDropDown = this.element.querySelector('.details-dropdown'); - - // Events - this.element.querySelector('.Search').addEventListener( "click", e => { - util.setBrowserHashPath('search'); - }); - - this.overviewSel.addEventListener('click', () => { - util.setBrowserHashPath('material', util.materialId); - }); - - this.detailsDropDown.addEventListener('change', e => { - util.setBrowserHashPath('material', - DataStore.getMaterialData().id+'/'+e.target.value); - }); - - let self = this; - function adjustDropdownOptions() { - let esOption = self.detailsDropDown.querySelector('option[value="electronicstruct"]'); - if (!DataStore.hasElecStructureData) self.detailsDropDown.removeChild(esOption); - - let thOption = self.detailsDropDown.querySelector('option[value="thermalprops"]'); - if (!DataStore.hasThermalData) self.detailsDropDown.removeChild(thOption); - // Remove because we want it's executed once - self.detailsDropDown.removeEventListener('focus', adjustDropdownOptions); - } - - this.detailsDropDown.addEventListener('focus', adjustDropdownOptions); - } - - - setState(appModule, param){ - let overviewSelLabel = this.overviewSel.querySelector('span'); - overviewSelLabel.style.fontWeight = 'normal'; - - if (appModule === 'search'){ - this.overviewSel.style.display = 'none'; - this.detailsSel.style.display = 'none'; - - /* - if (param === 'results'){ - this.resultsSel.style.display = 'inline'; - this.resultsSel.querySelector('span').style.fontWeight = 'bold'; - this.element.style.visibility = 'visible'; - }else this.element.style.visibility = 'hidden'; - */ - this.element.style.visibility = 'hidden'; - - }else if (appModule === 'material'){ - this.element.style.visibility = 'visible'; - this.overviewSel.style.display = 'inline'; - - if (param === undefined){ // Overview page - this.detailsSel.style.display = 'none'; - overviewSelLabel.style.fontWeight = 'bold'; - }else{ // Details page - this.detailsSel.style.display = 'inline'; - this.detailsDropDown.value = param; - } - } - } // setState - - } // class Breadcrumb - - - /*************************** - App setup - ***************************/ - - let breadcrumb = new Breadcrumb(); - - let searchMod; - let MaterialModule; - let materialModDOM; - let currentModule; // current module DOM being shown - +/***/ (function(module, exports, __webpack_require__) { - function showModuleDOM(module){ - if (currentModule) contentElement.removeChild(currentModule); - currentModule= module; - contentElement.appendChild(currentModule); - } - - - /****** App level events setup ********/ - - PubSub.subscribe('show-material', data => { - console.log('Handling event show-material: '+data.id+' view: '+data.view); +"use strict"; - //titleElement.innerHTML = 'NOMAD Encyclopedia - Material '+data.id; - breadcrumb.setState('material',data.view); +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - if (typeof materialModDOM === 'undefined'){ - MaterialModule = new MaterialMod(); - materialModDOM= MaterialModule.element; - } - MaterialModule.setMaterialView(data); - showModuleDOM(materialModDOM); - - // 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'; - }); + /* + This is an app-level utility JavaScript file. It holds: + - Environments conf (API URL and user cookie domain configuration) + * Maybe this conf info can be removed from here to a better place + - global state variables and app-level constants + - miscellaneous app-level function + - local vars + - app-level util functions - PubSub.subscribe('show-search', search => { - console.log('Handling event show-search: '+search); + * Maybe this file should be rethought + */ - titleElement.innerHTML = 'NOMAD Encyclopedia - Search'; - breadcrumb.setState('search',search); - if (search === undefined){ - searchMod.showSearchPage(); - LoadingPopup.hide(); // In case it comes from the result page - }//else if (search === 'results') searchMod.showSearchResults(); - showModuleDOM(searchMod.element); +let DataStore = __webpack_require__(1); +let Conf = __webpack_require__(17); - if (flaggingTab.style.visibility !== 'hidden') - flaggingTab.style.visibility = 'hidden'; - }); +// global state vars +let materialId = null; - /****** App routing config ******/ +// app-level constants - Router.add('search', search => PubSub.publish('show-search', search)); - Router.add('material', (matId, view) => PubSub.publish('show-material', {'id': matId, 'view': view})); +const IMAGE_DIR = 'img/'; +const AUTH_REQUEST_HEADER_GUEST_USER = 'Basic '+ btoa(Conf.GuestUserToken+':'); +const MAT_VIEW = { + 'structure' : 'structure', + 'electronicstruct': 'electronicstruct', + 'methodology': 'methodology', + 'thermalprops': 'thermalprops', + 'elasticconst': 'elasticconst' +}; - /****** init ******/ +let ELEMENTS = [ + 'H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', // Si = 14 + 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', // Nin = 28 + 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', // Nb = 41 + 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', // Xe = 54 + 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', // Ho= 67 + 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', // Hg = 80 + 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', // Np = 93 + 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Ha', 'Sg', // sg = 106 + 'Ns', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og' // Mt = 109 +]; - searchMod = new SearchModule(); - //console.log('document.location: '+document.location.hash); - if (document.location.hash === '') document.location += "#/search"; - Router.route(); +// API URL and user cookie domain configuration +// Case 1: my local dev environment +let API_HOST= 'http://enc-staging-nomad.esc.rzg.mpg.de/'; +let USER_COOKIE_DOMAIN = 'localhost'; +let path = 'current/v1.0/'; // current development version of the API +// let path = 'v1.0/'; // stable staging - /********* User authentication ***********/ +// Case 2: production environment +if (document.location.href.indexOf('nomad-coe.eu') > 0){// + API_HOST='https://encyclopedia.nomad-coe.eu/'; + USER_COOKIE_DOMAIN = '.nomad-coe.eu'; + path = 'api/v1.0/'; +} + // Case 3: testing-staging environment and local installations (docker containers) of full Encyclopedia stack +else if (document.location.href.indexOf('gui') > 0){ + API_HOST= '/'; // USER_COOKIE_DOMAIN = 'localhost' and path = 'v1.0/'; + if (document.location.href.indexOf('staging') > 0) // testing-staging server + USER_COOKIE_DOMAIN = '.mpg.de'; +} - let userNameElement = document.querySelector('#user-name'); - let logoutButton = document.querySelector('#logout-button'); +let API_BASE_URL = API_HOST + path; +//console.log('API host: ',API_HOST,' USER_COOKIE_DOMAIN: ',USER_COOKIE_DOMAIN); - 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'; - } - } +document.querySelector('#guest-user a').href = API_BASE_URL+'saml/?sso2'; - function setAppLoggedOut(){ - userNameElement.innerHTML = ''; - document.querySelector('#guest-user').style.display = 'inline'; - document.querySelector('#auth-user').style.display = 'none'; - util.setAuthRequestHeader(); +// Mockup URLs +//const FERMI_SURFACE_URL= HOST+'files/fermi/'+ +//'fed3fa9fbc68aa6c5e51845396889666ca37bb2e626e1da53.x3d'; - if (flaggingTab.style.visibility !== 'hidden') - flaggingTab.style.visibility = 'hidden'; - } - function getCookie(name) { - let value = "; " + document.cookie; - let parts = value.split("; " + name + "="); - if (parts.length === 2) return parts.pop().split(";").shift(); - } +// Local variables +let authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER; +//console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue); +let userData = null; - function parseCookie(userData) { - return userData.substring(1, userData.length-1).replace(/\\054/g,',').replace(/\\/g,''); - } +// app-level util functions - let userInfoCookie = getCookie('user_info'); - //console.log('Cookies: ', document.cookie, userInfoCookie); +function getUserData(){ + return userData; +} - if (userInfoCookie !== undefined){ - let userInfoData = JSON.parse(parseCookie(userInfoCookie)); - //console.log('userInfoData: ', userInfoData); - setAppAuthenticated(userInfoData); - } +function getServerLocation(){ + return document.location.hostname; +} - // Logout - logoutButton.addEventListener( "click", e => { - document.cookie='user_info=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain='+ - util.USER_COOKIE_DOMAIN+'; path=/'; - //console.log('Logging out document.cookie ',document.cookie); +function authServerReq(url, callback){ + var oReq = new XMLHttpRequest(); + oReq.addEventListener("load", callback); + console.log('util.authServerReq: ', API_BASE_URL+url); + oReq.open("GET", API_BASE_URL+url); + //console.log('util.authServerReq oReq: ', oReq); + oReq.send(); + return oReq; +} - setAppLoggedOut(); - //console.log('Logging out ',userNameElement.innerHTML); - }); +function setAuthRequestHeader(userDataP, value){ -/***/ }, + if (value === undefined){// default value + authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER; + userData = null; + //console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue); + } else{ + authRequestHeaderValue = 'Basic '+ btoa(value+':'); + userData = userDataP; + //console.log('user',user,'authRequestHeader: ',authRequestHeaderValue); + } +} + + +function serverReq(url, callback){ + var oReq = new XMLHttpRequest(); + oReq.addEventListener("load", callback); + oReq.open("GET", url); + //console.log('authRequestHeaderValue: ',authRequestHeaderValue); + oReq.setRequestHeader('Authorization', authRequestHeaderValue); + oReq.send(); + return oReq; +} + + +function serverReqPOST(url, data, callback){ + 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); + oReq.send(data); + return oReq; +} + + +function getSubscriptedFormula(formula){ + let finalFormula= ''; // 'elementCode' : number + for (let i = 0; i < formula.length; i++){ + if (formula.charCodeAt(i) >= 47 && formula.charCodeAt(i) < 58 ) + finalFormula += '<sub>'+formula[i]+'</sub>'; + else finalFormula += formula[i]; + //console.log(formula.charCodeAt(i) + " "+finalFormula); + } + return finalFormula; +} + + +function getSearchURL(){ + return API_BASE_URL+'esmaterials'; +} + +function getSuggestionURL(quantity){ + return API_BASE_URL+'suggestions?property='+quantity; +} + +function getMaterialURL(matId){ + return API_BASE_URL+'materials/'+matId;//'/materials/matid'; // +} + +function getMaterialCalcURL(matId, calcId, property = ''){ + let propertyString = (property === '' ? '' : '?property='+property); + return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+propertyString; +} + +function getMaterialXsURL(what, matId){ + return API_BASE_URL+'materials/'+matId+'/'+what+'?pagination=off';//page=1&per_page=5000';//'/materials/calculations';// +} + +function getCalcEnergiesURL(matId,calcId){ + return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+'/energies';//'/materials/calculations';// +} + +function getFlaggingURL(){ + return API_BASE_URL+'flagme'; +} + + +// Launch an app event +function setBrowserHashPath(modulePath, finalPath){ + if (typeof finalPath === 'undefined') document.location= '#/'+modulePath; + else document.location= '#/'+modulePath+'/'+finalPath; +} + +function loadLib(url){ + let script = document.createElement('script'); + script.setAttribute('type', 'text/javascript'); + script.setAttribute('src', url); + document.getElementsByTagName('head')[0].appendChild(script); +} + +function getNumberArray(string){ + let sArray= string.substring(1,string.length-1).split(','); + let fArray= []; + for (var i = 0; i < sArray.length; i++) { + fArray.push(parseFloat(sArray[i])); + } + //console.log('getNumberArray.SPLIT: '+fArray); + return fArray; +} + +function getCellDataForViewer(matData){ + let cellData= {}; + cellData.normalizedCell= [ + getNumberArray(matData.cell.a), + getNumberArray(matData.cell.b), + getNumberArray(matData.cell.c) + ]; + + cellData.periodicity = JSON.parse(matData.periodicity); + + cellData.labels= []; + cellData.positions= []; + + for (var i = 0; i < matData.elements.length; i++) { + cellData.labels.push(matData.elements[i].label); + cellData.positions.push(getNumberArray(matData.elements[i].position)); + } + return cellData; +} + + +function J2eV(energy, decimals){ + let result= energy/1.602176565e-19; + if (decimals === undefined){ + if (result < 0.01) return result.toFixed(6); + else return result.toFixed(3); + }else{ + return result.toFixed(decimals); + } +} + +function eV2J(energy){ + return energy*1.602176565e-19; +} + + +/* +function getBandGapStatsValue(calcs){ + let bandGapSum= 0; + let bandArray= []; + let bandGapDirect= calcs[0].band_gap_direct; + let bandGapType= (bandGapDirect ? "direct" : "indirect"); + + for (var i = 0; i < calcs.length; i++) { + //if (calcs[i].band_gap > 0){ + bandGapSum+= calcs[i].band_gap; + bandArray.push(calcs[i].band_gap); + if (calcs[i].band_gap_direct !== bandGapDirect) + bandGapType= 'various results'; + //} + //console.log(bandGapSum+' '+calcs[i].band_gap+' '+bandArray.length); + } + + let html= '';//let html= ((bandGapSum / bandArray.length)/1.602176565e-19).toFixed(3)+' eV ('+bandGapType+')';; + let min= (Math.min.apply(null, bandArray)/1.602176565e-19).toFixed(3); + let max= (Math.max.apply(null, bandArray)/1.602176565e-19).toFixed(3); + html+= ' ('+min+' ... '+max+' eV)'; + //html+= ' ['+bandArray.length+' / '+calcs.length+']'; + + return html; +}*/ + +function m2Angstrom(dist){ + return (dist/1e-10).toFixed(3)+' Å'; +} + + +function getLatticeAnglesValues(calcs, twoD, bulk){ + let lattParams= [0.0, 0.0, 0.0]; + calcs.forEach( (calc) => { + if (calc.lattice_parameters !== undefined && calc.lattice_parameters !== null){ + let tempLattParams= getNumberArray(calc.lattice_parameters); + lattParams[0] += tempLattParams[3]; + lattParams[1] += tempLattParams[4]; + lattParams[2] += tempLattParams[5]; + } + }); + + if (bulk) + return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div> + <div>β = ${rad2degree(lattParams[1] / calcs.size)}</div> + <div>γ = ${rad2degree(lattParams[2] / calcs.size)}</div>`; + else if (twoD) + return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div>`; + else return ''; // 1D +} + + +function rad2degree(angle){ + return (angle * (180 / Math.PI)).toFixed(0)+'°'; +} + +function m3ToAngstrom3(vol){ + return (vol/1e-30).toFixed(3)+' Å<sup>3</sup>'; +} + + +function getAverage(array){ + let sum = 0; + for (var i = 0; i < array.length; i++) sum += array[i]; + return sum/array.length; +} + + +function getQuantityStatsMap(calcs){ + + let quantities = ['volume', 'atomic_density', 'mass_density', 'lattice_a', 'lattice_b', 'lattice_c']; + let labels = ['Volume (ų)', 'Atomic density (Å⁻³)', 'Mass density (kg/m³)', 'a (Å)', 'b (Å)', 'c (Å)']; + let quantitiesMap = new Map(); + + if (calcs.values().next().value.cell_volume === null){ // not bulk type volume of a calc null + 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)+ + ' <span style="font-size: 0.9em">['+stats.min.toFixed(decimals) + +' , '+stats.max.toFixed(decimals)+']</span>'; + + quantitiesMap.set(quantity, stats); + }); + return quantitiesMap; +} + + +function toAngstromMinus3(density){ + return (density*1e-30).toFixed(3)+' Å<sup>-3</sup>'; +} + + +function getMaterialTitle(data, html){ + let title; + title = getSubscriptedFormula(data.formula_reduced); + if (html !== undefined && html ===false) title = data.formula_reduced; + + if (data.space_group_number !== null) + title += ' - space group '+data.space_group_number; + //return '<span style="font-size: 0.9em">'+title+' </span>'; + return title; +} + +function getMinMaxHTML(calcs,prop){ + let propArray= []; + + calcs.forEach( (calc) => { + propArray.push(calc[prop]); + }); + + return '('+Math.min.apply(null, propArray)+' ... '+Math.max.apply(null, propArray)+')'; +} + + +function generateDiagramSteps(maxVal){ + + 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; + //console.log('util.generateDiagramSteps ',exp, maxVal/d, factor); + let ceil = Math.ceil(maxVal*factor/d); + let stepArray = []; + for (var i = 0; i <= d; i++) { + stepArray[i] = ceil*i/factor; + } + //console.log('stepArray '+stepArray); + exp = (exp < 0 ? 0 : exp); + return [stepArray, exp]; +} + + + +/* +function addBandGapData(calcJson, bsData){ + if (calcJson.band_gap > 0) { + bsData.bandGapData = {}; + bsData.bandGapData.cbmEnergy = calcJson.band_gap_lower_energy; + bsData.bandGapData.cbmKpt = getNumberArray(calcJson.band_gap_lower_kpt); + bsData.bandGapData.vbmEnergy = calcJson.band_gap_upper_energy; + bsData.bandGapData.vbmKpt = getNumberArray(calcJson.band_gap_upper_kpt); + } +}*/ + +/* +function is2DSystem_temporary_patch(){ + +//console.log('TEMPORARY PATCH is2DSystem:', DataStore.getMaterialData()); + return DataStore.getMaterialData().system_type === '2D'; +} +*/ + + +module.exports = { + materialId, + MAT_VIEW: MAT_VIEW, + IMAGE_DIR: IMAGE_DIR, + USER_COOKIE_DOMAIN, + ELEMENTS: ELEMENTS, + setAuthRequestHeader, + getUserData, + getServerLocation, + authServerReq, + serverReq, + serverReqPOST, + getSearchURL: getSearchURL, + getSuggestionURL, + getMaterialURL: getMaterialURL, + getMaterialCalcURL: getMaterialCalcURL, + getMaterialXsURL: getMaterialXsURL, + getCalcEnergiesURL: getCalcEnergiesURL, + getFlaggingURL, + setBrowserHashPath: setBrowserHashPath, + loadLib: loadLib, + getNumberArray: getNumberArray, + getCellDataForViewer: getCellDataForViewer, + //FERMI_SURFACE_URL: FERMI_SURFACE_URL, + J2eV: J2eV, + eV2J, + //getBandGapStatsValue: getBandGapStatsValue, + m2Angstrom: m2Angstrom, + getLatticeAnglesValues: getLatticeAnglesValues, + rad2degree: rad2degree, + m3ToAngstrom3: m3ToAngstrom3, + getQuantityStatsMap: getQuantityStatsMap, + toAngstromMinus3, + getMaterialTitle, + getMinMaxHTML: getMinMaxHTML, + getSubscriptedFormula: getSubscriptedFormula, + getAverage, + generateDiagramSteps, + //is2DSystem_temporary_patch + //addBandGapData +} + + +/***/ }), /* 1 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This is an app-level utility JavaScript file. It holds: - - - Environments conf (API URL and user cookie domain configuration) - * Maybe this conf info can be removed from here to a better place - - global state variables and app-level constants - - miscellaneous app-level function - - local vars - - app-level util functions - - * Maybe this file should be rethought - */ - - - "use strict"; - - let DataStore = __webpack_require__(2); - let Conf = __webpack_require__(3); - - - // global state vars - let materialId = null; - - - // app-level constants - - const IMAGE_DIR = 'img/'; - - const AUTH_REQUEST_HEADER_GUEST_USER = 'Basic '+ btoa(Conf.GuestUserToken+':'); - - const MAT_VIEW = { - 'structure' : 'structure', - 'electronicstruct': 'electronicstruct', - 'methodology': 'methodology', - 'thermalprops': 'thermalprops', - 'elasticconst': 'elasticconst' - }; - - let ELEMENTS = [ - 'H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', // Si = 14 - 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', // Nin = 28 - 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', // Nb = 41 - 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', // Xe = 54 - 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', // Ho= 67 - 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', // Hg = 80 - 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', // Np = 93 - 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Ha', 'Sg', // sg = 106 - 'Ns', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og' // Mt = 109 - ]; - - - // API URL and user cookie domain configuration - - // Case 1: my local dev environment - let API_HOST= 'http://enc-staging-nomad.esc.rzg.mpg.de/'; - let USER_COOKIE_DOMAIN = 'localhost'; - let path = 'current/v1.0/'; // current development version of the API - // let path = 'v1.0/'; // stable staging - - // Case 2: production environment - if (document.location.href.indexOf('nomad-coe.eu') > 0){// - API_HOST='https://encyclopedia.nomad-coe.eu/'; - USER_COOKIE_DOMAIN = '.nomad-coe.eu'; - path = 'api/v1.0/'; - } - // Case 3: testing-staging environment and local installations (docker containers) of full Encyclopedia stack - else if (document.location.href.indexOf('gui') > 0){ - API_HOST= '/'; // USER_COOKIE_DOMAIN = 'localhost' and path = 'v1.0/'; - if (document.location.href.indexOf('staging') > 0) // testing-staging server - USER_COOKIE_DOMAIN = '.mpg.de'; - } - - let API_BASE_URL = API_HOST + path; - //console.log('API host: ',API_HOST,' USER_COOKIE_DOMAIN: ',USER_COOKIE_DOMAIN); - - document.querySelector('#guest-user a').href = API_BASE_URL+'saml/?sso2'; - - - // Mockup URLs - //const FERMI_SURFACE_URL= HOST+'files/fermi/'+ - //'fed3fa9fbc68aa6c5e51845396889666ca37bb2e626e1da53.x3d'; - - - - // Local variables - - let authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER; - //console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue); - let userData = null; - - - // app-level util functions - - function getUserData(){ - return userData; - } - +/***/ (function(module, exports) { + + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + This file create and holds the application data models. + It stores the data loaded from the backend (immutable). + In addition it creates app life-time entities for convenience + */ + + +let materialData; + +function setMaterialData(dataFromAPI){ + materialData = dataFromAPI; +} + +function getMaterialData(){ + return materialData; +} + + +let calcs; +let calcMap = new Map(); + +function setCalculations(calcsFromAPI){ + calcs = calcsFromAPI; + for (let i = 0; i < calcs.length; i++) + calcMap.set(calcs[i].id, calcs[i]); +} + +function getCalculations(){ + return calcs; +} + +function getCalc(calcId){ + return calcMap.get(calcId); +} + + + +let groups; + +function processCalcGroups(groupsFromAPI){ + + let methodCalcsMap = new Map(); + groupsFromAPI.forEach( (group, i) => { + if (group.group_type !== 'method'){ + let calcsMins; + if (methodCalcsMap.has(group.method_hash) ){ + calcsMins = methodCalcsMap.get(group.method_hash); + }else{ + calcsMins = {'ids': [], 'minEnergies': []}; + methodCalcsMap.set(group.method_hash, calcsMins); + } + calcsMins.ids.push(group.representative_calculation_id); + calcsMins.minEnergies.push(group.energy_minimum); + } + }); + + let methodRepresentativeMap = new Map(); + methodCalcsMap.forEach( (calcsData, methodHash) => { + let minVal = Math.min.apply(null, calcsData.minEnergies); + let index = calcsData.minEnergies.indexOf(minVal); + methodRepresentativeMap.set(methodHash, calcsData.ids[index]); + }); + + let calcsInGroups = new Map(); + groupsFromAPI.forEach( (group, i) => { + if (group.group_type !== 'method'){ + let groupData = { + 'calcs': new Set(), + 'method_representative': methodRepresentativeMap.get(group.method_hash) + }; + calcsInGroups.set( + getGroupTypeCode(group)+group.representative_calculation_id, groupData); + //let calcSet = new Set(); // The representative is (not?) in the set + group.calculations_list.forEach( calcId => groupData.calcs.add(calcId) ); + } + }); + return calcsInGroups; +} + +function setGroups(groupsFromAPI){ + groups = processCalcGroups(groupsFromAPI); +} + +function getGroups(){ + return groups; +} + +function getGroupTypeCode(group){ + if (group.group_type === 'equation of state') return 'eos'; + else if (group.group_type === 'convergence') return 'par'; +} + +function getGroupType(leafId){ + let code = leafId.substring(0,3); + if (code === 'eos') return 'equation of state'; + else if (code === 'par') return 'convergence'; + else return null; +} + +function isGroup(leafId){//*** ********** REFACTOR + return (leafId.substring(0,3) === 'eos' || leafId.substring(0,3) === 'par'); +} + +function getCalcReprIntId(leafId){ + if (isGroup(leafId)) return parseInt(leafId.substring(3)); + else return parseInt(leafId); +} + +function isInAnyGroup(calcId){ + let thereIs = false; + groups.forEach( (groupData, groupId) => { + //console.log('isInAnyGroup', calcId, groupData.calcs); + if (groupData.calcs.has(calcId)) thereIs = true;//return true; + }); + return thereIs; +} + +function isInAnyNotDisabledGroup(calcId){ + let thereIs = false; + + groups.forEach( (groupData, groupId) => { + let representative = getCalc(getCalcReprIntId(groupId)); + //let codeNameTrimed= representative.code_name.trim(); + //console.log('isInAnyGroup', calcId, groupData.calcs); + + /***** Exception: disable grouping for some codes + if (codeNameTrimed !== 'VASP' && codeNameTrimed !== 'FHI-aims' + && groupData.calcs.has(calcId)) thereIs = true; */ + if (groupData.calcs.has(calcId)) thereIs = true; + }); + return thereIs; +} + +function getGroupLeafId(calcId){ + let leafId = null; + groups.forEach( (groupData, groupId) => { + //console.log('isInAnyGroup', calcId, groupData.calcs); + if (groupData.calcs.has(calcId)) leafId = groupId;//return true; + }); + //console.log('getGroupLeafId', leafId); + return leafId; +} + + +let hasThermalData, hasElecStructureData; + +/* +function hasThermalData(bool){ + hasThermalData = bool; +} + +function setHasThermalData(bool){ + hasThermalData = bool; +} + +function hasElecStructureData(bool){ + hasThermalData = bool; +} + +function setHasElecStructureData(bool){ + hasElecStructureData = bool; +}*/ + + + +// EXPORTS +module.exports = { setMaterialData, getMaterialData, getCalculations, getCalc, + setCalculations, getGroups, setGroups, isGroup, getGroupType, + getCalcReprIntId, isInAnyGroup, isInAnyNotDisabledGroup, getGroupLeafId, + hasThermalData, hasElecStructureData}; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + SVG drawing util library + */ + + + + +const xmlns="http://www.w3.org/2000/svg"; +const xlink="http://www.w3.org/1999/xlink"; + + +function addPoint(parent, x, y, r, className) { + let e = document.createElementNS(xmlns, "circle"); + e.setAttribute("r", r); // e.setAttributeNS(null, "r", 5); + e.setAttribute("cx", x); + e.setAttribute("cy", y); + if (className !== undefined) e.setAttribute("class", className); + parent.appendChild(e); + return e; +} + +function addCircle(parent, x, y, r, fillColor, strokeColor, strokeWidth) { + let e = document.createElementNS(xmlns, "circle"); + e.setAttribute("r", r); // e.setAttributeNS(null, "r", 5); + e.setAttribute("cx", x); + e.setAttribute("cy", y); + e.setAttribute("fill", fillColor); + e.setAttribute("stroke", strokeColor); + e.setAttribute("stroke-width", strokeWidth); + parent.appendChild(e); + return e; +} + + +function addLine(parent, x1, y1, x2, y2, className) { + let e = document.createElementNS(xmlns, "line"); + e.setAttribute("x1", x1); + e.setAttribute("y1", y1); + e.setAttribute("x2", x2); + e.setAttribute("y2", y2); + if (className !== undefined) e.setAttribute("class", className); + //e.setAttribute("stroke-width", stroke); + parent.appendChild(e); + return e; +} + + +function addRect(parent, x, y, w, h) { + let e = document.createElementNS(xmlns, "rect"); + e.setAttribute("x", x); + e.setAttribute("y", y); + e.setAttribute("width", w); + e.setAttribute("height", h); + parent.appendChild(e); + return e; +} + + + +function addText(parent, x, y, text, textAnchor = 'start', className) { + let e = document.createElementNS(xmlns, "text"); + e.setAttribute("x", x); + e.setAttribute("y", y); + e.textContent= text; + //e.setAttribute("stroke", 'black'); + e.setAttribute("text-anchor", textAnchor); + if (className !== undefined) e.setAttribute("class", className); + parent.appendChild(e); + return e; +} + + +function addPolyline(parent, points, className) { + let e = document.createElementNS(xmlns, "polyline"); + e.setAttribute("points", points); + if (className !== undefined) e.setAttribute("class", className); + //e.setAttribute("stroke-width", stroke); + parent.appendChild(e); +} + + +function removeElement(element){ + element.parentElement.removeChild(element); +} + + + +// EXPORTS +module.exports = { + addPoint: addPoint, + addCircle, + addLine: addLine, + addRect: addRect, + addText: addText, + addPolyline: addPolyline, + removeElement: removeElement +}; - function getServerLocation(){ - return document.location.hostname; - } +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { - function authServerReq(url, callback){ - var oReq = new XMLHttpRequest(); - oReq.addEventListener("load", callback); - console.log('util.authServerReq: ', API_BASE_URL+url); - oReq.open("GET", API_BASE_URL+url); - //console.log('util.authServerReq oReq: ', oReq); - oReq.send(); - return oReq; - } +"use strict"; +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This file implements an information system for the user that provides info + related to the concepts or quantities shown on the UI + The literals are read form the file infosys.json + */ - function setAuthRequestHeader(userDataP, value){ - if (value === undefined){// default value - authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER; - userData = null; - //console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue); - } else{ - authRequestHeaderValue = 'Basic '+ btoa(value+':'); - userData = userDataP; - //console.log('user',user,'authRequestHeader: ',authRequestHeaderValue); - } - } +let util = __webpack_require__(0); +let SwitchComponent = __webpack_require__(10); + +const INFOSYS_FILE_PATH = 'infosys.json'; + +let tooltip = document.querySelector('#info-tooltip'); +let tooltipContent = document.querySelector('#tooltip-content'); + +let elements = []; +let timerSet = null; +let on = false; +let data = null; + +function clearCurrentTimeoutAnSetANew(){ + if (timerSet !== null) window.clearTimeout(timerSet); + timerSet = window.setTimeout(t => tooltip.style.display = 'none', 1000); +} + + +let switchComponent = new SwitchComponent(util.IMAGE_DIR+'switch'); +document.querySelector('#info-sys-switch-box').appendChild(switchComponent.element); + +switchComponent.setListener( off => { + on = !off; + if (off) { + elements.forEach( element => { + element.removeEventListener('mouseover', mouseOver); + element.className = ''; + }); + }else{ + if (data === null) + util.serverReq(INFOSYS_FILE_PATH, e => data = JSON.parse(e.target.response)); + + elements.forEach(enableTooltip); + } + + + tooltip.addEventListener('mouseover', e => { + window.clearTimeout(timerSet); + }); + + tooltip.addEventListener('mouseout', e => { + clearCurrentTimeoutAnSetANew(); + }); + +}); + + +function addToInfoSystem(baseElement){ + let infosysLabels = baseElement.querySelectorAll('span[info-sys-data]'); + + for (let i = 0; i < infosysLabels.length; ++i) + elements.push(infosysLabels[i]); + + //if (on) infosysLabels.forEach(enableTooltip); + if (on) + for (let i = 0; i < infosysLabels.length; ++i) + enableTooltip(infosysLabels[i]); +} + + +function addElementToInfoSystem(element, value){ + elements.push(element); + element.setAttribute('info-sys-data',value); + + if (on) enableTooltip(element); +} + + +function enableTooltip(element){ + element.addEventListener('mouseover', mouseOver); + element.addEventListener('mouseout', e => { + clearCurrentTimeoutAnSetANew(); + element.style.cursor = 'inherit'; + }); + element.className = 'info-sys-label'; +} + + +function mouseOver(e){ + let r = e.target.getBoundingClientRect(); + let quantity = e.target.getAttribute('info-sys-data'); + + let index = quantity.indexOf('.value'); + if (index > 0){ // quantity value + let quantityObject = data[quantity.split('-').join(' ').substring(0, index)]; + //console.log('VALUE', quantityObject); + if (quantityObject.value_template === undefined){ //direct value + let valueObj = quantityObject.values[quantity.split(':')[1]]; + tooltipContent.innerHTML = getHTML(valueObj); + }else{ // value template + let object = quantityObject.value_template; + object.text = templating(object.text, quantity.split(':')[1]); + object.link = templating(object.link, quantity.split(':')[1]); + tooltipContent.innerHTML = getHTML(object); + //console.log('VALUE TEMPLATE: ', object); + } + }else // quantity name + tooltipContent.innerHTML = getHTML(data[quantity.split('-').join(' ')]); + + tooltip.style.visibility = 'hidden'; + tooltip.style.display = 'block'; + let ttRect = tooltip.getBoundingClientRect(); + let leftOffset = ttRect.width - r.width; + let leftPos = r.left -leftOffset/2; + if (leftPos + ttRect.width > window.innerWidth) + leftPos = window.innerWidth -ttRect.width; + //let topOffset = ttRect.height + 20 - window.pageYOffset; + let topOffset = - window.pageYOffset; + tooltip.style.left = (leftPos < 0 ? 5 : leftPos)+'px'; + tooltip.style.top = (r.top + r.height -topOffset)+'px'; + tooltip.style.visibility = 'visible'; + window.clearTimeout(timerSet); + e.target.style.cursor = 'help'; +} + + +function templating(s, param) { + let initIndex = s.indexOf('${'); + let finalIndex = s.indexOf('}'); + if (initIndex >= 0 && finalIndex >= 0 && finalIndex > initIndex){ + return s.substring(0,initIndex)+param+s.substring(finalIndex+1); + }else return s; +} + +function getHTML(object) { + let html = ''; + if (object.text === undefined){ + //html = 'NO TEXT!! Comment: '+object.comment; + }else{ // there is text attr + html += object.text; + } + if (object.link !== undefined){ + if (object.text !== undefined) html += '<br>'; + html += '<a href="'+object.link+'" target="_blank">More information</a>'; + } + + return html; +} + + +// EXPORTS +module.exports = { addToInfoSystem, addElementToInfoSystem }; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { - function serverReq(url, callback){ - var oReq = new XMLHttpRequest(); - oReq.addEventListener("load", callback); - oReq.open("GET", url); - //console.log('authRequestHeaderValue: ',authRequestHeaderValue); - oReq.setRequestHeader('Authorization', authRequestHeaderValue); - oReq.send(); - return oReq; - } +"use strict"; +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - function serverReqPOST(url, data, callback){ - 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); - oReq.send(data); - return oReq; - } + /* + This component is the loading popup window + */ - function getSubscriptedFormula(formula){ - let finalFormula= ''; // 'elementCode' : number - for (let i = 0; i < formula.length; i++){ - if (formula.charCodeAt(i) >= 47 && formula.charCodeAt(i) < 58 ) - finalFormula += '<sub>'+formula[i]+'</sub>'; - else finalFormula += formula[i]; - //console.log(formula.charCodeAt(i) + " "+finalFormula); - } - return finalFormula; - } - function getSearchURL(){ - return API_BASE_URL+'esmaterials'; - } - function getSuggestionURL(quantity){ - return API_BASE_URL+'suggestions?property='+quantity; - } +let util = __webpack_require__(0); - function getMaterialURL(matId){ - return API_BASE_URL+'materials/'+matId;//'/materials/matid'; // - } +let loadingPopup = document.querySelector('#loading-popup'); - function getMaterialCalcURL(matId, calcId, property = ''){ - let propertyString = (property === '' ? '' : '?property='+property); - return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+propertyString; - } +function show(){ + let ttRect = loadingPopup.getBoundingClientRect(); + let leftPos = (window.innerWidth - ttRect.width)/2; + let topPos = (window.innerHeight -ttRect.height)/2; + loadingPopup.style.left = leftPos+'px'; + loadingPopup.style.top = (topPos-100)+'px'; + loadingPopup.style.visibility = 'visible'; +} - function getMaterialXsURL(what, matId){ - return API_BASE_URL+'materials/'+matId+'/'+what+'?pagination=off';//page=1&per_page=5000';//'/materials/calculations';// - } +function hide(){ + loadingPopup.style.visibility = 'hidden'; +} - function getCalcEnergiesURL(matId,calcId){ - return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+'/energies';//'/materials/calculations';// - } +// EXPORTS +module.exports = { show, hide }; - function getFlaggingURL(){ - return API_BASE_URL+'flagme'; - } +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { - // Launch an app event - function setBrowserHashPath(modulePath, finalPath){ - if (typeof finalPath === 'undefined') document.location= '#/'+modulePath; - else document.location= '#/'+modulePath+'/'+finalPath; - } +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + This is the base class that models a regular plotter. + It is inherited by several classes implementing specifc plotters + */ + + + + +let svg = __webpack_require__(2); + +const xmlns="http://www.w3.org/2000/svg"; +const xlink="http://www.w3.org/1999/xlink"; + + +class PlotterBase{ + + constructor(margins = {left: 20, right: 0, top: 10, bottom: 20}) { + this.svg = document.createElementNS(xmlns, "svg"); + this.plotArea = document.createElementNS(xmlns, "g"); + this.svg.appendChild(this.plotArea); + + this.margins= margins; + this.parentElement= null; + this.yLabelText = null; + this.noDataGroup = null; + } + + + attach(element, width, height){ + this.parentElement= element; + this.parentElement.appendChild(this.svg); + this.width = (width !== undefined ? width : this.parentElement.clientWidth); + this.height = (height !== undefined ? height : this.svg.width); + this.svg.setAttribute("width", this.width); + this.svg.setAttribute("height", this.height); + this.plotRangeX = this.width - this.margins.left - this.margins.right; + this.plotRangeY = this.height - this.margins.top - this.margins.bottom; + } + + + isAttached(){ + return this.parentElement !== null; + } + + + setRangeAndLabels(xLabel, xMin, xMax, yLabel, yMin, yMax){ + this.xLabel= xLabel; + this.xMin = xMin; + this.xMax = xMax; + this.yLabel= yLabel; + this.yMin= yMin; + this.yMax= yMax; + + this.xRel= this.plotRangeX/(this.xMax-this.xMin); + this.yRel= this.plotRangeY/(this.yMax-this.yMin); + } + + + drawAxis(xSteps = 0, ySteps = 0, decimals = 2){ + + this.plotArea.setAttribute("transform", 'matrix(1 0 0 1 '+ + this.margins.left+' '+(this.height - this.margins.bottom)+')'); + + this.yLabelText = svg.addText(this.svg, 0, 0, this.yLabel, 'middle', 'axis-steps-big'); + this.yLabelText.setAttribute('transform','translate(13,' + +(this.plotRangeY/2+this.margins.top)+') rotate(-90)'); + svg.addText(this.plotArea, this.plotRangeX/2, this.margins.bottom-1, + this.xLabel, 'middle', 'axis-steps-big'); + + if (xSteps !== null){ + let xStep= this.plotRangeX/ xSteps; + for (let i = 0; i <= xSteps; i++) { + svg.addLine(this.plotArea, xStep*i, 0, xStep*i, 4, 1); + svg.addText(this.plotArea, xStep*i, 14, + +((xStep*i/this.xRel)+this.xMin).toFixed(decimals), 'middle', 'statisticsviewersteps'); + } + } + + if (ySteps === null && this.yMax > 0 && this.yMin < 0){ + let i = 1; + while(this.yMax*i > this.yMin) { + svg.addLine(this.plotArea, 0, this.transformY(this.yMax*i), -3, this.transformY(this.yMax*i), 1); + let numberText = (Math.abs(this.yMax*i) >= 10000 ? (this.yMax*i).toExponential() : this.yMax*i ); + svg.addText(this.plotArea,-5, this.transformY(this.yMax*i)+3, numberText, + 'end', 'statisticsviewersteps'); + i--; + } + } + + if (ySteps !== null){ + let yStep= this.plotRangeY/ ySteps; + for (let i = 0; i <= ySteps; i++) { + svg.addLine(this.plotArea, 0, -yStep*i, -3, -yStep*i, 1); + let numberToPaint= (yStep*i/this.yRel) + this.yMin; + // Fix to prevent the the -0 printing + if (Math.abs(numberToPaint) < 0.01) numberToPaint = 0; + //console.log('drawAxis', yStep, i, this.yRel, this.yMin, numberToPaint); + svg.addText(this.plotArea,-5, -(yStep*i-3), numberToPaint.toFixed(decimals), 'end', 'statisticsviewersteps'); + } + } + + svg.addLine(this.plotArea, 0, 0, this.plotRangeX+1 ,0 ,'main-axis'); + svg.addLine(this.plotArea, 0,0,0,-(this.plotRangeY+1) ,'main-axis'); + svg.addLine(this.plotArea, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY ,'main-axis'); + svg.addLine(this.plotArea, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY,'main-axis'); + } + + + clear(){ + this.svg.removeChild(this.plotArea); + this.plotArea = document.createElementNS(xmlns, "g"); + this.svg.appendChild(this.plotArea); + this.plotArea.setAttribute("transform", 'matrix(1 0 0 1 '+ + this.margins.left+' '+(this.height - this.margins.bottom)+')'); + + if (this.yLabelText !== null){ + this.svg.removeChild(this.yLabelText); + this.yLabelText = null; + } + if (this.noDataGroup !== null){ + this.svg.removeChild(this.noDataGroup); + this.noDataGroup = null; + } + } + + + setNoData(){ + this.clear(); + if (this.noDataGroup === null){ + this.noDataGroup = document.createElementNS(xmlns, 'g'); + this.svg.appendChild(this.noDataGroup); + svg.addRect(this.noDataGroup, 0, 0, this.width, this.height); + this.noDataGroup.setAttribute('fill', '#EEE'); + svg.addText(this.noDataGroup, this.width/2, this.height/2+10, + 'NO DATA', 'middle', 'nodata'); + } + + } + + + // Transform from y-axis units to y-axis pixels (svg-coordinates) + transformY(y){ + return -this.yRel*(y - this.yMin); + } + +} + + +// EXPORTS +module.exports = PlotterBase; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { - function loadLib(url){ - let script = document.createElement('script'); - script.setAttribute('type', 'text/javascript'); - script.setAttribute('src', url); - document.getElementsByTagName('head')[0].appendChild(script); - } +"use strict"; - function getNumberArray(string){ - let sArray= string.substring(1,string.length-1).split(','); - let fArray= []; - for (var i = 0; i < sArray.length; i++) { - fArray.push(parseFloat(sArray[i])); - } - //console.log('getNumberArray.SPLIT: '+fArray); - return fArray; - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - function getCellDataForViewer(matData){ - let cellData= {}; - cellData.normalizedCell= [ - getNumberArray(matData.cell.a), - getNumberArray(matData.cell.b), - getNumberArray(matData.cell.c) - ]; + /* + Base class from which other main 'Details' view containers inherit. + Every of these containers shows all the material info related to a type of info: + Structure, Electronic Structure, etc + */ - cellData.periodicity = JSON.parse(matData.periodicity); - cellData.labels= []; - cellData.positions= []; - for (var i = 0; i < matData.elements.length; i++) { - cellData.labels.push(matData.elements[i].label); - cellData.positions.push(getNumberArray(matData.elements[i].position)); - } - return cellData; - } +let util = __webpack_require__(0); +let DataStore = __webpack_require__(1); - function J2eV(energy, decimals){ - let result= energy/1.602176565e-19; - if (decimals === undefined){ - if (result < 0.01) return result.toFixed(6); - else return result.toFixed(3); - }else{ - return result.toFixed(decimals); - } - } +class DetailsViewBase { - function eV2J(energy){ - return energy*1.602176565e-19; - } + constructor(domId) { + this.element = document.createElement('div'); + this.element.setAttribute('id',domId); + this.gotoResultsListener= null; + this.gotoOverviewListener= null; + this.element.innerHTML= '<div class="material-title"></div>'; + this.element.style.display= 'none'; + } - /* - function getBandGapStatsValue(calcs){ - let bandGapSum= 0; - let bandArray= []; - let bandGapDirect= calcs[0].band_gap_direct; - let bandGapType= (bandGapDirect ? "direct" : "indirect"); - - for (var i = 0; i < calcs.length; i++) { - //if (calcs[i].band_gap > 0){ - bandGapSum+= calcs[i].band_gap; - bandArray.push(calcs[i].band_gap); - if (calcs[i].band_gap_direct !== bandGapDirect) - bandGapType= 'various results'; - //} - //console.log(bandGapSum+' '+calcs[i].band_gap+' '+bandArray.length); - } + attachAndSetEvents(element){ + element.appendChild(this.element); + this.materialTitle= this.element.querySelector('.material-title'); + } - let html= '';//let html= ((bandGapSum / bandArray.length)/1.602176565e-19).toFixed(3)+' eV ('+bandGapType+')';; - let min= (Math.min.apply(null, bandArray)/1.602176565e-19).toFixed(3); - let max= (Math.max.apply(null, bandArray)/1.602176565e-19).toFixed(3); - html+= ' ('+min+' ... '+max+' eV)'; - //html+= ' ['+bandArray.length+' / '+calcs.length+']'; - return html; - }*/ + attachNavTree(navTree){ + navTree.attach(this.navTreeWrapper); + } - function m2Angstrom(dist){ - return (dist/1e-10).toFixed(3)+' Å'; - } + setVisible(){ + this.element.style.display= 'block'; + } - function getLatticeAnglesValues(calcs, twoD, bulk){ - let lattParams= [0.0, 0.0, 0.0]; - calcs.forEach( (calc) => { - if (calc.lattice_parameters !== undefined && calc.lattice_parameters !== null){ - let tempLattParams= getNumberArray(calc.lattice_parameters); - lattParams[0] += tempLattParams[3]; - lattParams[1] += tempLattParams[4]; - lattParams[2] += tempLattParams[5]; - } - }); - if (bulk) - return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div> - <div>β = ${rad2degree(lattParams[1] / calcs.size)}</div> - <div>γ = ${rad2degree(lattParams[2] / calcs.size)}</div>`; - else if (twoD) - return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div>`; - else return ''; // 1D - } + setMaterialData() { + this.materialTitle.innerHTML= util.getMaterialTitle(DataStore.getMaterialData()); + } - function rad2degree(angle){ - return (angle * (180 / Math.PI)).toFixed(0)+'°'; - } + updateCalcs(calcs){ + } - function m3ToAngstrom3(vol){ - return (vol/1e-30).toFixed(3)+' Å<sup>3</sup>'; - } + updateMarkedCalc(calc){ + } - function getAverage(array){ - let sum = 0; - for (var i = 0; i < array.length; i++) sum += array[i]; - return sum/array.length; - } +} +// EXPORTS +module.exports = DetailsViewBase; - function getQuantityStatsMap(calcs){ - let quantities = ['volume', 'atomic_density', 'mass_density', 'lattice_a', 'lattice_b', 'lattice_c']; - let labels = ['Volume (ų)', 'Atomic density (Å⁻³)', 'Mass density (kg/m³)', 'a (Å)', 'b (Å)', 'c (Å)']; - let quantitiesMap = new Map(); +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + Implements the Band Structure plotter. + It's used to show either the regular Band Structure data or + the Phonon Dispersion. + */ + + + + +let InteractivePlotterBase = __webpack_require__(8); +let svg = __webpack_require__(2); +let util = __webpack_require__(0); + +const MULTIPLO1 = 1.602176565e-19; +const MULTIPLO2 = 1/5.034117012222e22; + + +class BSPlotter extends InteractivePlotterBase{ + + constructor() { + super({left: 55, right: 5, top: 0, bottom: 30}); + this.phononMode = false; + this.factor = MULTIPLO1; + } + + + setPhononMode(){ + this.phononMode = true; + this.factor = MULTIPLO2; + this.outOfRangeColorActivated = false; + } + + + // detach if necessary and attach + attach(element, width, height){ + super.attach(element, width, height); + } + + + getTopAndLowestPoints(bandStructData){ + let bandMax = -10000; + let bandMin = 10000; + for (let i = 0; i < bandStructData.length; i++) { // Per segment + // spin1 - per band energy loop + for (let j = 0; j < bandStructData[i].band_energies[0].length; j++) { + let tempValue= Math.max.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/); + if (tempValue > bandMax) bandMax= tempValue; + tempValue= Math.min.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/); + if (tempValue < bandMin) bandMin= tempValue; + } + + if (bandStructData[i].band_energies.length === 2) + // spin2 - per band energy loop + for (let j = 0; j < bandStructData[i].band_energies[1].length; j++) { // Per segment + let tempValue= Math.max.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/); + if (tempValue > bandMax) bandMax= tempValue; + tempValue= Math.min.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/); + if (tempValue < bandMin) bandMin= tempValue; + } + } + return [bandMin/this.factor, bandMax/this.factor]; + } + + + drawKPointLabel(x, label){ + svg.addText(this.axisGroup, x*this.xRel, 16, label, 'middle', 'steps'); + } + + + setBandStructureData(data){ + + let bandStructData; + if (data.segments !== undefined) bandStructData = data.segments; + else bandStructData = data; + + this.bandGapData = undefined; + if (data.band_gap !== undefined && data.band_gap.position !== undefined){ + this.bandGapData = data.band_gap.position; + this.bandGapData.cbmDistances = []; + this.bandGapData.vbmDistances = []; + } + + // Gather all the points per band (divided by spin) crossing the segments + this.bandsDataSpin1= []; // [segment][band][kpoint] + this.bandsDataSpin2= []; + this.segmentLimitsX = []; + this._reset(); + + let topAndLowestPoints = this.getTopAndLowestPoints(bandStructData); + let minEnergyVal = topAndLowestPoints[0]; + let maxEnergyVal = topAndLowestPoints[1]; + + if (this.phononMode){ + this.setAxisRangeAndLabels('',0,1,'Frequency (cm⁻¹)',-50, 320, + minEnergyVal, maxEnergyVal, 100); + }else + this.setAxisRangeAndLabels('',0,1,'Energy (eV)' ,-6 ,11 , minEnergyVal, + maxEnergyVal, 5 ); + + // Calculates de distance + let totalDistance= 0; + for (let k = 0; k < bandStructData.length; k++) { + let kPoints= bandStructData[k].band_k_points; + totalDistance+= kPointDistance(kPoints,kPoints.length-1); + } + let currentDistance= 0; + let prevLastLabel = null; + let dataOverflow = false; + + for (let k = 0; k < bandStructData.length; k++) { // For every segment + + let segment= bandStructData[k]; + let kPoints= segment.band_k_points; + let labels= segment.band_segm_labels; + + let energiesSpin1= segment.band_energies[0]; + let energiesSpin2= segment.band_energies[1]; + this.bandsDataSpin1.push([]); // Add a new array per segment + this.bandsDataSpin2.push([]); + + let segmentDistance= kPointDistance(kPoints,kPoints.length-1); + + // keeping the segment limits (x coordenate) for after painting + this.segmentLimitsX.push(currentDistance/totalDistance); + + if (labels !== null){ + // Set k-points labels + if (prevLastLabel !== null && prevLastLabel !== labels[0]) + this.drawKPointLabel(currentDistance/totalDistance, + getSymbol(prevLastLabel)+'|'+getSymbol(labels[0])); + else + this.drawKPointLabel(currentDistance/totalDistance,getSymbol(labels[0])); + // The last label + if (k === bandStructData.length -1) + this.drawKPointLabel(1, getSymbol(labels[1])); + + prevLastLabel = labels[1]; + } + + for (let i = 0; i < kPoints.length; i++) { // For every k-point + + let tempDistance= (currentDistance + kPointDistance(kPoints, i))/totalDistance; + + if (this.bandGapData !== undefined){ + + if (this.bandGapData.lower !== undefined){ + let kpt = this.bandGapData.lower.kpt; + if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] + && kPoints[i][2] === kpt[2]){ + this.bandGapData.cbmDistances.push(tempDistance); + } + } + + if (this.bandGapData.upper !== undefined){ + let kpt = this.bandGapData.upper.kpt; + if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] + && kPoints[i][2] === kpt[2]){ + this.bandGapData.vbmDistances.push(tempDistance); + } + } + + /* + let kpt = this.bandGapData.cbmKpt; + if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] + && kPoints[i][2] === kpt[2]){ + //console.log("kpoints: ", kPoints[i], kpt, tempDistance); + this.bandGapData.cbmX = tempDistance; + } + kpt = this.bandGapData.vbmKpt; + if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] + && kPoints[i][2] === kpt[2]){ + //console.log("kpoints: ", kPoints[i], kpt, tempDistance); + this.bandGapData.vbmX = tempDistance; + } + */ + } + + + // All bands spin1 + for (let j = 0; j < energiesSpin1[i].length; j++) { + if (i === 0) this.bandsDataSpin1[k][j] = [];//if (k === 0 && i === 0) this.bandsDataSpin1[j] = []; + let currentY = energiesSpin1[i][j]/this.factor; + this.bandsDataSpin1[k][j].push({x: tempDistance, y: currentY}); + if (!dataOverflow && currentY > 10000) dataOverflow = true; + } + // All bands spin2 + if (energiesSpin2 !== undefined) + for (let j = 0; j < energiesSpin2[i].length; j++) { + if (i === 0) this.bandsDataSpin2[k][j] = []; + let currentY = energiesSpin2[i][j]/this.factor; + this.bandsDataSpin2[k][j].push({x: tempDistance, y: currentY}); + if (!dataOverflow && currentY > 10000) dataOverflow = true; + } + //console.log("K PPPPPP Ponint: "+i+' DIS: '+tempDistance, this.bandsDataSpin1[k]); + } + + currentDistance+= segmentDistance; + } + + if (dataOverflow) throw 'Plotter Data Overflow: Probably the energy data is not in correct units'; //console.log('BSPlotter data overflow'); + else this.repaint(); + } + + + repaintData(yMin, yMax){ + + this.segmentLimitsX.forEach(x => { + let yMinPx = this.transformY(yMin); + let yMaxPx = this.transformY(yMax); + if (this.phononMode) { yMinPx += 200; yMaxPx -= 200; } + svg.addLine(this.plotContent, x*this.xRel, yMinPx, + x*this.xRel, yMaxPx, 'segment'); + }); + + // Drawing lines + let polylinePoints; + for (var i = 0; i < this.bandsDataSpin1.length; i++) // loop the segments + + for (var j = 0; j < this.bandsDataSpin1[i].length; j++) { // loop the bands + polylinePoints = ''; + for (var k = 0; k < this.bandsDataSpin1[i][j].length; k++) { // loop the kpoints + polylinePoints+= ' '+this.xRel*this.bandsDataSpin1[i][j][k].x+ + ' '+this.transformY(this.bandsDataSpin1[i][j][k].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); + } + + if (this.bandsDataSpin2.length > 0){ + for (var i = 0; i < this.bandsDataSpin2.length; i++) // loop the segments + + for (var j = 0; j < this.bandsDataSpin2[i].length; j++) { // loop the kpoints + polylinePoints = ''; + for (var k = 0; k < this.bandsDataSpin2[i][j].length; k++) { // loop the bands + polylinePoints+= ' '+this.xRel*this.bandsDataSpin2[i][j][k].x+ + ' '+this.transformY(this.bandsDataSpin2[i][j][k].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); + } + } + + + // Paint CBM and VBM + // console.log("Paint CBM and VBM: ", this.vbm); this.vbm.y = -1.01577992001999e-20/this.factor; + if (this.bandGapData !== undefined + // If there are two spins related data the CBM and VBM points aren't shown + /*&& this.bandsDataSpin2.length === 0*/) + { + + this.bandGapData.cbmDistances.forEach( distance => { + let x = this.xRel * distance; + let y = this.transformY(this.bandGapData.lower.energy/this.factor); + //console.log("hhhhhhhhh: ", x, y); + svg.addPoint(this.plotContent, x, y , 3, 'cbm-vbm-points'); + svg.addText(this.plotContent, x+4, y-6, 'CBM'); + }); + + + this.bandGapData.vbmDistances.forEach( distance => { + let x = this.xRel*distance; + let y = this.transformY(this.bandGapData.upper.energy/this.factor); + svg.addPoint(this.plotContent, x, y, 3, 'cbm-vbm-points'); + svg.addText(this.plotContent, x+4, y+14, 'VBM'); + }); + + /* + + let x = this.xRel*this.bandGapData.cbmDistance; + let y = this.transformY(this.bandGapData.lower.energy/this.factor); + //console.log("hhhhhhhhh: ", x, y); + svg.addPoint(this.plotContent, x, y , 3, 'cbm-vbm-points'); + svg.addText(this.plotContent, x+4, y-6, 'CBM'); + + x = this.xRel*this.bandGapData.vbmDistance; + y = this.transformY(this.bandGapData.upper.energy/this.factor); + svg.addPoint(this.plotContent, x, y, 3, 'cbm-vbm-points'); + svg.addText(this.plotContent, x+4, y+14, 'VBM'); + */ + } + + } + + +} + + +function kPointDistance(kPoints, position){ + let p0= kPoints[0]; + let p1= kPoints[position]; + let deltaX= p1[0] - p0[0]; + let deltaY= p1[1] - p0[1]; + let deltaZ= p1[2] - p0[2]; + return Math.sqrt(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ); +} + +function getSymbol(label){ + if (label === 'Gamma' || label === 'G') return 'Γ'; + else return label; +} + +// EXPORTS +module.exports = BSPlotter; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { - if (calcs.values().next().value.cell_volume === null){ // not bulk type volume of a calc null - quantities = ['lattice_a', 'lattice_b', 'lattice_c']; - labels = ['a (Å)', 'b (Å)', 'c (Å)']; - } +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + This is the base class that models a highly interactive plotter. + It is inherited by several classes implementing specifc plotters. + This plotter implements zoom and y-axis offset + */ + + + + + +let svg = __webpack_require__(2); + +const xmlns="http://www.w3.org/2000/svg"; +const xlink="http://www.w3.org/1999/xlink"; + + +class InteractivePlotterBase{ + + constructor(margins = {left: 20, right: 0, top: 0, bottom: 20}) { + this.margins= margins; + this.svg = document.createElementNS(xmlns, "svg"); + + this.parentElement= null; + this.plotContent = null; + this.axisGroup = null; + this.yAxisLabelsGroup = null; + this.yLabelText = null; // If null, y axis label and numbers are not painted + this.noDataGroup = null; + + this.yZoom = 1; // Initial zoom + this.yOffset = 0; // Initial y offset = 0 + this.repaintListener = null; + this.nodataLabel = null; + + this.outOfRangeColorActivated = true; + } + + + attach(element, width, height){ + this.parentElement= element; + this.parentElement.appendChild(this.svg); + this.width = (width !== undefined ? width : this.parentElement.clientWidth); + this.height = (height !== undefined ? height : this.svg.width); + this.svg.setAttribute("width", this.width); + this.svg.setAttribute("height", this.height); + this.plotRangeX = this.width - this.margins.left - this.margins.right; + this.plotRangeY = this.height - this.margins.top - this.margins.bottom; + + // y axis area (zoomable) + this.yAxisArea = document.createElementNS(xmlns, "svg"); + this.svg.appendChild(this.yAxisArea); + let yAxisAreaWidth = this.margins.left; + this.plotAreaHeight = this.height - this.margins.bottom - this.margins.top; + this.yAxisArea.setAttribute("width", yAxisAreaWidth); + this.yAxisArea.setAttribute("height", this.plotAreaHeight);//-OVERLAP_CORRECTOR); + this.yAxisArea.setAttribute("x", 0); + this.yAxisArea.setAttribute("y", 0);//OVERLAP_CORRECTOR); + + // SVG plot area window + this.plotArea = document.createElementNS(xmlns, "svg"); + this.svg.appendChild(this.plotArea); + this.plotAreaWidth = this.width - this.margins.left - this.margins.right; + this.plotArea.setAttribute("width", this.plotAreaWidth); + this.plotArea.setAttribute("height", this.plotAreaHeight); + this.plotArea.setAttribute("x", this.margins.left); + this.plotArea.setAttribute("y", this.margins.top); + + // Rect filling plot area in order to support styles and events + this.plotAreaBg = svg.addRect(this.plotArea, 0, 0, this.plotAreaWidth, this.plotAreaHeight); + this.plotAreaBg.setAttribute('class', 'moveable-plot'); + this.plotAreaBg.setAttribute('opacity', 0.0); + + this. _events(); + } + + + isAttached(){ + return this.parentElement !== null; + } + + + setAxisRangeAndLabels(xLabel, xMin, xMax, yLabel, yMinInit, yMaxInit, + yMin, yMax, yLabelGap, decimals = 2){ + this.xLabel= xLabel; + this.xMin = xMin; + this.xMax = xMax; + this.yMinInit= yMinInit; + this.yMaxInit= yMaxInit; + this.yMin= yMin; + this.yMax= yMax; + this.yLabelGapInit = yLabelGap; + this.xRel= this.plotRangeX/(this.xMax-this.xMin); + this.yRel= this.plotRangeY/(this.yMaxInit-this.yMinInit); + + this._resetAxisGroup(); + + // Draw axes + svg.addLine(this.axisGroup, 0,0,this.plotRangeX,0, 'main-axis'); + svg.addLine(this.axisGroup, 0,0,0,-this.plotRangeY ,'main-axis'); + svg.addLine(this.axisGroup, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY, 'main-axis'); + svg.addLine(this.axisGroup, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY, 'main-axis'); + + // Paint x and y axes labels + if (yLabel !== null){ + this.yLabelText = svg.addText(this.svg, 0, 0, yLabel, 'middle', 'axis-steps-big'); + this.yLabelText.setAttribute('transform','translate(15,'+(this.plotRangeY/2+this.margins.top)+') rotate(-90)'); + } + if (xLabel !== null) + svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom-12, this.xLabel, 'middle', 'axis-steps-big'); + + // initialize y axis steps (dynamic construction) container + this._resetYAxisLabelGroup(); + + // transformY precalcultation 1 + this.precalculation_1 = this.plotAreaHeight + this.yMinInit*this.yRel; + this.precalculation_2 = this.precalculation_1;//- this.yOffset // when this quantity changes + } + + + _events(){ + + this.plotArea.addEventListener('wheel', (e) => { + e.preventDefault(); + if (e.deltaY > 0 && this.yZoom > 0.5 ) this.yZoom -= 0.2; + else if (e.deltaY < 0 && this.yZoom < 2) this.yZoom += 0.2; + this.repaint(); + if (this.repaintListener !== null) + this.repaintListener(this.yZoom, this.yOffset); + }); + + let initPosY; + this.plotArea.addEventListener('mousedown', (e) => { + e.preventDefault(); + initPosY = e.clientY + this.yOffset; + //console.log('mousedown: e.clientY + this.yOffset', e.clientY, this.yOffset); + this.plotArea.addEventListener('mousemove', moveListener); + + this.plotArea.addEventListener('mouseup', (e) => { + this.plotArea.removeEventListener('mousemove', moveListener); + }); + this.plotArea.addEventListener('mouseout', (e) => { + this.plotArea.removeEventListener('mousemove', moveListener); + }); + }); + + let self = this; + function moveListener(e) { + //console.log('Y offset:', e.clientY - initPosY); + // Bad if (initPosY - e.clientY > this.yMax || initPosY - e.clientY < this.yMin) + self.yOffset = initPosY - e.clientY ; + self.precalculation_2 = self.precalculation_1 - self.yOffset; + self.repaint(); + if (self.repaintListener !== null) + self.repaintListener(self.yZoom, self.yOffset); + } + } + + + setYZoomAndOffset(yZoom, yOffset){ + this.yZoom = yZoom; + this.yOffset = yOffset; + this.precalculation_2 = this.precalculation_1 - this.yOffset; + } + + + setExternalYAxisMax(externalYAxisMax){ + this.externalYAxisMax = externalYAxisMax; + } + + + getYAxisMax(){ + return this.yAxisMax; + } + + + repaint(){ + // repaint Y axis + this._resetYAxisLabelGroup(); + + let yLabelGap; + if (this.yZoom > 1) yLabelGap = this.yLabelGapInit/5; + else yLabelGap = this.yLabelGapInit; + + let min = Math.floor(this.yMin/yLabelGap)*yLabelGap; + let max = Math.ceil(this.yMax/yLabelGap)*yLabelGap; + this.yAxisMax = max; + if (this.externalYAxisMax !== undefined) max = this.externalYAxisMax; + + if (this.yLabelText !== null) { + for (let i = min; i < max+1; i = i + yLabelGap) { + svg.addLine(this.yAxisLabelsGroup, this.margins.left, + this.transformY(i), this.margins.left-3, this.transformY(i)); + svg.addText(this.yAxisLabelsGroup, this.margins.left-5, + this.transformY(i)+5, i, 'end', 'axis-steps'); + } + } + + // repaint plot content + this._resetPlotContent(); + + // Out of range areas + if (this.outOfRangeColorActivated){ + let area = svg.addRect(this.plotContent, 0, this.transformY(this.yMax)-2*this.plotAreaHeight, this.plotAreaWidth, 2*this.plotAreaHeight); + area.setAttribute('class', 'out-of-range'); + let area1 = svg.addRect(this.plotContent, 0, this.transformY(this.yMin), this.plotAreaWidth, 2*this.plotAreaHeight); + area1.setAttribute('class', 'out-of-range'); + } + + // Zero line + svg.addLine(this.plotContent, 0, this.transformY(0), this.plotRangeX, + this.transformY(0), 'zeroline'); + + // repaint data lines + this.repaintData(min, max); + + // Add the top layer: rect for events + this.plotArea.removeChild(this.plotAreaBg); + this.plotArea.appendChild(this.plotAreaBg); + } + + + setRepaintListener(listener) { + this.repaintListener = listener; + } + + + transformY(y){ + // Precalculation usage + // this.plotAreaHeight -y*this.yZoom*this.yRel + this.yMinInit*this.yRel - this.yOffset + // -y*this.yZoom*this.yRel (calculated here) this.plotAreaHeight + this.yMinInit*this.yRel - this.yOffset + let result = -y*this.yZoom*this.yRel + this.precalculation_2; + if (result > 10000) throw 'Y coordinate too large'; + return result; + } + + + _resetPlotContent(){ + // TRy to delete with textContent property + if (this.plotContent !== null) + this.plotArea.removeChild(this.plotContent); + this.plotContent = document.createElementNS(xmlns, "g"); + this.plotArea.appendChild(this.plotContent); + } - 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)+ - ' <span style="font-size: 0.9em">['+stats.min.toFixed(decimals) - +' , '+stats.max.toFixed(decimals)+']</span>'; - - quantitiesMap.set(quantity, stats); - }); - return quantitiesMap; - } + _resetAxisGroup(){ + if (this.axisGroup !== null) + this.svg.removeChild(this.axisGroup); + this.axisGroup = document.createElementNS(xmlns, 'g'); + this.svg.appendChild(this.axisGroup); - function toAngstromMinus3(density){ - return (density*1e-30).toFixed(3)+' Å<sup>-3</sup>'; - } + // The y axis is inverted so the y coordinate has to be multiplied by -1 + this.axisGroup.setAttribute("transform", 'matrix(1 0 0 1 '+ + this.margins.left+' '+(this.height - this.margins.bottom)+')'); + } - function getMaterialTitle(data, html){ - let title; - title = getSubscriptedFormula(data.formula_reduced); - if (html !== undefined && html ===false) title = data.formula_reduced; + _resetYAxisLabelGroup(){ + if (this.yLabelText === null) return; - if (data.space_group_number !== null) - title += ' - space group '+data.space_group_number; - //return '<span style="font-size: 0.9em">'+title+' </span>'; - return title; - } + if (this.yAxisLabelsGroup !== null) + this.yAxisArea.removeChild(this.yAxisLabelsGroup); + this.yAxisLabelsGroup = document.createElementNS(xmlns, 'g'); + this.yAxisArea.appendChild(this.yAxisLabelsGroup); + //this.yAxisLabelsGroup.setAttribute("transform", 'matrix(1 0 0 1 0 -'+OVERLAP_CORRECTOR+')'); + } - function getMinMaxHTML(calcs,prop){ - let propArray= []; - calcs.forEach( (calc) => { - propArray.push(calc[prop]); - }); + _reset(){ + this.yZoom = 1; // Initial zoom + this.yOffset = 0; // Initial y offset = 0 - return '('+Math.min.apply(null, propArray)+' ... '+Math.max.apply(null, propArray)+')'; - } + // initialize plot content (dynamic construction) container + this._resetPlotContent(); + + if (this.yLabelText !== null){ + this.svg.removeChild(this.yLabelText); + this.yLabelText = null; + } + + if (this.noDataGroup !== null){ + this.svg.removeChild(this.noDataGroup); + this.noDataGroup = null; + } + } - function generateDiagramSteps(maxVal){ + setNoData(){ + this._resetYAxisLabelGroup(); + this._resetPlotContent(); + this._resetAxisGroup(); + if (this.noDataGroup === null){ + this.noDataGroup = document.createElementNS(xmlns, 'g'); + this.svg.appendChild(this.noDataGroup); + svg.addRect(this.noDataGroup, 0, 0, this.width, this.height); + this.noDataGroup.setAttribute('fill', '#EEE'); + svg.addText(this.noDataGroup, this.width/2, this.height/2+10, + 'NO DATA', 'middle', 'nodata'); + } - 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; - //console.log('util.generateDiagramSteps ',exp, maxVal/d, factor); - let ceil = Math.ceil(maxVal*factor/d); - let stepArray = []; - for (var i = 0; i <= d; i++) { - stepArray[i] = ceil*i/factor; - } - //console.log('stepArray '+stepArray); - exp = (exp < 0 ? 0 : exp); - return [stepArray, exp]; - } +} +// EXPORTS +module.exports = InteractivePlotterBase; - /* - function addBandGapData(calcJson, bsData){ - if (calcJson.band_gap > 0) { - bsData.bandGapData = {}; - bsData.bandGapData.cbmEnergy = calcJson.band_gap_lower_energy; - bsData.bandGapData.cbmKpt = getNumberArray(calcJson.band_gap_lower_kpt); - bsData.bandGapData.vbmEnergy = calcJson.band_gap_upper_energy; - bsData.bandGapData.vbmKpt = getNumberArray(calcJson.band_gap_upper_kpt); - } - }*/ +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { - /* - function is2DSystem_temporary_patch(){ - //console.log('TEMPORARY PATCH is2DSystem:', DataStore.getMaterialData()); - return DataStore.getMaterialData().system_type === '2D'; - } - */ - - - module.exports = { - materialId, - MAT_VIEW: MAT_VIEW, - IMAGE_DIR: IMAGE_DIR, - USER_COOKIE_DOMAIN, - ELEMENTS: ELEMENTS, - setAuthRequestHeader, - getUserData, - getServerLocation, - authServerReq, - serverReq, - serverReqPOST, - getSearchURL: getSearchURL, - getSuggestionURL, - getMaterialURL: getMaterialURL, - getMaterialCalcURL: getMaterialCalcURL, - getMaterialXsURL: getMaterialXsURL, - getCalcEnergiesURL: getCalcEnergiesURL, - getFlaggingURL, - setBrowserHashPath: setBrowserHashPath, - loadLib: loadLib, - getNumberArray: getNumberArray, - getCellDataForViewer: getCellDataForViewer, - //FERMI_SURFACE_URL: FERMI_SURFACE_URL, - J2eV: J2eV, - eV2J, - //getBandGapStatsValue: getBandGapStatsValue, - m2Angstrom: m2Angstrom, - getLatticeAnglesValues: getLatticeAnglesValues, - rad2degree: rad2degree, - m3ToAngstrom3: m3ToAngstrom3, - getQuantityStatsMap: getQuantityStatsMap, - toAngstromMinus3, - getMaterialTitle, - getMinMaxHTML: getMinMaxHTML, - getSubscriptedFormula: getSubscriptedFormula, - getAverage, - generateDiagramSteps, - //is2DSystem_temporary_patch - //addBandGapData - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - This file create and holds the application data models. - It stores the data loaded from the backend (immutable). - In addition it creates app life-time entities for convenience - */ - - - let materialData; - - function setMaterialData(dataFromAPI){ - materialData = dataFromAPI; - } + /* + This file implements the Material Module of the application. + It's a UI component container that displays the selected material information. + It's complex because of the amount and diversity of material info available. - function getMaterialData(){ - return materialData; - } + In this file other two inner components of the module are implemented + (by convenience): + StructureViewerWrapper and DropDown (used exclusively in StructureViewerWrapper) + */ - let calcs; - let calcMap = new Map(); +let svg = __webpack_require__(2); +let util = __webpack_require__(0); +let NavTree = __webpack_require__(21); +let Overview = __webpack_require__(22); +let StructureDetails = __webpack_require__(23); +let ElectronicStructDetails = __webpack_require__(25); +let MethodologyDetails = __webpack_require__(27); +let ThermalPropsDetails = __webpack_require__(29); +let ElasticConstDetails = __webpack_require__(32); +let DataStore = __webpack_require__(1); +let LoadingPopup = __webpack_require__(4); + + +// Store material data at this level (material model) ? +let materialData = null, materialCalcsData = null; + +// Store the default marked tree leafs +let markedTreeLeafs = { eStruct: null, thermalProps: null }; + + +class MaterialMod{ + + constructor(){ + this.element = document.createElement('div'); + this.element.setAttribute("id",'material-module'); + + this.overview = new Overview(); + this.overview.attachAndSetEvents(this.element); + + this.navTree= new NavTree(); + + this.structureViewer = null; + + // They are created hidden + // Structure detail section init + this.structureDetails = new StructureDetails(); + this.structureDetails.attachAndSetEvents(this.element); + + // Electronic structure detail section init + this.electronicStructDetails = new ElectronicStructDetails(); + this.electronicStructDetails.attachAndSetEvents(this.element); + + // Methodology detail section init + this.methodologyDetails = new MethodologyDetails(); + this.methodologyDetails.attachAndSetEvents(this.element); + + this.thermalDetails = new ThermalPropsDetails(); + this.thermalDetails.attachAndSetEvents(this.element); + + this.elasticDetails = new ElasticConstDetails(); + this.elasticDetails.attachAndSetEvents(this.element); + + this.currentDetailView= null; + } + + + setMaterialView(data){ + if (materialData === null || materialData.id !== parseInt(data.id) ){ + this._loadMaterial(data.id, data.view); + // Reset the checkboxes to the initial checked value (new material) + if (this.structureViewer !== null){ + this.structureViewer.axisCheckbox.checked = true; + this.structureViewer.bondsCheckbox.checked = true; + } + + }else{ + this._setView(data.view); + document.querySelector('title').innerHTML = 'NOMAD Encyclopedia - '+ + util.getMaterialTitle(DataStore.getMaterialData(), false); + } + } + + + getCurrentPageStatus(){ + + let tempeStructCalcs = null; + if (this.currentDetailView === null){ + tempeStructCalcs = this.overview.getEStructChosenCalcs(); + } + + return { + pageId: this.currentDetailViewId, + markedLeaf: this.navTree.getMarkedLeaf(), + eStructCalcs: tempeStructCalcs + }; + } + - function setCalculations(calcsFromAPI){ - calcs = calcsFromAPI; - for (let i = 0; i < calcs.length; i++) - calcMap.set(calcs[i].id, calcs[i]); - } + _setView(view){ + //console.log('FFFFF setMaterialView: '+view); + // Hide the current view + if (this.currentDetailView === null) + this.overview.element.style.display= 'none'; + else + this.currentDetailView.element.style.display= 'none'; - function getCalculations(){ - return calcs; - } + if (typeof view === 'undefined'){ // Overview view + this.currentDetailView = null; + this.currentDetailViewId = null; + this.overview.setVisible();//this.overview.element.style.display= 'block'; + this.navTree.setLeafMarkedListener(undefined); + this._setCellViewer(this.overview.vizBox); + }else{ + this.currentDetailViewId = view; + this._setDetailView(util.MAT_VIEW[view]); + } + } + + + _setCellViewer(hostElement){ + //console.log('_setCellViewer', this.structureViewer,materialData); + if (this.structureViewer === null){ + this.structureViewer = new StructureViewerWrapper(hostElement);//CellViewer(hostElement);// + if (materialData !== null){ // Case: landing at e. structure details page + this.structureViewer.load(util.getCellDataForViewer(materialData)); + this.structureViewer.setMaterialId(materialData.id); + } + }else + this.structureViewer.changeHostElement(hostElement); + } + + + _setDetailView(view) { +// console.log('FFFFF _setDetailView: '+view); + + if (view === util.MAT_VIEW.structure){ + this.currentDetailView= this.structureDetails; + this.navTree.showCalcsGraphDataAvalability(false); + this.navTree.setHeight(250); + this.navTree.setMarkedLeafIfNoneMarked(null); // Set the first leaf marked + + }else if (view === util.MAT_VIEW.electronicstruct){ + this.currentDetailView= this.electronicStructDetails; + this.navTree.showCalcsGraphDataAvalability(true); + this.navTree.setHeight(400); + this.navTree.setMarkedLeafIfNoneMarked(markedTreeLeafs.eStruct); + + }else if (view === util.MAT_VIEW.methodology){ // Methodology + this.currentDetailView = this.methodologyDetails; + + }else if (view === util.MAT_VIEW.thermalprops){ // Thermal properties + this.currentDetailView= this.thermalDetails; + this.navTree.showCalcsGraphDataAvalability(true); + this.navTree.setHeight(600); + this.navTree.setMarkedLeafIfNoneMarked(markedTreeLeafs.thermalProps); + } + + /* To be implemented + else{ // Elastic constants + this.currentDetailView = this.elasticDetails; + this.navTree.showCalcsGraphDataAvalability(false); + this.navTree.setHeight(600); + this.navTree.setMarkedLeafIfNoneMarked(null); + }*/ + + this.currentDetailView.setVisible();//this.currentDetailView.element.style.display= 'block'; + + if (view === util.MAT_VIEW.structure) + this._setCellViewer(this.structureDetails.vizBox); + + //this.currentDetailView.setMaterialData(materialData); //// WHY DOES It do always? + if (view !== util.MAT_VIEW.methodology) + this.currentDetailView.attachNavTree(this.navTree); + + this.currentDetailView.updateSelection(this.navTree.getTreeSelectedCalcs()); + + this.currentDetailView.updateMarkedLeaf(this.navTree.getMarkedLeaf()); + + this.navTree.setTreeSelectionListener( leafIds => { + this.currentDetailView.updateSelection(leafIds); + }); + this.navTree.setLeafMarkedListener( leafId => { + this.currentDetailView.updateMarkedLeaf(leafId); + }); + } + + + _loadMaterial(matId,view){ + + this.overview.element.style.visibility= 'hidden'; + LoadingPopup.show(); + util.serverReq(util.getMaterialURL(matId), e1 => { + + materialData= JSON.parse(e1.target.response); + util.materialId = materialData.id; + + if (e1.target.status === 200){ + + util.serverReq(util.getMaterialXsURL('elements',matId), e2 => { + materialData.elements= JSON.parse(e2.target.response).results; + + util.serverReq(util.getMaterialXsURL('cells',matId), e3 => { + let cells= JSON.parse(e3.target.response).results; + if (!cells[0].is_primitive) materialData.cell= cells[0]; + else materialData.cell= cells[1]; + + DataStore.setMaterialData(materialData); + //console.log("CELLS: "+JSON.stringify(materialData.cell)); + document.querySelector('title').innerHTML = + 'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false); + this.overview.setMaterialData(); + this.structureDetails.setMaterialData(); + this.electronicStructDetails.setMaterialData(); + this.methodologyDetails.setMaterialData(); + this.thermalDetails.setMaterialData(); + this.elasticDetails.setMaterialData(); + //console.log("MATDATA LOADED: "); + if (this.structureViewer !== null) + this.structureViewer.load(util.getCellDataForViewer(materialData)); + + util.serverReq(util.getMaterialXsURL('calculations',matId), e4 => { + DataStore.setCalculations(JSON.parse(e4.target.response).results); + + util.serverReq(util.getMaterialXsURL('groups', matId), e5 => { + DataStore.setGroups(JSON.parse(e5.target.response).groups); + + let name = (materialData.material_name === null ? + materialData.formula : materialData.material_name); + this.navTree.build(name); + this.overview.setCalcsData(markedTreeLeafs); + this.navTree.selectAll(); + //console.log('MaterialMod - thermalPropsDetailsTreeLeaf: ', markedTreeLeafs.thermalProps); + + this._setView(view); + this.overview.element.style.visibility= 'visible'; + LoadingPopup.hide(); + }); + }); + }); + }); + + }else{ // Error - First request + } + + }); + } +} // class MaterialMod + + +// Wrapper the structure viewer to be properly integrated +// on the UI components showing it +class StructureViewerWrapper{ + + constructor(hostElement){ + this.hostElement = hostElement; + + this.viewer = new StructureViewer(hostElement); + //Get the focus -> this.viewer.renderer.domElement.setAttribute('tabindex', '0'); + + this.legendElement = document.createElement('div'); + this.legendElement.setAttribute('class', 'element-labels'); + this.legendElement.setAttribute('style', 'position: absolute; bottom: 50px; right: 0'); + this.hostElement.appendChild(this.legendElement); + + this.footerElement = document.createElement('div'); + this.footerElement.setAttribute('class', 'structure-viewer-legend'); + this.hostElement.appendChild(this.footerElement); + this.footerElement.innerHTML = ` + + <div style="float: left; padding-right: 12px" > + <input type="checkbox" class="show-axis" checked> Show axis + </div> + + <div style="float: left; padding-right: 18px" > + <input type="checkbox" class="show-bonds" checked> Show bonds + </div> + + <div style="float: left; position:relative;" > + <img class="view-reset" style="cursor: pointer;" height="18px" + src="${util.IMAGE_DIR}reset.svg" /> + <div class="view-reset-tooltip" > Set original view </div> + </div> + + <!-- + <div class="view-reset-tooltip" style="float: left; display: none; font-size: 0.8em;" > + Set original <br> view + </div> + --> + + <div class="vr-download" style="float: right"> </div> + + <div style="clear: both;"></div> + `; + + this.axisCheckbox = this.footerElement.querySelector('.show-axis'); + this.axisCheckbox.addEventListener('click', e => { + this.viewer.toggleLatticeParameters(this.axisCheckbox.checked); + }); + + this.bondsCheckbox = this.footerElement.querySelector('.show-bonds'); + this.bondsCheckbox.addEventListener('click', e => { + this.viewer.toggleBonds(this.bondsCheckbox.checked); + }); + + this.labelsContainer = this.hostElement.querySelector('.element-labels'); + + this.vrLinksContainer = this.footerElement.querySelector('.vr-download'); + this.vrDropDown = new DropDown(); + this.vrLinksContainer.appendChild(this.vrDropDown.element); + + let resetButton = this.hostElement.querySelector('.view-reset'); + resetButton.addEventListener('click', e => this.viewer.reset() ); + + resetButton.addEventListener('mouseover', e => { + this.hostElement.querySelector('.view-reset-tooltip').style.display = 'block'; + }); + resetButton.addEventListener('mouseout', e => { + this.hostElement.querySelector('.view-reset-tooltip').style.display = 'none'; + }); + } + + load(data){ + this.viewer.load(data); + this.createElementLegend(); + } + + setMaterialId(id){ + this.vrDropDown.setMaterialId(id); + } + + changeHostElement(hostElement){ + if (this.hostElement !== hostElement){ + this.hostElement.removeChild(this.legendElement); + this.hostElement.removeChild(this.footerElement); + this.hostElement = hostElement; + this.viewer.changeHostElement(hostElement); + this.hostElement.appendChild(this.legendElement); + this.hostElement.appendChild(this.footerElement); + } + } + + + createElementLegend() { + // Empty the old legend + this.labelsContainer.innerHTML = ''; + + let elements = this.viewer.elements; + // Create a list of elements + let elementArray = []; + for (let property in elements) { + if (elements.hasOwnProperty(property)) + elementArray.push([property, elements[property][0], elements[property][1]]); + } + + // Sort by name + elementArray.sort(function (a, b) { + if (a[0] < b[0]) return -1; + if (a[0] > b[0]) return 1; + return 0; + }); + + let svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svgElement.setAttribute("width", 50); + svgElement.setAttribute("height", elementArray.length*25); + + this.labelsContainer.appendChild(svgElement); + + for (let i = 0; i < elementArray.length; ++i) { + let elementName = elementArray[i][0]; + //let elementColor = "#" + elementArray[i][1].toString(16); Lauri's bugfixing feb-2017 + let elementColor = elementArray[i][1].toString(16); + let nZeros = 6 - elementColor.length; + let prefix = "#" + Array(nZeros + 1).join("0"); + elementColor = prefix + elementColor; + + //let elementRadius = 25 * elementArray[i][2]; + //elementRadius = Math.min(100, elementRadius); + //elementRadius = Math.max(30, elementRadius); + svg.addCircle(svgElement, 10, 25*i+12, 8, elementColor, "#777", 1); + svg.addText(svgElement, 24, 25*i+18, elementName, 'start', 'structure-viewer-legend-labels'); + } + } +} // class StructureViewerWrapper + + +// used exclusively in StructureViewerWrapper +class DropDown{ + + constructor(materialId){ + this.folded = true; + this.element = document.createElement('div'); + //this.element.className = className; + + this.element.innerHTML+=` + <div > + <span style=" vertical-align: 30%; ">Virtual Reality files</span> + <img style="cursor: pointer" src="${util.IMAGE_DIR}folded.png" /> + </div> + + <div class="vr-download-panel" style="position: relative; display: none"> + + </div> + `; + + // Focus related properties (in order to hide the box when the user click out) + this.element.tabIndex = '0'; // enabled the support of focusing + this.element.style.outline = 'none'; // The outline is not shown when it gains the focus + + this.foldingPanel = this.element.querySelector('.vr-download-panel'); + this.foldBtn = this.element.querySelector('img'); + + this.foldBtn.addEventListener('click', e => { + this.folded = !this.folded; + this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' : + util.IMAGE_DIR+'unfolded.png'); + //this.foldBtn.className = (this.folded ? 'on' : 'off'); + this.foldingPanel.style.display = (this.folded ? 'none' : 'block'); + }); + + this.element.addEventListener('blur' , e => { + this.folded = true; + this.foldBtn.src = util.IMAGE_DIR+'folded.png'; + this.foldingPanel.style.display = 'none'; + }); + + //this.cellViewer.toggleLatticeParameters(false); + } + + setMaterialId(id){ + this.foldingPanel.innerHTML = ` + <div class="vr-download-panel-unfolded" style="width: 210px;"> + <div style="padding: 5px; "> + <a href="http://nomad.srv.lrz.de/cgi-bin/NOMAD/material?${id}">Get VR file</a> + </div> + <br> + <div style="padding-bottom: 5px; ">Visualization tools for specific devices:</div> + + <div style="padding: 5px; "> + <a href="http://nomad.srv.lrz.de/NOMAD/NOMADViveT-Setup.exe">HTC Vive</a> + </div> + <!-- + <div style="padding: 5px; "> + <a href="http://nomad.srv.lrz.de/NOMAD/NOMADGearvrT.apk">Samsung GearVR</a> + </div> + --> + <div style="padding: 5px; "> + <a target="_blank" href="https://play.google.com/store/apps/details?id=com.lrz.nomadvr">Google Cardboard</a> + </div> + + </div> + ` + } + +} // class DropDown + + +// EXPORTS +module.exports = MaterialMod; + + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { - function getCalc(calcId){ - return calcMap.get(calcId); - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - let groups; + /* + This component implements a generic switch/toggle button + */ - function processCalcGroups(groupsFromAPI){ - let methodCalcsMap = new Map(); - groupsFromAPI.forEach( (group, i) => { - if (group.group_type !== 'method'){ - let calcsMins; - if (methodCalcsMap.has(group.method_hash) ){ - calcsMins = methodCalcsMap.get(group.method_hash); - }else{ - calcsMins = {'ids': [], 'minEnergies': []}; - methodCalcsMap.set(group.method_hash, calcsMins); - } - calcsMins.ids.push(group.representative_calculation_id); - calcsMins.minEnergies.push(group.energy_minimum); - } - }); +let util = __webpack_require__(0); - let methodRepresentativeMap = new Map(); - methodCalcsMap.forEach( (calcsData, methodHash) => { - let minVal = Math.min.apply(null, calcsData.minEnergies); - let index = calcsData.minEnergies.indexOf(minVal); - methodRepresentativeMap.set(methodHash, calcsData.ids[index]); - }); +class SwitchComponent { - let calcsInGroups = new Map(); - groupsFromAPI.forEach( (group, i) => { - if (group.group_type !== 'method'){ - let groupData = { - 'calcs': new Set(), - 'method_representative': methodRepresentativeMap.get(group.method_hash) - }; - calcsInGroups.set( - getGroupTypeCode(group)+group.representative_calculation_id, groupData); - //let calcSet = new Set(); // The representative is (not?) in the set - group.calculations_list.forEach( calcId => groupData.calcs.add(calcId) ); - } - }); - return calcsInGroups; - } + constructor(imageBasePath) { - function setGroups(groupsFromAPI){ - groups = processCalcGroups(groupsFromAPI); - } + this.off = true; - function getGroups(){ - return groups; - } + this.element = document.createElement('span'); + this.element.innerHTML+=` + <img src="${imageBasePath}_off.png" width="24px" + style="margin-bottom: -1px; cursor: pointer"/> + `; + this.image = this.element.querySelector('img'); - function getGroupTypeCode(group){ - if (group.group_type === 'equation of state') return 'eos'; - else if (group.group_type === 'convergence') return 'par'; - } + this.element.addEventListener('click', e => { + this.off = !this.off; + let imagePath = (this.off ? imageBasePath+'_off' : imageBasePath); + this.image.setAttribute('src',imagePath+'.png'); + this.listener(this.off); + }); + } - function getGroupType(leafId){ - let code = leafId.substring(0,3); - if (code === 'eos') return 'equation of state'; - else if (code === 'par') return 'convergence'; - else return null; - } - function isGroup(leafId){//*** ********** REFACTOR - return (leafId.substring(0,3) === 'eos' || leafId.substring(0,3) === 'par'); - } + setListener(listener){ + this.listener = listener; + } +} - function getCalcReprIntId(leafId){ - if (isGroup(leafId)) return parseInt(leafId.substring(3)); - else return parseInt(leafId); - } +// EXPORTS +module.exports = SwitchComponent; - function isInAnyGroup(calcId){ - let thereIs = false; - groups.forEach( (groupData, groupId) => { - //console.log('isInAnyGroup', calcId, groupData.calcs); - if (groupData.calcs.has(calcId)) thereIs = true;//return true; - }); - return thereIs; - } - function isInAnyNotDisabledGroup(calcId){ - let thereIs = false; +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { - groups.forEach( (groupData, groupId) => { - let representative = getCalc(getCalcReprIntId(groupId)); - //let codeNameTrimed= representative.code_name.trim(); - //console.log('isInAnyGroup', calcId, groupData.calcs); +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + Implements the DOS plotter. + */ - /***** Exception: disable grouping for some codes - if (codeNameTrimed !== 'VASP' && codeNameTrimed !== 'FHI-aims' - && groupData.calcs.has(calcId)) thereIs = true; */ - if (groupData.calcs.has(calcId)) thereIs = true; - }); - return thereIs; - } - function getGroupLeafId(calcId){ - let leafId = null; - groups.forEach( (groupData, groupId) => { - //console.log('isInAnyGroup', calcId, groupData.calcs); - if (groupData.calcs.has(calcId)) leafId = groupId;//return true; - }); - //console.log('getGroupLeafId', leafId); - return leafId; - } - let hasThermalData, hasElecStructureData; +let InteractivePlotterBase = __webpack_require__(8); +let svg = __webpack_require__(2); +let util = __webpack_require__(0); + - /* - function hasThermalData(bool){ - hasThermalData = bool; - } +const E_MIN = -6; +const E_MAX = 11; +const E_FACTOR = 1.602176565e-19; +//const DOSVALUE_FACTOR = 1.602176565e-49; - function setHasThermalData(bool){ - hasThermalData = bool; - } - function hasElecStructureData(bool){ - hasThermalData = bool; - } +class DOSPlotter extends InteractivePlotterBase{ - function setHasElecStructureData(bool){ - hasElecStructureData = bool; - }*/ + constructor(margins) { + super(margins); + } + // detach if necessary and attach + attach(element, width, height){ + super.attach(element, width, height); + } - // EXPORTS - module.exports = { setMaterialData, getMaterialData, getCalculations, getCalc, - setCalculations, getGroups, setGroups, isGroup, getGroupType, - getCalcReprIntId, isInAnyGroup, isInAnyNotDisabledGroup, getGroupLeafId, - hasThermalData, hasElecStructureData}; + setPoints(points, codeName){ + let dosValueDivisor = E_FACTOR; + //if (codeName === 'VASP') dosValueDivisor = 1e-10*(E_FACTOR*E_FACTOR); -/***/ }, -/* 3 */ -/***/ function(module, exports) { + this.pointsSpin1 = []; + this.pointsSpin2 = []; + this._reset(); - - const Conf = {}; + let pSpin1= points.dos_values[0]; + let pSpin2 = null; + if (points.dos_values.length === 2) pSpin2 = points.dos_values[1]; + let pointsY= points.dos_energies; + let pointsXInPlotRange = []; + let pointsYInPlotRange = []; - Conf.GuestUserToken = - 'eyJhbGciOiJIUzI1NiIsImlhdCI6MTUyMzg4MDE1OSwiZXhwIjoxNjgxNTYwMTU5fQ.ey'+ - 'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4'; + for (var i = 0; i < pointsY.length; i++) { + let energy = pointsY[i]/E_FACTOR; + let dos_value_spin1 = pSpin1[i]/dosValueDivisor; + pointsXInPlotRange.push(dos_value_spin1); + if (pSpin2 !== null) pointsXInPlotRange.push(pSpin2[i]); + pointsYInPlotRange.push(energy); + //console.log('POINTS : ',pointsX[i], energy); + this.pointsSpin1.push({x: dos_value_spin1, y: energy}); + if (pSpin2 !== null) + this.pointsSpin2.push({x: pSpin2[i]/dosValueDivisor, y: energy}); + } + let maxDosVal = Math.max.apply(null, pointsXInPlotRange); + let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange); + let minEnergyVal = Math.min.apply(null, pointsYInPlotRange); - // EXPORTS - module.exports = Conf; + // x axis steps generation + let t = util.generateDiagramSteps(maxDosVal); + let xSteps = t[0], exp = t[1]; + this.setAxisRangeAndLabels(null, 0, xSteps[xSteps.length-1], 'Energy (eV)', + E_MIN, E_MAX, minEnergyVal, maxEnergyVal, 5); -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This component is the loading popup window - */ - - - "use strict"; - - let util = __webpack_require__(1); - - let loadingPopup = document.querySelector('#loading-popup'); - - function show(){ - let ttRect = loadingPopup.getBoundingClientRect(); - let leftPos = (window.innerWidth - ttRect.width)/2; - let topPos = (window.innerHeight -ttRect.height)/2; - loadingPopup.style.left = leftPos+'px'; - loadingPopup.style.top = (topPos-100)+'px'; - loadingPopup.style.visibility = 'visible'; - } + svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom, + 'DOS (states/eV/cell)', 'middle', 'axis-steps-big'); - function hide(){ - loadingPopup.style.visibility = 'hidden'; - } + // draw x axis steps + 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); + //console.log('step ',xSteps[i], stepX); + svg.addText(this.axisGroup, stepX, 13, + (i === 0 ? '0' : xSteps[i].toFixed(exp)),'middle', 'axis-steps-smaller'); + } - // EXPORTS - module.exports = { show, hide }; + this.repaint(); + } -/***/ }, -/* 5 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This component is the popup window (and the darkened background) with a form - inside used for users to report (flagging) errors on the calculations - */ - - "use strict"; - - let util = __webpack_require__(1); - let DataStore = __webpack_require__(2); - - // main DOM elements - let flaggingPopupBackground = document.querySelector('#flagging-form-popup-bg'); - let flaggingFormPopup = document.querySelector('#flagging-form-popup'); - flaggingFormPopup.innerHTML =` - - <div> <img src="img/cross.svg" height="12px" - style="float: right; cursor: pointer" /> - </div> - - <div class="form-wrapper"> - <div class="popup-title"> Error reporting</div> - <select id="flagging-category" name="category"> - <option value="">Select a category *</option> - <option value="structure">Structure</option> - <option value="electronicstruct">Electronic structure</option> - <option value="methodology">Methodology</option> - <option value="thermalprops">Thermal properties</option> - </select> - - <select id="flagging-subcategory" name="subcategory"> - - </select> - - <textarea id="subject" name="subject" style="height:200px" - placeholder="Write a short explanation about the error detected" ></textarea> - - <div id="form-validation-msg"> </div> - - <div style="display: flex; justify-content: space-evenly;"> - <button style="display: block">Send</button> - </div> - - - </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 validationMsg = flaggingFormPopup.querySelector('#form-validation-msg'); - let sendButton= flaggingFormPopup.querySelector('button'); - - let treeLeaf = null; - let overviewEStructCalcs = null; - - - function _setCurrentPage(pageId){ - - subcategoryField.innerHTML = ''; - - if (pageId === null){ - categoryField.disabled = false; - subcategoryField.style.display = 'none'; - - }else{ - categoryField.disabled = true; - subcategoryField.style.display = 'block'; - subcategoryField.appendChild(createOption('Choose the subcategory *', '')); - - switch (pageId) { - - case util.MAT_VIEW.structure: - categoryField.selectedIndex = 1; - subcategoryField.appendChild(createOption('Structure representation')); - subcategoryField.appendChild(createOption('Calculation tree')); - subcategoryField.appendChild(createOption('Summary')); - subcategoryField.appendChild(createOption('Specific calculation')); - break; - - case util.MAT_VIEW.electronicstruct: - categoryField.selectedIndex = 2; - subcategoryField.appendChild(createOption('Calculation tree')); - subcategoryField.appendChild(createOption('Summary')); - subcategoryField.appendChild(createOption('Band structure')); - subcategoryField.appendChild(createOption('DOS')); - subcategoryField.appendChild(createOption('Brillouin zone')); - break; - - case util.MAT_VIEW.methodology: - categoryField.selectedIndex = 3; - subcategoryField.style.display = 'none'; - break; - - case util.MAT_VIEW.thermalprops: - categoryField.selectedIndex = 4; - subcategoryField.appendChild(createOption('Calculation tree')); - subcategoryField.appendChild(createOption('Phonon dispersion')); - subcategoryField.appendChild(createOption('Phonon DOS')); - subcategoryField.appendChild(createOption('Specific heat')); - subcategoryField.appendChild(createOption('Helmholtz free energy')); - break; - } - } + repaintData(){ - } // function _setCurrentPage + let polylinePoints = ''; + for (var i = 0; i < this.pointsSpin1.length; i++) { + polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+ + ' '+this.transformY(this.pointsSpin1[i].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); + polylinePoints = ''; + for (var i = 0; i < this.pointsSpin2.length; i++) { + polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+ + ' '+this.transformY(this.pointsSpin2[i].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); + } - function show(pageStatus){ - //console.log('pageStatus : ',pageStatus); - treeLeaf = pageStatus.markedLeaf; - overviewEStructCalcs = pageStatus.eStructCalcs; - // Show/hide some dropdown list options - eStructOption.style.display = (DataStore.hasElecStructureData ? 'block' : 'none'); - thermalOption.style.display = (DataStore.hasThermalData ? 'block' : 'none'); + setYAxisLabelsVisibility(value){ + if (this.yAxisLabelsGroup !== null) + this.yAxisLabelsGroup.style.visibility = (value ? 'visible' : 'hidden'); + } - _setCurrentPage(pageStatus.pageId); +} - let ttRect = flaggingFormPopup.getBoundingClientRect(); - let leftPos = (window.innerWidth - ttRect.width)/2; - let topPos = (window.innerHeight -ttRect.height)/2; - flaggingFormPopup.style.left = leftPos+'px'; - flaggingFormPopup.style.top = (topPos-20)+'px'; - flaggingFormPopup.style.visibility = 'visible'; - flaggingPopupBackground.style.visibility = 'visible'; - } +// EXPORTS +module.exports = DOSPlotter; - function hide(){ - flaggingPopupBackground.style.visibility = 'hidden'; - flaggingFormPopup.style.visibility = 'hidden'; - // reset UI - categoryField.selectedIndex = 0; - subcategoryField.selectedIndex = 0; - flaggingFormPopup.querySelector('textarea').value = ''; - validationMsg.innerHTML = ''; - } +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; - function createOption(text, value){ - let opt = document.createElement('option'); - opt.value = (value === undefined ? text : value); - opt.innerHTML = text; - return opt; - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - closeButton.addEventListener('click', e => { - hide(); - }); + /* + Specific Heat plotter implementation + */ - sendButton.addEventListener('click', e => { - let categoryChosen = categoryField.options[categoryField.selectedIndex]; - if (!categoryField.disabled && categoryChosen.value === ''){ // Overview case - validationMsg.innerHTML = 'The category fields must be set'; +let svg = __webpack_require__(2); +let PlotterBase = __webpack_require__(5); - }else if (categoryField.disabled && subcategoryField.value === '' // Detaisl 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().id; - let userdata = util.getUserData(); +class HeatCapPlotter extends PlotterBase{ + constructor() { + super({left: 50, right: 16, top: 10, bottom: 32}); + this.tooltip; + } - 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; + setData(data){ + //console.log(JSON.stringify(data));// + this.clear(); + // up to 600K data is taken + let indexOf600K = data.temperature.indexOf(600)+1; + let values = data.value.slice(0, indexOf600K); + let temperatures = data.temperature.slice(0, indexOf600K); - 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); + let yMaxValue = Math.max.apply(null, values); + //console.log('maxValue: ',maxValue);// + this.setRangeAndLabels('T (K)', 0, 600, 'Cv (J/K/kg)', 0, Math.ceil(yMaxValue/200)*200); + this.drawAxis(4, 4, 0); - }else{ // Details pages - descriptionText += '\\n\\n**Category:** '+categoryChosen.text; + let polylinePoints = ''; + temperatures.forEach( (t, i) => { + let y = values[i];///1e-25; + polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(y - this.yMin); + }); + svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1'); + } - 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; - } - } +} - descriptionText += '\\n\\n**User text:** '+textareaText; - let queryJson =`{ - "title": "${titleText}", - "description": "${descriptionText}"}`; - console.log('Flagging POST request Json: ',queryJson);//, util.getFlaggingURL()); +// EXPORTS +module.exports = HeatCapPlotter; - util.serverReqPOST(util.getFlaggingURL(), queryJson, e => { - console.log('response',e); - if (e.target.status === 200) hide(); +/***/ }), +/* 13 */ +/***/ (function(module, exports) { + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This class implements the bar to select calculations on a material + */ + + +class CalcSelectorBar{ + + constructor(className, width){ + this.first = true; + this.last = false; + this.element = document.createElement('div'); + this.element.className = className; + if (width !== undefined) this.element.style.width = width; + this.element.innerHTML = ` + <div class="prev-sel-btn" style="float: left; width: 20%;"> + <div style="padding-left: 10%;"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.069 11.872" width="20px"> + <path transform="scale(0.7) translate(-346.291 -664.481)" + d="M356.36,666.024l-1.544-1.544-8.525,8.513,8.493,8.447,1.544-1.544-6.8-6.9Z" /> + </svg> + </div> + </div> + <div class="calc-sel-text" style="float: left; width: 60%;"> + NOT Calculation + </div> + <div class="next-sel-btn" style="float: right; width: 20%;"> + <div style="padding-right: 10%;"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="-10.069 -11.872 10.069 11.872" width="20px"> + <g transform="rotate(180) scale(0.7)"> + <path d="M10.069,1.544,8.525,0,0,8.513,8.493,16.96l1.544-1.544-6.8-6.9Z"/> + </g> + </svg> + </div> + </div> + <div style="clear: both;"></div> + `; + + this.prevBtn = this.element.querySelector('.prev-sel-btn'); + this.prevIcon = this.element.querySelector('.prev-sel-btn path'); + this.calcSelectorTxt = this.element.querySelector('.calc-sel-text'); + this.nextBtn = this.element.querySelector('.next-sel-btn'); + this.nextIcon = this.element.querySelector('.next-sel-btn path'); + this._styleButtons(); + this._events(); + } + + + _events() { + this.prevBtn.addEventListener( "click", e => { + e.preventDefault(); + if (this.first) return; + /*** repensar esto es problematico porque necesita poder ser configurado desde fuera **/ + //if (this.last) this.last = false; + this.first = this.prevListener(); + this.last = false; + this._styleButtons(); + }); + + this.nextBtn.addEventListener( "click", e => { + e.preventDefault(); + if (this.last) return; + //if (this.first) this.first = false; + // this.last = this.nextListener(); + this.first = false; + this.last = this.nextListener(); + this._styleButtons(); + }); + } + + + _styleButtons(){ + this.prevIcon.setAttribute("class", + 'calc-selector-icon'+(this.first ? '-disabled' : '')); + this.nextIcon.setAttribute("class", + 'calc-selector-icon'+(this.last ? '-disabled' : '')); + } + + + setPrevListener(listener){ + this.prevListener = listener; + } + + + setNextListener(listener){ + this.nextListener = listener; + } + + + setState(text, first, last){ + this.calcSelectorTxt.innerHTML = text; + this.first = first; + this.last = last; + this._styleButtons(); + } + +} + + +// EXPORTS +module.exports = CalcSelectorBar; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + The StatsViewer class implements a graphical UI component composed of + a points plotter and a frecuency graph. + (classes defined in this file MainGraph and FrequencyGraph) + */ + + + + +let svg = __webpack_require__(2); +let PlotterBase = __webpack_require__(5); + + +class MainGraph extends PlotterBase{ + + constructor() { + super({left: 54, right: 20, top: 20, bottom: 30}); + this.tooltip; + } + + + drawPoints(points){ + for (let i = 0; i < points.length; i++) { + //console.log('drawPoint '+this.xRel, i, this.yRel, points[i]); + let pointElement = svg.addPoint(this.plotArea, + this.xRel*i, -this.yRel*(points[i] - this.yMin), 2, 'stats-viewer-point'); + + pointElement.addEventListener('mouseover', (e) => { + this.tooltip = svg.addText(this.plotArea, e.target.getBBox().x+6, + e.target.getBBox().y-4, (points[i]).toFixed(2), 'start', 'tooltip'); + /*getBBox() Rect data (x, y, w, h) of the element were the event happendd, + coordenate system: the parent element coordinate system */ + }); + pointElement.addEventListener('mouseout', (e) => { + svg.removeElement(this.tooltip); }); - - } - - }); - - - - // EXPORTS - module.exports = { show, hide }; - - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the design pattern pub/sub, which allows one module - to broadcast messages to other modules. - */ - - "use strict"; - - var messages = new Map(); - var lastUid = -1; - - /** - * subscribe( message, func ) -> String - * - message (String): The message to subscribe to - * - func (Function): The function to call when a new message is published - * Subscribes the passed function to the passed message. Every returned token - * is unique and should be stored if you need to unsubscribe - **/ - function subscribe( message, func ){ - //console.log('SUBSCRIBING Message '+message); - if ( typeof func !== 'function'){ - return false; - } - // message is not registered yet - if ( !messages.has( message ) ){ - messages.set( message, new Map()); - } - // forcing token as String, to allow for future expansions without breaking usage - // and allow for easy use as key names for the 'messages' object - var token = 'uid_' + String(++lastUid); - messages.get(message).set(token, func); //messages[message][token] = func; - //print(); - // return token for unsubscribing - return token; - } - - - function publish( message, data){ - - var hasSubscribers = messages.has( message ) - && (messages.get( message ).size > 0); - - if ( !hasSubscribers ){ - return false; } + } - var deliver = function (){ - //deliverMessage(message, data); - var subscribers = messages.get(message); - //console.log('DELIVERING Message '+message); - subscribers.forEach(function(func, token) { - func(data); - //console.log('EXE funct '+ func); - }); - }; +} - setTimeout( deliver, 0 ); // async - return true; - } +class FrequencyGraph extends PlotterBase{ - function print(){ - console.log('PubSub data: '); - messages.forEach(function(functions, msg) { - console.log(msg + ': '); + constructor() { + super({left: 4, right: 10, top: 20, bottom: 30}); + } - functions.forEach(function(func, token) { - console.log(' '+token + ': ' + func); - }); + drawBars(points, yMin, yRange){ + let ranges = [0,0,0,0,0,0,0,0,0,0]; + points.forEach(point => { + let rangeIndex = Math.floor(((point-yMin)/yRange)*10); + if (rangeIndex > 9) rangeIndex = 9; + ranges[rangeIndex] += 1; }); - } - - // EXPORTS - module.exports = { subscribe: subscribe, publish: publish }; - - -/***/ }, -/* 7 */ -/***/ function(module, exports) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the app Routing system: the feature that allows - the page navigation in a single-page environment. - */ - - - "use strict"; - - let routes = new Map(); - - - function add(route, func){ - routes.set(route, func); - } - - - window.addEventListener("hashchange", route); - - function route(){ - let hashPath= document.location.hash.substring(2); - let command, param, subparam; - - // remove the ending / - if (hashPath.lastIndexOf('/') === (hashPath.length-1)) - hashPath = hashPath.substring(0,hashPath.length-1); - - 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); - - if (routes.has(command)) { - routes.get(command)(param, subparam); - } - }; - - - function print(){ - console.log('Router data: '); - routes.forEach(function(func, url) { - console.log(url + ': ' + func); + ranges.forEach((value, index) => { + let yBar= (index + 0.5)*this.yRel; + svg.addLine(this.plotArea, 0, -yBar, value*this.xRel, -yBar, 'bar'); }); - } - - - // EXPORTS - module.exports = { add: add, route: route }; - - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the Material Module of the application. - It's a UI component container that displays the selected material information. - It's complex because of the amount and diversity of material info available. - - In this file other two inner components of the module are implemented - (by convenience): - StructureViewerWrapper and DropDown (used exclusively in StructureViewerWrapper) - */ - - - let svg = __webpack_require__(9); - let util = __webpack_require__(1); - let NavTree = __webpack_require__(10); - let Overview = __webpack_require__(11); - let StructureDetails = __webpack_require__(19); - let ElectronicStructDetails = __webpack_require__(24); - let MethodologyDetails = __webpack_require__(26); - let ThermalPropsDetails = __webpack_require__(28); - let ElasticConstDetails = __webpack_require__(31); - let DataStore = __webpack_require__(2); - let LoadingPopup = __webpack_require__(4); - - - // Store material data at this level (material model) ? - let materialData = null, materialCalcsData = null; - - // Store the default marked tree leafs - let markedTreeLeafs = { eStruct: null, thermalProps: null }; - - - class MaterialMod{ - - constructor(){ - this.element = document.createElement('div'); - this.element.setAttribute("id",'material-module'); - - this.overview = new Overview(); - this.overview.attachAndSetEvents(this.element); - - this.navTree= new NavTree(); - - this.structureViewer = null; - - // They are created hidden - // Structure detail section init - this.structureDetails = new StructureDetails(); - this.structureDetails.attachAndSetEvents(this.element); - - // Electronic structure detail section init - this.electronicStructDetails = new ElectronicStructDetails(); - this.electronicStructDetails.attachAndSetEvents(this.element); - - // Methodology detail section init - this.methodologyDetails = new MethodologyDetails(); - this.methodologyDetails.attachAndSetEvents(this.element); - - this.thermalDetails = new ThermalPropsDetails(); - this.thermalDetails.attachAndSetEvents(this.element); - - this.elasticDetails = new ElasticConstDetails(); - this.elasticDetails.attachAndSetEvents(this.element); - - this.currentDetailView= null; - } - - - setMaterialView(data){ - if (materialData === null || materialData.id !== parseInt(data.id) ){ - this._loadMaterial(data.id, data.view); - // Reset the checkboxes to the initial checked value (new material) - if (this.structureViewer !== null){ - this.structureViewer.axisCheckbox.checked = true; - this.structureViewer.bondsCheckbox.checked = true; - } - - }else{ - this._setView(data.view); - document.querySelector('title').innerHTML = 'NOMAD Encyclopedia - '+ - util.getMaterialTitle(DataStore.getMaterialData(), false); - } - } - - - getCurrentPageStatus(){ - - let tempeStructCalcs = null; - if (this.currentDetailView === null){ - tempeStructCalcs = this.overview.getEStructChosenCalcs(); - } - - return { - pageId: this.currentDetailViewId, - markedLeaf: this.navTree.getMarkedLeaf(), - eStructCalcs: tempeStructCalcs - }; - } - - - _setView(view){ - //console.log('FFFFF setMaterialView: '+view); - // Hide the current view - if (this.currentDetailView === null) - this.overview.element.style.display= 'none'; - else - this.currentDetailView.element.style.display= 'none'; - - if (typeof view === 'undefined'){ // Overview view - this.currentDetailView = null; - this.currentDetailViewId = null; - this.overview.setVisible();//this.overview.element.style.display= 'block'; - this.navTree.setLeafMarkedListener(undefined); - this._setCellViewer(this.overview.vizBox); - }else{ - this.currentDetailViewId = view; - this._setDetailView(util.MAT_VIEW[view]); - } - } - - - _setCellViewer(hostElement){ - //console.log('_setCellViewer', this.structureViewer,materialData); - if (this.structureViewer === null){ - this.structureViewer = new StructureViewerWrapper(hostElement);//CellViewer(hostElement);// - if (materialData !== null){ // Case: landing at e. structure details page - this.structureViewer.load(util.getCellDataForViewer(materialData)); - this.structureViewer.setMaterialId(materialData.id); - } - }else - this.structureViewer.changeHostElement(hostElement); - } - - - _setDetailView(view) { - // console.log('FFFFF _setDetailView: '+view); - - if (view === util.MAT_VIEW.structure){ - this.currentDetailView= this.structureDetails; - this.navTree.showCalcsGraphDataAvalability(false); - this.navTree.setHeight(250); - this.navTree.setMarkedLeafIfNoneMarked(null); // Set the first leaf marked - - }else if (view === util.MAT_VIEW.electronicstruct){ - this.currentDetailView= this.electronicStructDetails; - this.navTree.showCalcsGraphDataAvalability(true); - this.navTree.setHeight(400); - this.navTree.setMarkedLeafIfNoneMarked(markedTreeLeafs.eStruct); - - }else if (view === util.MAT_VIEW.methodology){ // Methodology - this.currentDetailView = this.methodologyDetails; - - }else if (view === util.MAT_VIEW.thermalprops){ // Thermal properties - this.currentDetailView= this.thermalDetails; - this.navTree.showCalcsGraphDataAvalability(true); - this.navTree.setHeight(600); - this.navTree.setMarkedLeafIfNoneMarked(markedTreeLeafs.thermalProps); - } - - /* To be implemented - else{ // Elastic constants - this.currentDetailView = this.elasticDetails; - this.navTree.showCalcsGraphDataAvalability(false); - this.navTree.setHeight(600); - this.navTree.setMarkedLeafIfNoneMarked(null); - }*/ - - this.currentDetailView.setVisible();//this.currentDetailView.element.style.display= 'block'; - - if (view === util.MAT_VIEW.structure) - this._setCellViewer(this.structureDetails.vizBox); - - //this.currentDetailView.setMaterialData(materialData); //// WHY DOES It do always? - if (view !== util.MAT_VIEW.methodology) - this.currentDetailView.attachNavTree(this.navTree); - - this.currentDetailView.updateSelection(this.navTree.getTreeSelectedCalcs()); - - this.currentDetailView.updateMarkedLeaf(this.navTree.getMarkedLeaf()); - - this.navTree.setTreeSelectionListener( leafIds => { - this.currentDetailView.updateSelection(leafIds); - }); - this.navTree.setLeafMarkedListener( leafId => { - this.currentDetailView.updateMarkedLeaf(leafId); - }); - } - - - _loadMaterial(matId,view){ - - this.overview.element.style.visibility= 'hidden'; - LoadingPopup.show(); - util.serverReq(util.getMaterialURL(matId), e1 => { - - materialData= JSON.parse(e1.target.response); - util.materialId = materialData.id; - - if (e1.target.status === 200){ - - util.serverReq(util.getMaterialXsURL('elements',matId), e2 => { - materialData.elements= JSON.parse(e2.target.response).results; + } +} - util.serverReq(util.getMaterialXsURL('cells',matId), e3 => { - let cells= JSON.parse(e3.target.response).results; - if (!cells[0].is_primitive) materialData.cell= cells[0]; - else materialData.cell= cells[1]; - DataStore.setMaterialData(materialData); - //console.log("CELLS: "+JSON.stringify(materialData.cell)); - document.querySelector('title').innerHTML = - 'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false); - this.overview.setMaterialData(); - this.structureDetails.setMaterialData(); - this.electronicStructDetails.setMaterialData(); - this.methodologyDetails.setMaterialData(); - this.thermalDetails.setMaterialData(); - this.elasticDetails.setMaterialData(); - //console.log("MATDATA LOADED: "); - if (this.structureViewer !== null) - this.structureViewer.load(util.getCellDataForViewer(materialData)); - - util.serverReq(util.getMaterialXsURL('calculations',matId), e4 => { - DataStore.setCalculations(JSON.parse(e4.target.response).results); - - util.serverReq(util.getMaterialXsURL('groups', matId), e5 => { - DataStore.setGroups(JSON.parse(e5.target.response).groups); - - let name = (materialData.material_name === null ? - materialData.formula : materialData.material_name); - this.navTree.build(name); - this.overview.setCalcsData(markedTreeLeafs); - this.navTree.selectAll(); - //console.log('MaterialMod - thermalPropsDetailsTreeLeaf: ', markedTreeLeafs.thermalProps); - - this._setView(view); - this.overview.element.style.visibility= 'visible'; - LoadingPopup.hide(); - }); - }); - }); - }); - - }else{ // Error - First request - } - - }); - } - } // class MaterialMod - - - // Wrapper the structure viewer to be properly integrated - // on the UI components showing it - class StructureViewerWrapper{ - - constructor(hostElement){ - this.hostElement = hostElement; - - this.viewer = new StructureViewer(hostElement); - //Get the focus -> this.viewer.renderer.domElement.setAttribute('tabindex', '0'); - - this.legendElement = document.createElement('div'); - this.legendElement.setAttribute('class', 'element-labels'); - this.legendElement.setAttribute('style', 'position: absolute; bottom: 50px; right: 0'); - this.hostElement.appendChild(this.legendElement); - - this.footerElement = document.createElement('div'); - this.footerElement.setAttribute('class', 'structure-viewer-legend'); - this.hostElement.appendChild(this.footerElement); - this.footerElement.innerHTML = ` - - <div style="float: left; padding-right: 12px" > - <input type="checkbox" class="show-axis" checked> Show axis - </div> - - <div style="float: left; padding-right: 18px" > - <input type="checkbox" class="show-bonds" checked> Show bonds - </div> - - <div style="float: left; position:relative;" > - <img class="view-reset" style="cursor: pointer;" height="18px" - src="${util.IMAGE_DIR}reset.svg" /> - <div class="view-reset-tooltip" > Set original view </div> - </div> - - <!-- - <div class="view-reset-tooltip" style="float: left; display: none; font-size: 0.8em;" > - Set original <br> view - </div> - --> - - <div class="vr-download" style="float: right"> </div> - - <div style="clear: both;"></div> - `; - - this.axisCheckbox = this.footerElement.querySelector('.show-axis'); - this.axisCheckbox.addEventListener('click', e => { - this.viewer.toggleLatticeParameters(this.axisCheckbox.checked); - }); - - this.bondsCheckbox = this.footerElement.querySelector('.show-bonds'); - this.bondsCheckbox.addEventListener('click', e => { - this.viewer.toggleBonds(this.bondsCheckbox.checked); - }); - - this.labelsContainer = this.hostElement.querySelector('.element-labels'); - - this.vrLinksContainer = this.footerElement.querySelector('.vr-download'); - this.vrDropDown = new DropDown(); - this.vrLinksContainer.appendChild(this.vrDropDown.element); - - let resetButton = this.hostElement.querySelector('.view-reset'); - resetButton.addEventListener('click', e => this.viewer.reset() ); - - resetButton.addEventListener('mouseover', e => { - this.hostElement.querySelector('.view-reset-tooltip').style.display = 'block'; - }); - resetButton.addEventListener('mouseout', e => { - this.hostElement.querySelector('.view-reset-tooltip').style.display = 'none'; - }); - } - - load(data){ - this.viewer.load(data); - this.createElementLegend(); - } - - setMaterialId(id){ - this.vrDropDown.setMaterialId(id); - } - - changeHostElement(hostElement){ - if (this.hostElement !== hostElement){ - this.hostElement.removeChild(this.legendElement); - this.hostElement.removeChild(this.footerElement); - this.hostElement = hostElement; - this.viewer.changeHostElement(hostElement); - this.hostElement.appendChild(this.legendElement); - this.hostElement.appendChild(this.footerElement); - } - } - - - createElementLegend() { - // Empty the old legend - this.labelsContainer.innerHTML = ''; - - let elements = this.viewer.elements; - // Create a list of elements - let elementArray = []; - for (let property in elements) { - if (elements.hasOwnProperty(property)) - elementArray.push([property, elements[property][0], elements[property][1]]); - } - - // Sort by name - elementArray.sort(function (a, b) { - if (a[0] < b[0]) return -1; - if (a[0] > b[0]) return 1; - return 0; - }); - - let svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - svgElement.setAttribute("width", 50); - svgElement.setAttribute("height", elementArray.length*25); - - this.labelsContainer.appendChild(svgElement); - - for (let i = 0; i < elementArray.length; ++i) { - let elementName = elementArray[i][0]; - //let elementColor = "#" + elementArray[i][1].toString(16); Lauri's bugfixing feb-2017 - let elementColor = elementArray[i][1].toString(16); - let nZeros = 6 - elementColor.length; - let prefix = "#" + Array(nZeros + 1).join("0"); - elementColor = prefix + elementColor; - - //let elementRadius = 25 * elementArray[i][2]; - //elementRadius = Math.min(100, elementRadius); - //elementRadius = Math.max(30, elementRadius); - svg.addCircle(svgElement, 10, 25*i+12, 8, elementColor, "#777", 1); - svg.addText(svgElement, 24, 25*i+18, elementName, 'start', 'structure-viewer-legend-labels'); - } - } - } // class StructureViewerWrapper - - - // used exclusively in StructureViewerWrapper - class DropDown{ - - constructor(materialId){ - this.folded = true; - this.element = document.createElement('div'); - //this.element.className = className; - - this.element.innerHTML+=` - <div > - <span style=" vertical-align: 30%; ">Virtual Reality files</span> - <img style="cursor: pointer" src="${util.IMAGE_DIR}folded.png" /> - </div> - - <div class="vr-download-panel" style="position: relative; display: none"> - - </div> - `; - - // Focus related properties (in order to hide the box when the user click out) - this.element.tabIndex = '0'; // enabled the support of focusing - this.element.style.outline = 'none'; // The outline is not shown when it gains the focus - - this.foldingPanel = this.element.querySelector('.vr-download-panel'); - this.foldBtn = this.element.querySelector('img'); - - this.foldBtn.addEventListener('click', e => { - this.folded = !this.folded; - this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' : - util.IMAGE_DIR+'unfolded.png'); - //this.foldBtn.className = (this.folded ? 'on' : 'off'); - this.foldingPanel.style.display = (this.folded ? 'none' : 'block'); - }); - - this.element.addEventListener('blur' , e => { - this.folded = true; - this.foldBtn.src = util.IMAGE_DIR+'folded.png'; - this.foldingPanel.style.display = 'none'; - }); - - //this.cellViewer.toggleLatticeParameters(false); - } - - setMaterialId(id){ - this.foldingPanel.innerHTML = ` - <div class="vr-download-panel-unfolded" style="width: 210px;"> - <div style="padding: 5px; "> - <a href="http://nomad.srv.lrz.de/cgi-bin/NOMAD/material?${id}">Get VR file</a> - </div> - <br> - <div style="padding-bottom: 5px; ">Visualization tools for specific devices:</div> - - <div style="padding: 5px; "> - <a href="http://nomad.srv.lrz.de/NOMAD/NOMADViveT-Setup.exe">HTC Vive</a> - </div> - <!-- - <div style="padding: 5px; "> - <a href="http://nomad.srv.lrz.de/NOMAD/NOMADGearvrT.apk">Samsung GearVR</a> - </div> - --> - <div style="padding: 5px; "> - <a target="_blank" href="https://play.google.com/store/apps/details?id=com.lrz.nomadvr">Google Cardboard</a> - </div> - - </div> - ` - } - - } // class DropDown - - - // EXPORTS - module.exports = MaterialMod; - - -/***/ }, -/* 9 */ -/***/ function(module, exports) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - SVG drawing util library - */ - - - "use strict"; - - const xmlns="http://www.w3.org/2000/svg"; - const xlink="http://www.w3.org/1999/xlink"; - - - function addPoint(parent, x, y, r, className) { - let e = document.createElementNS(xmlns, "circle"); - e.setAttribute("r", r); // e.setAttributeNS(null, "r", 5); - e.setAttribute("cx", x); - e.setAttribute("cy", y); - if (className !== undefined) e.setAttribute("class", className); - parent.appendChild(e); - return e; - } - - function addCircle(parent, x, y, r, fillColor, strokeColor, strokeWidth) { - let e = document.createElementNS(xmlns, "circle"); - e.setAttribute("r", r); // e.setAttributeNS(null, "r", 5); - e.setAttribute("cx", x); - e.setAttribute("cy", y); - e.setAttribute("fill", fillColor); - e.setAttribute("stroke", strokeColor); - e.setAttribute("stroke-width", strokeWidth); - parent.appendChild(e); - return e; - } - - - function addLine(parent, x1, y1, x2, y2, className) { - let e = document.createElementNS(xmlns, "line"); - e.setAttribute("x1", x1); - e.setAttribute("y1", y1); - e.setAttribute("x2", x2); - e.setAttribute("y2", y2); - if (className !== undefined) e.setAttribute("class", className); - //e.setAttribute("stroke-width", stroke); - parent.appendChild(e); - return e; - } - - - function addRect(parent, x, y, w, h) { - let e = document.createElementNS(xmlns, "rect"); - e.setAttribute("x", x); - e.setAttribute("y", y); - e.setAttribute("width", w); - e.setAttribute("height", h); - parent.appendChild(e); - return e; - } - - - - function addText(parent, x, y, text, textAnchor = 'start', className) { - let e = document.createElementNS(xmlns, "text"); - e.setAttribute("x", x); - e.setAttribute("y", y); - e.textContent= text; - //e.setAttribute("stroke", 'black'); - e.setAttribute("text-anchor", textAnchor); - if (className !== undefined) e.setAttribute("class", className); - parent.appendChild(e); - return e; - } - - - function addPolyline(parent, points, className) { - let e = document.createElementNS(xmlns, "polyline"); - e.setAttribute("points", points); - if (className !== undefined) e.setAttribute("class", className); - //e.setAttribute("stroke-width", stroke); - parent.appendChild(e); - } - - - function removeElement(element){ - element.parentElement.removeChild(element); - } - - - - // EXPORTS - module.exports = { - addPoint: addPoint, - addCircle, - addLine: addLine, - addRect: addRect, - addText: addText, - addPolyline: addPolyline, - removeElement: removeElement - }; - - -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - The NavTree class define the UI tree component showing the material calculations. - There is only one instance that is shared by several 'Details' views. - */ - - "use strict"; - - let util = __webpack_require__(1); - let DataStore = __webpack_require__(2); - - - - function buildCalcTree(calcs, calcsInGroups){ - - let calcTree = new Map(); - calcsInGroups.forEach( (groupData, groupId) => { - - let representative = DataStore.getCalc(DataStore.getCalcReprIntId(groupId)); - let codeNameTrimed= representative.code_name.trim(); - let functionalType = representative.functional_type; - - /***** Exception: disable grouping for some codes - if (codeNameTrimed !== 'VASP' && codeNameTrimed !== 'FHI-aims') */ - if (calcTree.has(functionalType)){ - let functionalMap= calcTree.get(functionalType); - - if (functionalMap.has(codeNameTrimed)){ - // Get an array and push a new calc - functionalMap.get(codeNameTrimed).push(groupId); - }else // New code name - functionalMap.set(codeNameTrimed,[groupId]); - - }else{ // New functional - let newFunctionalMap= new Map(); - newFunctionalMap.set(codeNameTrimed,[groupId]); - calcTree.set(functionalType,newFunctionalMap); - } - }); - - // Simple calcs are added to the tree - calcs.forEach( calc => { - //if (!DataStore.isInAnyGroup(calc.id)) addToCalcTree(calc, calc.id+''); - //****** Exception: disable grouping for some codes - if (!DataStore.isInAnyNotDisabledGroup(calc.id)) - addToCalcTree(calc, calc.id+''); - }); - - - return calcTree; - - function addToCalcTree(calc, id){ - let codeNameTrimed= calc.code_name.trim(); - let functionalType = calc.functional_type; - if (calcTree.has(functionalType)){ - let functionalMap= calcTree.get(functionalType); - - if (functionalMap.has(codeNameTrimed)){ - // Get an array and push a new calc - functionalMap.get(codeNameTrimed).push(id); - }else // New code name - functionalMap.set(codeNameTrimed,[id]); - - }else{ // New functional - let newFunctionalMap= new Map(); - newFunctionalMap.set(codeNameTrimed,[id]); - calcTree.set(functionalType,newFunctionalMap); - } - } - } - - - /* Maybe these functions (getNextNode, getPreviousNode) can be removed - searching by node class calc-l and node-selected. Think about this */ - - function getNextNode(nodeBox){ - let nextCalcNodeBox= nodeBox.nextElementSibling.nextElementSibling; - // console.log("nextCALC.getNextNode " +nextCalcNodeBox); - if (nextCalcNodeBox === null){ - - if (nodeBox.parentElement.nextElementSibling !== null) - nextCalcNodeBox= nodeBox.parentElement.nextElementSibling/*nextCodeNodeBox*/ - .nextElementSibling/*nextCodeBox*/.children[0]/*nextCalc*/; - - else if (nodeBox.parentElement.parentElement.nextElementSibling !== null) - nextCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/ - .nextElementSibling.nextElementSibling/*nextFunctionalBox*/ - .children[1]/*nextCodeBox*/.children[0]/*nextCalc*/; - else //reaching the final node - nextCalcNodeBox= null; - } - return nextCalcNodeBox; - } - - - function getPreviousNode(nodeBox){ - let prevCalcNodeBox; - if (nodeBox.previousElementSibling !== null) - prevCalcNodeBox= nodeBox.previousElementSibling.previousElementSibling; - // console.log("nextCALC.getNextNode " +nextCalcNodeBox); - else{ - - if (nodeBox.parentElement.previousElementSibling.previousElementSibling !== null) - prevCalcNodeBox= nodeBox.parentElement.previousElementSibling/*prevCodeNodeBox*/ - .previousElementSibling/*prevCodeBox*/.children[0]/*prevCalc*/; - else if (nodeBox.parentElement.parentElement.previousElementSibling.previousElementSibling !== null) - prevCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/ - .previousElementSibling.previousElementSibling/*prevFunctionalBox*/ - .lastElementChild/*prevCodeBox*/.lastElementChild.previousElementSibling/*prevCalc*/; - else //reaching the final node - prevCalcNodeBox= null; - } - return prevCalcNodeBox; - } - - - - class NavTree { - - constructor() { - this.selectedCalcs= new Set(); - - this.element = document.createElement('div'); - this.element.setAttribute('id','navigation-tree'); - this.parentElement= null; - this.markedNode = null; - this._events(); - } - - // detach if necessary and attach - attach(element){ - if (this.parentElement !== null) - this.parentElement.removeChild(this.element); - this.parentElement= element; - this.parentElement.appendChild(this.element); - } - - - build(materialName){ - // Reset - this.selectedCalcs.clear(); - this.markedNode = null; - this.element.innerHTML= ''; - this.calcsInGroups = DataStore.getGroups(); - - - function getNodeHTML(level, label, unfolded, counter = 0){ - let foldingValue= 'node-'+(unfolded ? 'unfolded' : 'folded'); - let counterTag= (counter === 0 ? '' : - '<span class="node-counter">('+counter+')</span>'); - return ` - <div class="${level}"> - <span class="${foldingValue}"></span> - <span class="node-checkbox"></span> - <span class="node-label" >${label}</span> - ${counterTag} - </div> - `; - } - - function getCalcGraphInfoAvalabilityHTML(calc){ - let html= ''; - if (calc.has_band_structure) html += '<span class="tooltip">B<span class="tooltiptext">Band structure</span></span> '; - if (calc.has_dos) html += '<span class="tooltip">D<span class="tooltiptext">Density of states</span></span> '; - if (calc.has_fermi_surface) html += '<span class="tooltip">F<span class="tooltiptext">Fermi surface</span></span>'; - if (calc.has_thermal_properties) html += '<span class="tooltip">T<span class="tooltiptext">Phonons</span></span>'; - - return ' <span class="calc-graph-aval">'+html+'</span>'; - } - - // Init map to store calculations data - let calcs = DataStore.getCalculations(); - - let tree= buildCalcTree(calcs, this.calcsInGroups); - let rootElement= document.createElement('div'); - this.element.appendChild(rootElement); - rootElement.innerHTML= getNodeHTML('material-l',materialName,true); - - let functionalLevelBox= document.createElement('div'); - rootElement.appendChild(functionalLevelBox); - - tree.forEach((codeMap, functionalName) => { - //console.log(codeMap + " " + value); - functionalLevelBox.innerHTML+= getNodeHTML('functional-l',functionalName,true); - - let codeLevelBox= document.createElement('div'); - functionalLevelBox.appendChild(codeLevelBox); - - codeMap.forEach( (calcArray, codeName) => { - codeLevelBox.innerHTML+= getNodeHTML('code-l', codeName, false, calcArray.length); - - let calcLevelBox= document.createElement('div'); - codeLevelBox.appendChild(calcLevelBox); - calcLevelBox.style.display= 'none'; - - for (var i = 0; i < calcArray.length; i++) { - let graphInfoAvalabilityHTML = getCalcGraphInfoAvalabilityHTML( - DataStore.getCalc(DataStore.getCalcReprIntId(calcArray[i]))); - - let calcIcon = '', calcNumber = ''; - if (DataStore.isGroup(calcArray[i])){ - calcIcon = '<img class="folder-icon" src="'+util.IMAGE_DIR+'folder.png" />' - calcNumber = '('+this.calcsInGroups.get(calcArray[i]).calcs.size+')'; - } - - - calcLevelBox.innerHTML += ` - <div class="calc-l" data-calc-id="${calcArray[i]}" > - <span></span> - <span class="node-checkbox"></span> - - <span class="node-label" > - ${calcIcon} ${calcArray[i]} ${calcNumber} ${graphInfoAvalabilityHTML} - </span> - - <div style="float: right; padding: 1px 10px 0 0; display: none;"> - <img src="${util.IMAGE_DIR}next.png" /> - </div> - - </div> - <div> </div>`; - } - }); - }); - } // build method - - - selectAll(initMarkedLeafId){ - let materialNodeBox= this.element.children[0].children[0]; - this._recursiveNodeSelection(materialNodeBox, true); - keepTreeIntegrity(materialNodeBox, true); - // No calc marked - } - - - getMarkedLeaf(){ - if (this.markedNode === null) return null; - else return this.markedNode.getAttribute('data-calc-id'); - } - - - setMarkedLeafIfNoneMarked(leafId){ // If leafId === null first node selected - if (this.getMarkedLeaf() === null){ // If none marked - if (leafId === null) this._markFirstSelectedNode(); - else{ - let nodeBox = this.element.querySelector('div[data-calc-id="'+leafId+'"]'); - this._setMarkedCalc(nodeBox); - } - } - } - - - - _events() { - this.element.addEventListener('click',(e) => { - let classString = e.target.className; - - // drop down/up event - if (classString.indexOf('folded') >= 0){ - this._foldTreeNode(e.target); - - // descendant selection/deselection event - }else if ((classString.indexOf('node-checkbox') >= 0)){ - - let selectMode= (e.target.parentElement.className.indexOf('selected') < 0); - this._recursiveNodeSelection(e.target.parentElement, selectMode); - keepTreeIntegrity(e.target.parentElement, selectMode); - this.treeSelectionListener(this.selectedCalcs); - this._keepCalcMarked(selectMode); //if (this.calcMarked) - - }else if (/*this.calcMarked && */(classString.indexOf('node-label') >= 0 ) - && (e.target.parentElement.className === 'calc-l node-selected')){ - this._setMarkedCalc(e.target.parentElement/*nodeBox*/); - } - }); - - } - - - _foldTreeNode(dropDowmElement){ - let siblingElement= dropDowmElement.parentElement.nextElementSibling; - let classString = dropDowmElement.className; - - if (classString.indexOf('-folded') >= 0) { - dropDowmElement.className= dropDowmElement.className.replace('folded','unfolded'); - siblingElement.style.display= 'block'; - }else { - dropDowmElement.className= dropDowmElement.className.replace('unfolded','folded'); - siblingElement.style.display= 'none'; - } - } - - - _recursiveNodeSelection(nodeBox, select){ - - let nodeCheckBox= nodeBox.children[1]; - if (select){ - nodeBox.className += ' node-selected'; - }else{ // deselect - let index= nodeBox.className.indexOf(' node-selected'); - nodeBox.className= nodeBox.className.substring(0,index); - } - - if (nodeBox.className.indexOf('calc-l') >= 0) { // leaf node - let id= nodeBox.getAttribute('data-calc-id');//let id= parseInt(nodeBox.getAttribute('data-calc-id')); - if (select) this.selectedCalcs.add(id); - else this.selectedCalcs.delete(id); - - }else { // Not leaf node - - let nextLevelBox = nodeBox.nextElementSibling; // next levelBox - // Two children per banch: the first one is the node label box - // and the second one the next level with the descendants - for (let i = 0; i < nextLevelBox.children.length; i++ ) { - this._recursiveNodeSelection(nextLevelBox.children[i++], select); - } - } - } - - - - setTreeSelectionListener(listener){ - this.treeSelectionListener= listener; - } - - - setLeafMarkedListener(listener){ - this.leafMarkedListener = listener; - } - - - getTreeSelectedCalcs(){ - return this.selectedCalcs; - } - - - _keepCalcMarked(select){ - - if (select && (this.markedNode === null)){ - this._markFirstSelectedNode(); - - }else if (!select){ - let id= parseInt(this.markedNode.getAttribute('data-calc-id')); - if (this.selectedCalcs.size === 0){ - this.markedNode.className= this.markedNode.className.replace('-marked',''); - this.markedNode= null; - this.leafMarkedListener(null); - }else if (!this.selectedCalcs.has(id)){ - this._markFirstSelectedNode(); - } - } - } // _keepCalcMarked - - - _markFirstSelectedNode(){ - let calcNodeBoxes = this.element.getElementsByClassName('calc-l'); - for (var i = 0; i < calcNodeBoxes.length; i++) - if (calcNodeBoxes[i].className.indexOf('node-selected') >= 0){ - this._setMarkedCalc(calcNodeBoxes[i]); - return; - } - } - - - _setMarkedCalc(nodeBox){ - if (this.markedNode !== null){ - this.markedNode.className= this.markedNode.className.replace('-marked',''); - this.markedNode.querySelector('div').style.display = 'none'; - let folderIcon = this.markedNode.querySelector('.folder-icon'); - if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder.png'; - } - - nodeBox.className += '-marked'; - let folderIcon = nodeBox.querySelector('.folder-icon'); - if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder-sel.png'; - nodeBox.querySelector('div').style.display = 'block'; - this.markedNode= nodeBox; - - // The parent tree node is unfolded in order to show the leaf selected - let foldingElement = nodeBox.parentElement.previousElementSibling.firstElementChild; - if (foldingElement.className === 'node-folded'){ - foldingElement.className = 'node-unfolded'; - foldingElement.parentElement.nextElementSibling.style.display= 'block'; - } - - if (this.leafMarkedListener !== undefined) - this.leafMarkedListener(nodeBox.getAttribute('data-calc-id')); - } - - showCalcsGraphDataAvalability(bool){ - let elements= this.element.getElementsByClassName('calc-graph-aval'); - for (var i = 0; i < elements.length; i++) - elements[i].style.display= (bool ? 'inline' : 'none'); - } - - setHeight(heightPx){ - this.element.style.height = heightPx+'px'; - } - - } // class NavTree - - - - function keepTreeIntegrity(nodeBox, select){ - - if (nodeBox.className.indexOf('material-l') >= 0) return; - let levelBox= nodeBox.parentElement; - - for (let i = 0; i < levelBox.children.length; i++ ) { - let siblingNodeBox = levelBox.children[i++]; - if (siblingNodeBox !== nodeBox && - siblingNodeBox/*.children[1]*/.className.indexOf('selected') < 0) - return; - } - let parentNodeBox= levelBox.previousElementSibling; - if (select){ - parentNodeBox.className += ' node-selected'; - }else{ - let index= parentNodeBox.className.indexOf(' node-selected'); - parentNodeBox.className= parentNodeBox.className.substring(0,index); - } - keepTreeIntegrity(parentNodeBox, select); - } - - - - - // EXPORTS - module.exports = NavTree; - - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the Overview view component in the Material Module. - */ - - - "use strict"; - - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let LoadingPopup = __webpack_require__(4); - - let BSPlotter = __webpack_require__(14); - let DOSPlotter = __webpack_require__(16); - let HeatCapPlotter = __webpack_require__(17); - let MaterialMod = __webpack_require__(8); - let DataStore = __webpack_require__(2); - - const ELEMENT_INCLUDED_MSG = 'ELEMENT ALREADY INCLUDED'; - - - class Overview { - - constructor() { - - this.element = document.createElement('div'); - this.element.setAttribute('id','overview'); - this.materialId; - this.element.innerHTML=` - - <div class="material-title"> - </div> - - <div style="float: left; width: 40%;"> - - <div id="structure-ov" class="view-box"> - <div class="title">Structure - <img style="float: right" class="to-detail" src="img/more.svg" /> - <div style="clear: both;"></div> - </div> - - <div class="viz-box" style="height: 260px; position: relative"></div> - - <div class="footer"> - <div ><b><span>System type</span></b>: - <span class="system-type-field" ></span> - </div> - <div class="space-group-field" style="display: none"> - <b><span info-sys-data="space-group">Space group</span></b>: - <span class="space-group-value" ></span> - </div> - <div class="structure-type-field" style="display: none"> - <b><span info-sys-data="structure-type">Structure type</span></b>: - <span class="structure-type-value" ></span> - </div> - </div> - </div> - - - <!-- ***** Elastic Constants Box - - <div id="elastic-ov" class="view-box"> - <div class="title">Elastic constants - <img style="float: right" class="to-detail" src="img/more.svg" /> - <div style="clear: both;"></div> - </div> - - <div class="info-fields"> - Not analyzed yet - </div> - - </div> - --> - - - <div id="methodology-ov" class="view-box"> - <div class="title">Methodology - <img style="float: right" class="to-detail" src="img/more.svg" /> - <div style="clear: both;"></div> - </div> - - <div class="info-fields"> - - <div class="info-fields-label" > Available calculations </div> - - <div style="float: left; width: 45%" > - <b><span info-sys-data="functional-type">Functional</span></b> - <div class="functional-field" > </div> - </div> - <div style="float: right; width: 45%" > - <b><span info-sys-data="code-name">Code</span></b> - <div class="code-field"> </div> - </div> - <div style="clear: both;"></div> - </div> - - </div> - - </div> - - <div style="float: right; width: 60%;"> - - <div id="e-structure-ov" class="view-box" > <!--style="height: 610px; "--> - <div class="title">Electronic structure - <img style="float: right" class="to-detail" src="img/more.svg" /> - <div style="clear: both;"></div> - </div> - - <div > <!-- style="margin: 12% 0; " --> - - <div style="float: left; width: 60%; "> - <div style="padding: 20px 0 20px 30px"> - <div class="info-fields-label"> - <span info-sys-data="band-structure">Band structure</span> - </div> - <div> - <div id="band-plotter" > </div> - </div> - - <div class="footer-bs-calc"></div> - </div> - </div> - - <div style="float: left; width: 40%; "> - <div style="padding: 20px 30px 20px 60px"> - <div class="info-fields-label"> - <span info-sys-data="DOS">DOS</span> - </div> - - <div> - <div id="dos-plotter" > </div> - </div> - <div class="footer-dos-calc"></div> - </div> - </div> - - - <div style="clear: both;"></div> - - <div class="spin-legend" style="font-size: 0.9em; padding: 6px 30px 10px; display: none"> - <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg> - Spin <span style='font-size: 1.1em'>⇧</span> - - <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg> - Spin <span style='font-size: 1.1em'>⇩</span> - </div> - - </div> - - <!-- - <div class="footer"> - <b>Band gap</b>: <span class="e-struct-field" ></span> - </div> - --> - </div> - - <div id="thermal-props-ov" class="view-box" > - <div class="title">Vibrational and thermal properties - <img style="float: right" class="to-detail thermal-props" src="img/more.svg" /> - <div style="clear: both;"></div> - </div> - - <div style="padding: 36px; "> - <div class="info-fields-label"> - <span info-sys-data="heat-capacity-cv">Specific heat</span> - </div> - - - <div> - <div id="heat-plotter" > </div> - </div> - <div class="footer-heat-calc" style="text-align: center"></div> - </div> - - </div> - - </div> - - <div style="clear: both;"></div> - `; - - this.materialTitle= this.element.getElementsByClassName('material-title')[0]; - - this.systemType= this.element.querySelector('.system-type-field'); - this.spaceGroupField = this.element.querySelector('.space-group-field'); - this.spaceGroupValue = this.element.querySelector('.space-group-value'); - this.structTypeField= this.element.querySelector('.structure-type-field'); - this.structTypeValue= this.element.querySelector('.structure-type-value'); - //this.band_gap = this.element.getElementsByClassName('e-struct-field')[0]; - - //fields= this.element.getElementsByClassName('method-field'); - this.functional= this.element.querySelector('.functional-field');//fields[0]; - this.code= this.element.querySelector('.code-field');//fields[1]; - - let fields= this.element.getElementsByClassName('to-detail'); - this.structureDetailBtn= fields[0]; - this.electronicStructDetailBtn= fields[2]; - this.methodologyDetailBtn= fields[1]; - this.thermalDetailBtn= fields[3]; - /* - this.elasticDetailBtn= fields[1]; - this.methodologyDetailBtn= fields[2]; - this.electronicStructDetailBtn= fields[3]; - this.thermalDetailBtn= fields[4]; - */ - - this.vizBox = this.element.getElementsByClassName('viz-box')[0]; - //this.cellViewer= null; - - this.bandPlotter= null; - this.bsCalcIdBox = this.element.getElementsByClassName('footer-bs-calc')[0]; - this.dosPlotter= null; - this.dosCalcIdBox = this.element.getElementsByClassName('footer-dos-calc')[0]; - this.heatPlotter= null; - this.heatCalcIdBox = this.element.querySelector('.footer-heat-calc'); - - this.spinLegend = this.element.querySelector('.spin-legend'); - - // For static ones - InfoSys.addToInfoSystem(this.element); - - // Store the state of the calcs chosen on the Elec. Structure box - this.eStructCalcs = { bs: null, dos: null}; - } - - - attachAndSetEvents(element){ - element.appendChild(this.element); - this._events(); - } - - - _events() { - - this.structureDetailBtn.addEventListener( "click", (e) => { - util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.structure); - }); - - this.electronicStructDetailBtn.addEventListener( "click", (e) => { - util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.electronicstruct); - }); - - this.methodologyDetailBtn.addEventListener( "click", (e) => { - util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.methodology); - }); - - this.thermalDetailBtn.addEventListener( "click", (e) => { - util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.thermalprops); - }); - - /* - this.elasticDetailBtn.addEventListener( "click", (e) => { - util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.elasticconst); - }); - */ - - - //******* Optimize, genralize: - //this.element.querySelectorAll('.to-detail+.'+detailsId).addEventListener( "click", (e) => { - // util.setBrowserHashPath('material', this.materialId+'/'+detailsId); - } - - - getEStructChosenCalcs(){ - return this.eStructCalcs; - } - - - setDetailViewsListener(listener){ - this.detailViewsListener= listener; - } - - setVisible(){ - this.element.style.display= 'block'; - } - - - setMaterialData() { - - let data = DataStore.getMaterialData(); - this.materialTitle.innerHTML= util.getMaterialTitle(data); - this.materialId = data.id; - - let isBulk = (data.system_type === 'bulk'); - this.systemType.textContent= data.system_type; - this.structTypeField.style.display = - (isBulk && data.structure_type !== null ? 'block' : 'none'); - this.spaceGroupField.style.display = (isBulk ? 'block' : 'none'); - - if (isBulk){ - this.structTypeValue.textContent= data.structure_type; - this.spaceGroupValue.textContent = data.space_group_number+ - ' ('+data.space_group_international_short_symbol+')'; - InfoSys.addElementToInfoSystem(this.spaceGroupValue, - 'space-group.value:'+data.space_group_number); - } - - } - - - _evaluateCalc(calc){ - let value = 0; - if (calc.functional_type === 'GGA') value += 100; - if (calc.has_band_structure && calc.has_dos) value += 10; - switch (calc.code_name.trim()) { - case 'FHI-aims': value += 3; break; - case 'VASP': value += 2; break; - case 'Quantum Espresso': value += 1; break; - } - return value; - } - - - setCalcsData(markedTreeLeafs) { - //console.log('setCalcsData: '+JSON.stringify(data)); - let calcs = DataStore.getCalculations(); - - let functionalMap = new Map(); - let codeMap = new Map(); - let calcWithBS = null, calcWithDOS = null, calcWithHeat = null; - let calcBSEvaluation = -1, calcDOSEvaluation = -1; - - for (let i = 0; i < calcs.length; i++) { - - if (functionalMap.has(calcs[i].functional_type)){ - let num= functionalMap.get(calcs[i].functional_type); - // Because it's a number we can't use the returned value as a reference - functionalMap.set(calcs[i].functional_type,++num); - }else - functionalMap.set(calcs[i].functional_type,1); - - let codeNameTrimed= calcs[i].code_name.trim(); - if (codeMap.has(codeNameTrimed)){ - let num= codeMap.get(codeNameTrimed); - // Because it's a number we can't use the returned value as a reference - codeMap.set(codeNameTrimed,++num); - }else - codeMap.set(codeNameTrimed,1); - - let calcEvaluation = this._evaluateCalc(calcs[i]); - //console.log('calcEvaluation',calcEvaluation); - - if (calcs[i].has_band_structure && calcEvaluation > calcBSEvaluation){ - calcBSEvaluation = calcEvaluation; - calcWithBS = calcs[i]; - } - - if (calcs[i].has_dos && calcEvaluation > calcDOSEvaluation){ - calcDOSEvaluation = calcEvaluation; - calcWithDOS = calcs[i]; - } - //console.log('BS DOS Evaluation',calcBSEvaluation, calcDOSEvaluation); - - if (calcWithHeat === null && calcs[i].has_thermal_properties) - calcWithHeat = calcs[i]; - } - - if (calcWithBS !== null) this.eStructCalcs.bs = calcWithBS.id; - if (calcWithDOS !== null) this.eStructCalcs.dos = calcWithDOS.id; - - let tempCalcId = null; - if (calcWithBS !== null) tempCalcId = calcWithBS.id; - else if (calcWithDOS !== null) tempCalcId = calcWithDOS.id; - - if (tempCalcId === null) markedTreeLeafs.eStruct = null; // no graph data - else if (DataStore.isInAnyNotDisabledGroup(tempCalcId)){ - markedTreeLeafs.eStruct = DataStore.getGroupLeafId(tempCalcId); - }else - markedTreeLeafs.eStruct = +tempCalcId; - - if (calcWithHeat === null) markedTreeLeafs.thermalProps = null; - else if (DataStore.isInAnyNotDisabledGroup(calcWithHeat.id)){ - markedTreeLeafs.thermalProps = DataStore.getGroupLeafId(calcWithHeat.id); - }else - markedTreeLeafs.thermalProps = +calcWithHeat.id; - //console.log('Overview - thermalPropsDetailsTreeLeaf: ', markedTreeLeafs.thermalProps); - - //this.band_gap.innerHTML= util.getBandGapStatsValue(calcs); - - let functionalHTML= ''; - functionalMap.forEach((number,functional) => { - functionalHTML+= '<span info-sys-data="functional-type.value:'+functional+ - '">'+number+' '+functional+'</span> <br> '; - }); - - this.functional.innerHTML= functionalHTML; - InfoSys.addToInfoSystem(this.functional); - - let codeHTML= ''; - codeMap.forEach((number,codeName) => { - codeHTML+= '<span info-sys-data="code-name.value:'+codeName+ - '">'+number+' '+codeName+'</span> <br> '; - //codeHTML+= number+' '+codeName+' <br> '; - }); - this.code.innerHTML= codeHTML; - InfoSys.addToInfoSystem(this.code); - - - if (calcWithBS === null && calcWithDOS === null){ - document.getElementById('e-structure-ov').style.display = 'none'; - DataStore.hasElecStructureData = false; - }else{ - document.getElementById('e-structure-ov').style.display = 'block'; - DataStore.hasElecStructureData = true; - - if (this.bandPlotter === null){ - this.bandPlotter= new BSPlotter(); - this.bandPlotter.attach(document.getElementById('band-plotter'),undefined,316); - } - if (this.dosPlotter === null){ - this.dosPlotter= new DOSPlotter({left: 40, right: 16, top: 0, bottom: 30}); - this.dosPlotter.attach(document.getElementById('dos-plotter'),undefined,317); - } - - if (calcWithBS === null){ - this.bandPlotter.setNoData(); - this.bsCalcIdBox.innerHTML = ''; - }else{ - let url = util.getMaterialCalcURL(calcWithBS.material,calcWithBS.id, - 'band_structure'); - LoadingPopup.show(); - util.serverReq(url, e => { - if (e.target.status === 200){ - let bandStructData= JSON.parse(e.target.response).band_structure; - this.bandPlotter.setBandStructureData(bandStructData); - this.bsCalcIdBox.innerHTML = 'From calculation <b>'+calcWithBS.id+ - '</b><br><span style="font-size: 0.8em">('+calcWithBS.functional_type+' - '+calcWithBS.code_name+')</span>'; - if (bandStructData.segments[0].band_energies.length === 2) - this.spinLegend.style.display = 'block'; - } - LoadingPopup.hide(); - }); - } - - if (calcWithDOS === null){ - this.dosPlotter.setNoData(); - this.dosCalcIdBox.innerHTML = ''; - }else{ - let url = util.getMaterialCalcURL(calcWithDOS.material,calcWithDOS.id, - 'dos'); - LoadingPopup.show(); - util.serverReq(url, e => { - if (e.target.status === 200){ - let dosData= JSON.parse(e.target.response).dos; - - this.dosPlotter.setPoints(dosData);//paintPointsLine(dosData); - this.dosCalcIdBox.innerHTML = 'From calculation <b>'+calcWithDOS.id+ - '</b><br><span style="font-size: 0.8em">('+calcWithDOS.functional_type+' - '+calcWithDOS.code_name+')</span>'; - if (dosData.dos_values.length === 2) - this.spinLegend.style.display = 'block'; - } - LoadingPopup.hide(); - }); - } - } - - if (calcWithHeat === null){ - document.getElementById('thermal-props-ov').style.display = 'none'; - DataStore.hasThermalData = false; - }else{ - document.getElementById('thermal-props-ov').style.display = 'block'; - DataStore.hasThermalData = true; - - if (this.heatPlotter === null){ - this.heatPlotter= new HeatCapPlotter(); - this.heatPlotter.attach(document.getElementById('heat-plotter'),undefined,317); - } - - if (calcWithHeat === null){ - this.heatPlotter.setNoData(); - this.heatCalcIdBox.innerHTML = ''; - }else{ - let url = util.getMaterialCalcURL(calcWithHeat.material, calcWithHeat.id, - 'specific_heat_cv'); - LoadingPopup.show(); - util.serverReq(url, e => { - if (e.target.status === 200){ - let heatData= JSON.parse(e.target.response).specific_heat_cv; - //console.log(heatData); - this.heatPlotter.setData(heatData); - this.heatCalcIdBox.innerHTML = 'From calculation <b>'+calcWithHeat.id+'</b>'+ - '</b> <span style="font-size: 0.8em">('+calcWithHeat.functional_type+' - '+calcWithHeat.code_name+')</span>'; - } - LoadingPopup.hide(); - }); - } - } - - } // setCalcsData function - - } - - // EXPORTS - module.exports = Overview; - - -/***/ }, -/* 12 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements an information system for the user that provides info - related to the concepts or quantities shown on the UI - The literals are read form the file infosys.json - */ - - "use strict"; - - let util = __webpack_require__(1); - let SwitchComponent = __webpack_require__(13); - - const INFOSYS_FILE_PATH = 'infosys.json'; - - let tooltip = document.querySelector('#info-tooltip'); - let tooltipContent = document.querySelector('#tooltip-content'); - - let elements = []; - let timerSet = null; - let on = false; - let data = null; - - function clearCurrentTimeoutAnSetANew(){ - if (timerSet !== null) window.clearTimeout(timerSet); - timerSet = window.setTimeout(t => tooltip.style.display = 'none', 1000); - } - - - let switchComponent = new SwitchComponent(util.IMAGE_DIR+'switch'); - document.querySelector('#info-sys-switch-box').appendChild(switchComponent.element); - - switchComponent.setListener( off => { - on = !off; - if (off) { - elements.forEach( element => { - element.removeEventListener('mouseover', mouseOver); - element.className = ''; - }); - }else{ - if (data === null) - util.serverReq(INFOSYS_FILE_PATH, e => data = JSON.parse(e.target.response)); - - elements.forEach(enableTooltip); - } - - - tooltip.addEventListener('mouseover', e => { - window.clearTimeout(timerSet); - }); - - tooltip.addEventListener('mouseout', e => { - clearCurrentTimeoutAnSetANew(); - }); - - }); - - - function addToInfoSystem(baseElement){ - let infosysLabels = baseElement.querySelectorAll('span[info-sys-data]'); - - for (let i = 0; i < infosysLabels.length; ++i) - elements.push(infosysLabels[i]); - - //if (on) infosysLabels.forEach(enableTooltip); - if (on) - for (let i = 0; i < infosysLabels.length; ++i) - enableTooltip(infosysLabels[i]); - } - - - function addElementToInfoSystem(element, value){ - elements.push(element); - element.setAttribute('info-sys-data',value); - - if (on) enableTooltip(element); - } - - - function enableTooltip(element){ - element.addEventListener('mouseover', mouseOver); - element.addEventListener('mouseout', e => { - clearCurrentTimeoutAnSetANew(); - element.style.cursor = 'inherit'; - }); - element.className = 'info-sys-label'; - } - - - function mouseOver(e){ - let r = e.target.getBoundingClientRect(); - let quantity = e.target.getAttribute('info-sys-data'); - - let index = quantity.indexOf('.value'); - if (index > 0){ // quantity value - let quantityObject = data[quantity.split('-').join(' ').substring(0, index)]; - //console.log('VALUE', quantityObject); - if (quantityObject.value_template === undefined){ //direct value - let valueObj = quantityObject.values[quantity.split(':')[1]]; - tooltipContent.innerHTML = getHTML(valueObj); - }else{ // value template - let object = quantityObject.value_template; - object.text = templating(object.text, quantity.split(':')[1]); - object.link = templating(object.link, quantity.split(':')[1]); - tooltipContent.innerHTML = getHTML(object); - //console.log('VALUE TEMPLATE: ', object); - } - }else // quantity name - tooltipContent.innerHTML = getHTML(data[quantity.split('-').join(' ')]); - - tooltip.style.visibility = 'hidden'; - tooltip.style.display = 'block'; - let ttRect = tooltip.getBoundingClientRect(); - let leftOffset = ttRect.width - r.width; - let leftPos = r.left -leftOffset/2; - if (leftPos + ttRect.width > window.innerWidth) - leftPos = window.innerWidth -ttRect.width; - //let topOffset = ttRect.height + 20 - window.pageYOffset; - let topOffset = - window.pageYOffset; - tooltip.style.left = (leftPos < 0 ? 5 : leftPos)+'px'; - tooltip.style.top = (r.top + r.height -topOffset)+'px'; - tooltip.style.visibility = 'visible'; - window.clearTimeout(timerSet); - e.target.style.cursor = 'help'; - } - - - function templating(s, param) { - let initIndex = s.indexOf('${'); - let finalIndex = s.indexOf('}'); - if (initIndex >= 0 && finalIndex >= 0 && finalIndex > initIndex){ - return s.substring(0,initIndex)+param+s.substring(finalIndex+1); - }else return s; - } - - function getHTML(object) { - let html = ''; - if (object.text === undefined){ - //html = 'NO TEXT!! Comment: '+object.comment; - }else{ // there is text attr - html += object.text; - } - if (object.link !== undefined){ - if (object.text !== undefined) html += '<br>'; - html += '<a href="'+object.link+'" target="_blank">More information</a>'; - } - - return html; - } - - - // EXPORTS - module.exports = { addToInfoSystem, addElementToInfoSystem }; - - -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This component implements a generic switch/toggle button - */ - - - let util = __webpack_require__(1); - - class SwitchComponent { - - constructor(imageBasePath) { - - this.off = true; - - this.element = document.createElement('span'); - this.element.innerHTML+=` - <img src="${imageBasePath}_off.png" width="24px" - style="margin-bottom: -1px; cursor: pointer"/> - `; - this.image = this.element.querySelector('img'); - - this.element.addEventListener('click', e => { - this.off = !this.off; - let imagePath = (this.off ? imageBasePath+'_off' : imageBasePath); - this.image.setAttribute('src',imagePath+'.png'); - this.listener(this.off); - }); - } - - - setListener(listener){ - this.listener = listener; - } - } - - // EXPORTS - module.exports = SwitchComponent; - - -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Implements the Band Structure plotter. - It's used to show either the regular Band Structure data or - the Phonon Dispersion. - */ - - - "use strict"; - - let InteractivePlotterBase = __webpack_require__(15); - let svg = __webpack_require__(9); - let util = __webpack_require__(1); - - const MULTIPLO1 = 1.602176565e-19; - const MULTIPLO2 = 1/5.034117012222e22; - - - class BSPlotter extends InteractivePlotterBase{ - - constructor() { - super({left: 55, right: 5, top: 0, bottom: 30}); - this.phononMode = false; - this.factor = MULTIPLO1; - } - - - setPhononMode(){ - this.phononMode = true; - this.factor = MULTIPLO2; - this.outOfRangeColorActivated = false; - } - - - // detach if necessary and attach - attach(element, width, height){ - super.attach(element, width, height); - } - - - getTopAndLowestPoints(bandStructData){ - let bandMax = -10000; - let bandMin = 10000; - for (let i = 0; i < bandStructData.length; i++) { // Per segment - // spin1 - per band energy loop - for (let j = 0; j < bandStructData[i].band_energies[0].length; j++) { - let tempValue= Math.max.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/); - if (tempValue > bandMax) bandMax= tempValue; - tempValue= Math.min.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/); - if (tempValue < bandMin) bandMin= tempValue; - } - - if (bandStructData[i].band_energies.length === 2) - // spin2 - per band energy loop - for (let j = 0; j < bandStructData[i].band_energies[1].length; j++) { // Per segment - let tempValue= Math.max.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/); - if (tempValue > bandMax) bandMax= tempValue; - tempValue= Math.min.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/); - if (tempValue < bandMin) bandMin= tempValue; - } - } - return [bandMin/this.factor, bandMax/this.factor]; - } - - - drawKPointLabel(x, label){ - svg.addText(this.axisGroup, x*this.xRel, 16, label, 'middle', 'steps'); - } - - - setBandStructureData(data){ - - let bandStructData; - if (data.segments !== undefined) bandStructData = data.segments; - else bandStructData = data; - - this.bandGapData = undefined; - if (data.band_gap !== undefined && data.band_gap.position !== undefined){ - this.bandGapData = data.band_gap.position; - this.bandGapData.cbmDistances = []; - this.bandGapData.vbmDistances = []; - } - - // Gather all the points per band (divided by spin) crossing the segments - this.bandsDataSpin1= []; // [segment][band][kpoint] - this.bandsDataSpin2= []; - this.segmentLimitsX = []; - this._reset(); - - let topAndLowestPoints = this.getTopAndLowestPoints(bandStructData); - let minEnergyVal = topAndLowestPoints[0]; - let maxEnergyVal = topAndLowestPoints[1]; - - if (this.phononMode){ - this.setAxisRangeAndLabels('',0,1,'Frequency (cm⁻¹)',-50, 320, - minEnergyVal, maxEnergyVal, 100); - }else - this.setAxisRangeAndLabels('',0,1,'Energy (eV)' ,-6 ,11 , minEnergyVal, - maxEnergyVal, 5 ); - - // Calculates de distance - let totalDistance= 0; - for (let k = 0; k < bandStructData.length; k++) { - let kPoints= bandStructData[k].band_k_points; - totalDistance+= kPointDistance(kPoints,kPoints.length-1); - } - let currentDistance= 0; - let prevLastLabel = null; - let dataOverflow = false; - - for (let k = 0; k < bandStructData.length; k++) { // For every segment - - let segment= bandStructData[k]; - let kPoints= segment.band_k_points; - let labels= segment.band_segm_labels; - - let energiesSpin1= segment.band_energies[0]; - let energiesSpin2= segment.band_energies[1]; - this.bandsDataSpin1.push([]); // Add a new array per segment - this.bandsDataSpin2.push([]); - - let segmentDistance= kPointDistance(kPoints,kPoints.length-1); - - // keeping the segment limits (x coordenate) for after painting - this.segmentLimitsX.push(currentDistance/totalDistance); - - if (labels !== null){ - // Set k-points labels - if (prevLastLabel !== null && prevLastLabel !== labels[0]) - this.drawKPointLabel(currentDistance/totalDistance, - getSymbol(prevLastLabel)+'|'+getSymbol(labels[0])); - else - this.drawKPointLabel(currentDistance/totalDistance,getSymbol(labels[0])); - // The last label - if (k === bandStructData.length -1) - this.drawKPointLabel(1, getSymbol(labels[1])); - - prevLastLabel = labels[1]; - } - - for (let i = 0; i < kPoints.length; i++) { // For every k-point - - let tempDistance= (currentDistance + kPointDistance(kPoints, i))/totalDistance; - - if (this.bandGapData !== undefined){ - - if (this.bandGapData.lower !== undefined){ - let kpt = this.bandGapData.lower.kpt; - if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] - && kPoints[i][2] === kpt[2]){ - this.bandGapData.cbmDistances.push(tempDistance); - } - } - - if (this.bandGapData.upper !== undefined){ - let kpt = this.bandGapData.upper.kpt; - if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] - && kPoints[i][2] === kpt[2]){ - this.bandGapData.vbmDistances.push(tempDistance); - } - } - - /* - let kpt = this.bandGapData.cbmKpt; - if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] - && kPoints[i][2] === kpt[2]){ - //console.log("kpoints: ", kPoints[i], kpt, tempDistance); - this.bandGapData.cbmX = tempDistance; - } - kpt = this.bandGapData.vbmKpt; - if (kPoints[i][0] === kpt[0] && kPoints[i][1] === kpt[1] - && kPoints[i][2] === kpt[2]){ - //console.log("kpoints: ", kPoints[i], kpt, tempDistance); - this.bandGapData.vbmX = tempDistance; - } - */ - } - - - // All bands spin1 - for (let j = 0; j < energiesSpin1[i].length; j++) { - if (i === 0) this.bandsDataSpin1[k][j] = [];//if (k === 0 && i === 0) this.bandsDataSpin1[j] = []; - let currentY = energiesSpin1[i][j]/this.factor; - this.bandsDataSpin1[k][j].push({x: tempDistance, y: currentY}); - if (!dataOverflow && currentY > 10000) dataOverflow = true; - } - // All bands spin2 - if (energiesSpin2 !== undefined) - for (let j = 0; j < energiesSpin2[i].length; j++) { - if (i === 0) this.bandsDataSpin2[k][j] = []; - let currentY = energiesSpin2[i][j]/this.factor; - this.bandsDataSpin2[k][j].push({x: tempDistance, y: currentY}); - if (!dataOverflow && currentY > 10000) dataOverflow = true; - } - //console.log("K PPPPPP Ponint: "+i+' DIS: '+tempDistance, this.bandsDataSpin1[k]); - } - - currentDistance+= segmentDistance; - } - - if (dataOverflow) throw 'Plotter Data Overflow: Probably the energy data is not in correct units'; //console.log('BSPlotter data overflow'); - else this.repaint(); - } - - - repaintData(yMin, yMax){ - - this.segmentLimitsX.forEach(x => { - let yMinPx = this.transformY(yMin); - let yMaxPx = this.transformY(yMax); - if (this.phononMode) { yMinPx += 200; yMaxPx -= 200; } - svg.addLine(this.plotContent, x*this.xRel, yMinPx, - x*this.xRel, yMaxPx, 'segment'); - }); - - // Drawing lines - let polylinePoints; - for (var i = 0; i < this.bandsDataSpin1.length; i++) // loop the segments - - for (var j = 0; j < this.bandsDataSpin1[i].length; j++) { // loop the bands - polylinePoints = ''; - for (var k = 0; k < this.bandsDataSpin1[i][j].length; k++) { // loop the kpoints - polylinePoints+= ' '+this.xRel*this.bandsDataSpin1[i][j][k].x+ - ' '+this.transformY(this.bandsDataSpin1[i][j][k].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); - } - - if (this.bandsDataSpin2.length > 0){ - for (var i = 0; i < this.bandsDataSpin2.length; i++) // loop the segments - - for (var j = 0; j < this.bandsDataSpin2[i].length; j++) { // loop the kpoints - polylinePoints = ''; - for (var k = 0; k < this.bandsDataSpin2[i][j].length; k++) { // loop the bands - polylinePoints+= ' '+this.xRel*this.bandsDataSpin2[i][j][k].x+ - ' '+this.transformY(this.bandsDataSpin2[i][j][k].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); - } - } - - - // Paint CBM and VBM - // console.log("Paint CBM and VBM: ", this.vbm); this.vbm.y = -1.01577992001999e-20/this.factor; - if (this.bandGapData !== undefined - // If there are two spins related data the CBM and VBM points aren't shown - /*&& this.bandsDataSpin2.length === 0*/) - { - - this.bandGapData.cbmDistances.forEach( distance => { - let x = this.xRel * distance; - let y = this.transformY(this.bandGapData.lower.energy/this.factor); - //console.log("hhhhhhhhh: ", x, y); - svg.addPoint(this.plotContent, x, y , 3, 'cbm-vbm-points'); - svg.addText(this.plotContent, x+4, y-6, 'CBM'); - }); - - - this.bandGapData.vbmDistances.forEach( distance => { - let x = this.xRel*distance; - let y = this.transformY(this.bandGapData.upper.energy/this.factor); - svg.addPoint(this.plotContent, x, y, 3, 'cbm-vbm-points'); - svg.addText(this.plotContent, x+4, y+14, 'VBM'); - }); - - /* - - let x = this.xRel*this.bandGapData.cbmDistance; - let y = this.transformY(this.bandGapData.lower.energy/this.factor); - //console.log("hhhhhhhhh: ", x, y); - svg.addPoint(this.plotContent, x, y , 3, 'cbm-vbm-points'); - svg.addText(this.plotContent, x+4, y-6, 'CBM'); - - x = this.xRel*this.bandGapData.vbmDistance; - y = this.transformY(this.bandGapData.upper.energy/this.factor); - svg.addPoint(this.plotContent, x, y, 3, 'cbm-vbm-points'); - svg.addText(this.plotContent, x+4, y+14, 'VBM'); - */ - } - - } - - - } - - - function kPointDistance(kPoints, position){ - let p0= kPoints[0]; - let p1= kPoints[position]; - let deltaX= p1[0] - p0[0]; - let deltaY= p1[1] - p0[1]; - let deltaZ= p1[2] - p0[2]; - return Math.sqrt(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ); - } - - function getSymbol(label){ - if (label === 'Gamma' || label === 'G') return 'Γ'; - else return label; - } - - // EXPORTS - module.exports = BSPlotter; - - -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - This is the base class that models a highly interactive plotter. - It is inherited by several classes implementing specifc plotters. - This plotter implements zoom and y-axis offset - */ - - - "use strict"; - - - let svg = __webpack_require__(9); - - const xmlns="http://www.w3.org/2000/svg"; - const xlink="http://www.w3.org/1999/xlink"; - - - class InteractivePlotterBase{ - - constructor(margins = {left: 20, right: 0, top: 0, bottom: 20}) { - this.margins= margins; - this.svg = document.createElementNS(xmlns, "svg"); - - this.parentElement= null; - this.plotContent = null; - this.axisGroup = null; - this.yAxisLabelsGroup = null; - this.yLabelText = null; // If null, y axis label and numbers are not painted - this.noDataGroup = null; - - this.yZoom = 1; // Initial zoom - this.yOffset = 0; // Initial y offset = 0 - this.repaintListener = null; - this.nodataLabel = null; - - this.outOfRangeColorActivated = true; - } - - - attach(element, width, height){ - this.parentElement= element; - this.parentElement.appendChild(this.svg); - this.width = (width !== undefined ? width : this.parentElement.clientWidth); - this.height = (height !== undefined ? height : this.svg.width); - this.svg.setAttribute("width", this.width); - this.svg.setAttribute("height", this.height); - this.plotRangeX = this.width - this.margins.left - this.margins.right; - this.plotRangeY = this.height - this.margins.top - this.margins.bottom; - - // y axis area (zoomable) - this.yAxisArea = document.createElementNS(xmlns, "svg"); - this.svg.appendChild(this.yAxisArea); - let yAxisAreaWidth = this.margins.left; - this.plotAreaHeight = this.height - this.margins.bottom - this.margins.top; - this.yAxisArea.setAttribute("width", yAxisAreaWidth); - this.yAxisArea.setAttribute("height", this.plotAreaHeight);//-OVERLAP_CORRECTOR); - this.yAxisArea.setAttribute("x", 0); - this.yAxisArea.setAttribute("y", 0);//OVERLAP_CORRECTOR); - - // SVG plot area window - this.plotArea = document.createElementNS(xmlns, "svg"); - this.svg.appendChild(this.plotArea); - this.plotAreaWidth = this.width - this.margins.left - this.margins.right; - this.plotArea.setAttribute("width", this.plotAreaWidth); - this.plotArea.setAttribute("height", this.plotAreaHeight); - this.plotArea.setAttribute("x", this.margins.left); - this.plotArea.setAttribute("y", this.margins.top); - - // Rect filling plot area in order to support styles and events - this.plotAreaBg = svg.addRect(this.plotArea, 0, 0, this.plotAreaWidth, this.plotAreaHeight); - this.plotAreaBg.setAttribute('class', 'moveable-plot'); - this.plotAreaBg.setAttribute('opacity', 0.0); - - this. _events(); - } - - - isAttached(){ - return this.parentElement !== null; - } - - - setAxisRangeAndLabels(xLabel, xMin, xMax, yLabel, yMinInit, yMaxInit, - yMin, yMax, yLabelGap, decimals = 2){ - this.xLabel= xLabel; - this.xMin = xMin; - this.xMax = xMax; - this.yMinInit= yMinInit; - this.yMaxInit= yMaxInit; - this.yMin= yMin; - this.yMax= yMax; - this.yLabelGapInit = yLabelGap; - this.xRel= this.plotRangeX/(this.xMax-this.xMin); - this.yRel= this.plotRangeY/(this.yMaxInit-this.yMinInit); - - this._resetAxisGroup(); - - // Draw axes - svg.addLine(this.axisGroup, 0,0,this.plotRangeX,0, 'main-axis'); - svg.addLine(this.axisGroup, 0,0,0,-this.plotRangeY ,'main-axis'); - svg.addLine(this.axisGroup, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY, 'main-axis'); - svg.addLine(this.axisGroup, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY, 'main-axis'); - - // Paint x and y axes labels - if (yLabel !== null){ - this.yLabelText = svg.addText(this.svg, 0, 0, yLabel, 'middle', 'axis-steps-big'); - this.yLabelText.setAttribute('transform','translate(15,'+(this.plotRangeY/2+this.margins.top)+') rotate(-90)'); - } - if (xLabel !== null) - svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom-12, this.xLabel, 'middle', 'axis-steps-big'); - - // initialize y axis steps (dynamic construction) container - this._resetYAxisLabelGroup(); - - // transformY precalcultation 1 - this.precalculation_1 = this.plotAreaHeight + this.yMinInit*this.yRel; - this.precalculation_2 = this.precalculation_1;//- this.yOffset // when this quantity changes - } - - - _events(){ - - this.plotArea.addEventListener('wheel', (e) => { - e.preventDefault(); - if (e.deltaY > 0 && this.yZoom > 0.5 ) this.yZoom -= 0.2; - else if (e.deltaY < 0 && this.yZoom < 2) this.yZoom += 0.2; - this.repaint(); - if (this.repaintListener !== null) - this.repaintListener(this.yZoom, this.yOffset); - }); - - let initPosY; - this.plotArea.addEventListener('mousedown', (e) => { - e.preventDefault(); - initPosY = e.clientY + this.yOffset; - //console.log('mousedown: e.clientY + this.yOffset', e.clientY, this.yOffset); - this.plotArea.addEventListener('mousemove', moveListener); - - this.plotArea.addEventListener('mouseup', (e) => { - this.plotArea.removeEventListener('mousemove', moveListener); - }); - this.plotArea.addEventListener('mouseout', (e) => { - this.plotArea.removeEventListener('mousemove', moveListener); - }); - }); - - let self = this; - function moveListener(e) { - //console.log('Y offset:', e.clientY - initPosY); - // Bad if (initPosY - e.clientY > this.yMax || initPosY - e.clientY < this.yMin) - self.yOffset = initPosY - e.clientY ; - self.precalculation_2 = self.precalculation_1 - self.yOffset; - self.repaint(); - if (self.repaintListener !== null) - self.repaintListener(self.yZoom, self.yOffset); - } - } - - - setYZoomAndOffset(yZoom, yOffset){ - this.yZoom = yZoom; - this.yOffset = yOffset; - this.precalculation_2 = this.precalculation_1 - this.yOffset; - } - - - setExternalYAxisMax(externalYAxisMax){ - this.externalYAxisMax = externalYAxisMax; - } - - - getYAxisMax(){ - return this.yAxisMax; - } - - - repaint(){ - // repaint Y axis - this._resetYAxisLabelGroup(); - - let yLabelGap; - if (this.yZoom > 1) yLabelGap = this.yLabelGapInit/5; - else yLabelGap = this.yLabelGapInit; - - let min = Math.floor(this.yMin/yLabelGap)*yLabelGap; - let max = Math.ceil(this.yMax/yLabelGap)*yLabelGap; - this.yAxisMax = max; - if (this.externalYAxisMax !== undefined) max = this.externalYAxisMax; - - if (this.yLabelText !== null) { - for (let i = min; i < max+1; i = i + yLabelGap) { - svg.addLine(this.yAxisLabelsGroup, this.margins.left, - this.transformY(i), this.margins.left-3, this.transformY(i)); - svg.addText(this.yAxisLabelsGroup, this.margins.left-5, - this.transformY(i)+5, i, 'end', 'axis-steps'); - } - } - - // repaint plot content - this._resetPlotContent(); - - // Out of range areas - if (this.outOfRangeColorActivated){ - let area = svg.addRect(this.plotContent, 0, this.transformY(this.yMax)-2*this.plotAreaHeight, this.plotAreaWidth, 2*this.plotAreaHeight); - area.setAttribute('class', 'out-of-range'); - let area1 = svg.addRect(this.plotContent, 0, this.transformY(this.yMin), this.plotAreaWidth, 2*this.plotAreaHeight); - area1.setAttribute('class', 'out-of-range'); - } - - // Zero line - svg.addLine(this.plotContent, 0, this.transformY(0), this.plotRangeX, - this.transformY(0), 'zeroline'); - - // repaint data lines - this.repaintData(min, max); - - // Add the top layer: rect for events - this.plotArea.removeChild(this.plotAreaBg); - this.plotArea.appendChild(this.plotAreaBg); - } - - - setRepaintListener(listener) { - this.repaintListener = listener; - } - - - transformY(y){ - // Precalculation usage - // this.plotAreaHeight -y*this.yZoom*this.yRel + this.yMinInit*this.yRel - this.yOffset - // -y*this.yZoom*this.yRel (calculated here) this.plotAreaHeight + this.yMinInit*this.yRel - this.yOffset - let result = -y*this.yZoom*this.yRel + this.precalculation_2; - if (result > 10000) throw 'Y coordinate too large'; - return result; - } - - - _resetPlotContent(){ - // TRy to delete with textContent property - if (this.plotContent !== null) - this.plotArea.removeChild(this.plotContent); - this.plotContent = document.createElementNS(xmlns, "g"); - this.plotArea.appendChild(this.plotContent); - } - - - _resetAxisGroup(){ - if (this.axisGroup !== null) - this.svg.removeChild(this.axisGroup); - this.axisGroup = document.createElementNS(xmlns, 'g'); - this.svg.appendChild(this.axisGroup); - - // The y axis is inverted so the y coordinate has to be multiplied by -1 - this.axisGroup.setAttribute("transform", 'matrix(1 0 0 1 '+ - this.margins.left+' '+(this.height - this.margins.bottom)+')'); - } - - - _resetYAxisLabelGroup(){ - if (this.yLabelText === null) return; - - if (this.yAxisLabelsGroup !== null) - this.yAxisArea.removeChild(this.yAxisLabelsGroup); - this.yAxisLabelsGroup = document.createElementNS(xmlns, 'g'); - this.yAxisArea.appendChild(this.yAxisLabelsGroup); - //this.yAxisLabelsGroup.setAttribute("transform", 'matrix(1 0 0 1 0 -'+OVERLAP_CORRECTOR+')'); - } - - - _reset(){ - this.yZoom = 1; // Initial zoom - this.yOffset = 0; // Initial y offset = 0 - - // initialize plot content (dynamic construction) container - this._resetPlotContent(); - - if (this.yLabelText !== null){ - this.svg.removeChild(this.yLabelText); - this.yLabelText = null; - } - - if (this.noDataGroup !== null){ - this.svg.removeChild(this.noDataGroup); - this.noDataGroup = null; - } - } - - - setNoData(){ - this._resetYAxisLabelGroup(); - this._resetPlotContent(); - this._resetAxisGroup(); - if (this.noDataGroup === null){ - this.noDataGroup = document.createElementNS(xmlns, 'g'); - this.svg.appendChild(this.noDataGroup); - svg.addRect(this.noDataGroup, 0, 0, this.width, this.height); - this.noDataGroup.setAttribute('fill', '#EEE'); - svg.addText(this.noDataGroup, this.width/2, this.height/2+10, - 'NO DATA', 'middle', 'nodata'); - } - - } - - } - - // EXPORTS - module.exports = InteractivePlotterBase; - - -/***/ }, -/* 16 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Implements the DOS plotter. - */ - - - "use strict"; - - let InteractivePlotterBase = __webpack_require__(15); - let svg = __webpack_require__(9); - let util = __webpack_require__(1); - - - const E_MIN = -6; - const E_MAX = 11; - const E_FACTOR = 1.602176565e-19; - //const DOSVALUE_FACTOR = 1.602176565e-49; - - - class DOSPlotter extends InteractivePlotterBase{ - - constructor(margins) { - super(margins); - } - - // detach if necessary and attach - attach(element, width, height){ - super.attach(element, width, height); - } - - - setPoints(points, codeName){ - - let dosValueDivisor = E_FACTOR; - //if (codeName === 'VASP') dosValueDivisor = 1e-10*(E_FACTOR*E_FACTOR); - - this.pointsSpin1 = []; - this.pointsSpin2 = []; - this._reset(); - - let pSpin1= points.dos_values[0]; - let pSpin2 = null; - if (points.dos_values.length === 2) pSpin2 = points.dos_values[1]; - let pointsY= points.dos_energies; - let pointsXInPlotRange = []; - let pointsYInPlotRange = []; - - for (var i = 0; i < pointsY.length; i++) { - let energy = pointsY[i]/E_FACTOR; - let dos_value_spin1 = pSpin1[i]/dosValueDivisor; - pointsXInPlotRange.push(dos_value_spin1); - if (pSpin2 !== null) pointsXInPlotRange.push(pSpin2[i]); - pointsYInPlotRange.push(energy); - //console.log('POINTS : ',pointsX[i], energy); - this.pointsSpin1.push({x: dos_value_spin1, y: energy}); - if (pSpin2 !== null) - this.pointsSpin2.push({x: pSpin2[i]/dosValueDivisor, y: energy}); - } - - let maxDosVal = Math.max.apply(null, pointsXInPlotRange); - let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange); - let minEnergyVal = Math.min.apply(null, pointsYInPlotRange); - - // x axis steps generation - let t = util.generateDiagramSteps(maxDosVal); - let xSteps = t[0], exp = t[1]; - - this.setAxisRangeAndLabels(null, 0, xSteps[xSteps.length-1], 'Energy (eV)', - E_MIN, E_MAX, minEnergyVal, maxEnergyVal, 5); - - svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom, - 'DOS (states/eV/cell)', 'middle', 'axis-steps-big'); - - // draw x axis steps - 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); - //console.log('step ',xSteps[i], stepX); - svg.addText(this.axisGroup, stepX, 13, - (i === 0 ? '0' : xSteps[i].toFixed(exp)),'middle', 'axis-steps-smaller'); - } - - this.repaint(); - } - - - repaintData(){ - - let polylinePoints = ''; - for (var i = 0; i < this.pointsSpin1.length; i++) { - polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+ - ' '+this.transformY(this.pointsSpin1[i].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); - - polylinePoints = ''; - for (var i = 0; i < this.pointsSpin2.length; i++) { - polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+ - ' '+this.transformY(this.pointsSpin2[i].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); - } - - - setYAxisLabelsVisibility(value){ - if (this.yAxisLabelsGroup !== null) - this.yAxisLabelsGroup.style.visibility = (value ? 'visible' : 'hidden'); - } - - } - - - // EXPORTS - module.exports = DOSPlotter; - - -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - Specific Heat plotter implementation - */ - - - "use strict"; - - let svg = __webpack_require__(9); - let PlotterBase = __webpack_require__(18); - - - class HeatCapPlotter extends PlotterBase{ - - constructor() { - super({left: 50, right: 16, top: 10, bottom: 32}); - this.tooltip; - } - - - setData(data){ - //console.log(JSON.stringify(data));// - this.clear(); - // up to 600K data is taken - let indexOf600K = data.temperature.indexOf(600)+1; - let values = data.value.slice(0, indexOf600K); - let temperatures = data.temperature.slice(0, indexOf600K); - - let yMaxValue = Math.max.apply(null, values); - //console.log('maxValue: ',maxValue);// - this.setRangeAndLabels('T (K)', 0, 600, 'Cv (J/K/kg)', 0, Math.ceil(yMaxValue/200)*200); - this.drawAxis(4, 4, 0); - - let polylinePoints = ''; - temperatures.forEach( (t, i) => { - let y = values[i];///1e-25; - polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(y - this.yMin); - }); - svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1'); - } - - } - - - // EXPORTS - module.exports = HeatCapPlotter; - - -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - This is the base class that models a regular plotter. - It is inherited by several classes implementing specifc plotters - */ - - "use strict"; - - - let svg = __webpack_require__(9); - - const xmlns="http://www.w3.org/2000/svg"; - const xlink="http://www.w3.org/1999/xlink"; - - - class PlotterBase{ - - constructor(margins = {left: 20, right: 0, top: 10, bottom: 20}) { - this.svg = document.createElementNS(xmlns, "svg"); - this.plotArea = document.createElementNS(xmlns, "g"); - this.svg.appendChild(this.plotArea); - - this.margins= margins; - this.parentElement= null; - this.yLabelText = null; - this.noDataGroup = null; - } - - - attach(element, width, height){ - this.parentElement= element; - this.parentElement.appendChild(this.svg); - this.width = (width !== undefined ? width : this.parentElement.clientWidth); - this.height = (height !== undefined ? height : this.svg.width); - this.svg.setAttribute("width", this.width); - this.svg.setAttribute("height", this.height); - this.plotRangeX = this.width - this.margins.left - this.margins.right; - this.plotRangeY = this.height - this.margins.top - this.margins.bottom; - } - - - isAttached(){ - return this.parentElement !== null; - } - - - setRangeAndLabels(xLabel, xMin, xMax, yLabel, yMin, yMax){ - this.xLabel= xLabel; - this.xMin = xMin; - this.xMax = xMax; - this.yLabel= yLabel; - this.yMin= yMin; - this.yMax= yMax; - - this.xRel= this.plotRangeX/(this.xMax-this.xMin); - this.yRel= this.plotRangeY/(this.yMax-this.yMin); - } - - - drawAxis(xSteps = 0, ySteps = 0, decimals = 2){ - - this.plotArea.setAttribute("transform", 'matrix(1 0 0 1 '+ - this.margins.left+' '+(this.height - this.margins.bottom)+')'); - - this.yLabelText = svg.addText(this.svg, 0, 0, this.yLabel, 'middle', 'axis-steps-big'); - this.yLabelText.setAttribute('transform','translate(13,' - +(this.plotRangeY/2+this.margins.top)+') rotate(-90)'); - svg.addText(this.plotArea, this.plotRangeX/2, this.margins.bottom-1, - this.xLabel, 'middle', 'axis-steps-big'); - - if (xSteps !== null){ - let xStep= this.plotRangeX/ xSteps; - for (let i = 0; i <= xSteps; i++) { - svg.addLine(this.plotArea, xStep*i, 0, xStep*i, 4, 1); - svg.addText(this.plotArea, xStep*i, 14, - +((xStep*i/this.xRel)+this.xMin).toFixed(decimals), 'middle', 'statisticsviewersteps'); - } - } - - if (ySteps === null && this.yMax > 0 && this.yMin < 0){ - let i = 1; - while(this.yMax*i > this.yMin) { - svg.addLine(this.plotArea, 0, this.transformY(this.yMax*i), -3, this.transformY(this.yMax*i), 1); - let numberText = (Math.abs(this.yMax*i) >= 10000 ? (this.yMax*i).toExponential() : this.yMax*i ); - svg.addText(this.plotArea,-5, this.transformY(this.yMax*i)+3, numberText, - 'end', 'statisticsviewersteps'); - i--; - } - } - - if (ySteps !== null){ - let yStep= this.plotRangeY/ ySteps; - for (let i = 0; i <= ySteps; i++) { - svg.addLine(this.plotArea, 0, -yStep*i, -3, -yStep*i, 1); - let numberToPaint= (yStep*i/this.yRel) + this.yMin; - // Fix to prevent the the -0 printing - if (Math.abs(numberToPaint) < 0.01) numberToPaint = 0; - //console.log('drawAxis', yStep, i, this.yRel, this.yMin, numberToPaint); - svg.addText(this.plotArea,-5, -(yStep*i-3), numberToPaint.toFixed(decimals), 'end', 'statisticsviewersteps'); - } - } - - svg.addLine(this.plotArea, 0, 0, this.plotRangeX+1 ,0 ,'main-axis'); - svg.addLine(this.plotArea, 0,0,0,-(this.plotRangeY+1) ,'main-axis'); - svg.addLine(this.plotArea, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY ,'main-axis'); - svg.addLine(this.plotArea, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY,'main-axis'); - } - - - clear(){ - this.svg.removeChild(this.plotArea); - this.plotArea = document.createElementNS(xmlns, "g"); - this.svg.appendChild(this.plotArea); - this.plotArea.setAttribute("transform", 'matrix(1 0 0 1 '+ - this.margins.left+' '+(this.height - this.margins.bottom)+')'); - - if (this.yLabelText !== null){ - this.svg.removeChild(this.yLabelText); - this.yLabelText = null; - } - if (this.noDataGroup !== null){ - this.svg.removeChild(this.noDataGroup); - this.noDataGroup = null; - } - } - - - setNoData(){ - this.clear(); - if (this.noDataGroup === null){ - this.noDataGroup = document.createElementNS(xmlns, 'g'); - this.svg.appendChild(this.noDataGroup); - svg.addRect(this.noDataGroup, 0, 0, this.width, this.height); - this.noDataGroup.setAttribute('fill', '#EEE'); - svg.addText(this.noDataGroup, this.width/2, this.height/2+10, - 'NO DATA', 'middle', 'nodata'); - } - - } - - - // Transform from y-axis units to y-axis pixels (svg-coordinates) - transformY(y){ - return -this.yRel*(y - this.yMin); - } - - } - - - // EXPORTS - module.exports = PlotterBase; - - -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - 'Details' view container that shows all the material info related to - its structure. - - This container is extremely complex. - - In the file there are two defined (classes) components used in the container: - - TreeLeafViewer: The panel on the right showing the data of the tree leaf marked - - SummaryByFunctionalsComponent: the component (central panel, below part) - showing a summary for the selected item on the tree (by functional) - */ - - "use strict"; - - let DetailsViewBase = __webpack_require__(20); - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let CalcSelectorBar = __webpack_require__(21); - let StatsViewer = __webpack_require__(22); - let EquationOfStateViewer = __webpack_require__(23); - let DataStore = __webpack_require__(2); - let LoadingPopup = __webpack_require__(4); - - - - class StructureDetails extends DetailsViewBase { - - constructor() { - super('Structure'); - this.groupsData; // the calcs groups data is necesary to be shown - - this.firstId; - this.lastId; - - this.element.innerHTML+=` - - <div style="float: left; width: 36%;"> - <div class="view-box"> - <div class="title">Structure </div> - <div class="viz-box" style="height: 400px; position: relative"></div> - - <div class="footer-flex-wrapper"> - - <div class="fields-container"> - <div><b><span>System type</span></b>: - <span class="struct-field" ></span> - </div> - <div class="structure-type-field" style="display: none"> - <b><span info-sys-data="structure-type">Structure type</span></b>: - <span class="structure-type-value" ></span> - </div> - <div class="structure-prototype-field" style="display: none"> - <b><span info-sys-data="structure-prototype">Structure prototype</span></b>: - <span class="structure-prototype-value" ></span> - </div> - <div class="strukturbericht-field" style="display: none"> - <b><span info-sys-data="strukturbericht">Strukturbericht designation</span></b>: - <span class="strukturbericht-value" ></span> - </div> - </div> - - <div class="footer-flex" style="display: none"> - - <div class="fields-container" - style="flex-basis: 70%; border-right: 1px solid #E4E4E4; "> - - <div> - <b><span info-sys-data="crystal-system">Lattice</span></b>: - <span class="lattice-value" ></span> - </div> - <div> - <b><span info-sys-data="space-group">Space group</span></b>: - <span class="space-group-value" ></span> - </div> - <div> - <b><span info-sys-data="point-group">Point group</span></b>: - <span class="point-group-value" ></span> - </div> - </div> - - <div style="flex-basis: 30%; margin-left: 30px;"> - <div class="fields-container"> - <div><b><span info-sys-data="wyckoff-position-population">Wyckoff sites</span></b></div> - <div class="wyckoff-sites-value"> </div> - </div> - </div> - - </div> - - </div> - - </div> - </div> - - <div style="float: left; width: 36%;"> - <div class="view-box"> - <div class="title">Calculations</div> - <div class="navTreeWrapper"></div> - - <div class="summary-title">Summary </div> - <div style="font-size: 0.85em; text-align: center; padding: 4px;">Based on the calculations selected above</div> - - <div class="info-fields summary-box"> - <!-- Lattice constants Cell volume, Density panel dynamically generated - --> - </div> - </div> - </div> - - <div style="float: right; width: 28%;"> - <div class="calc-specifics-box"> - - <div style="padding-top: 10px; " > - <div class="tree-leaf-title"></div> - </div> - - <div class="tree-leaf-viewer-host"></div> - - </div> - </div> - - </div> - </div> - `; - - this.navTreeWrapper = - this.element.getElementsByClassName("navTreeWrapper")[0]; - - let fields= this.element.getElementsByClassName('struct-field'); - this.systemTypeField= fields[0]; - this.structTypeField= this.element.querySelector('.structure-type-field'); - this.structTypeValue= this.element.querySelector('.structure-type-value'); - this.structPrototypeField= this.element.querySelector('.structure-prototype-field'); - this.structPrototypeValue= this.element.querySelector('.structure-prototype-value'); - this.strukturberichtField= this.element.querySelector('.strukturbericht-field'); - this.strukturberichtValue= this.element.querySelector('.strukturbericht-value'); - - this.lowerBox = this.element.querySelector('.footer-flex'); - this.latticeValue= this.element.querySelector('.lattice-value'); - this.spaceGroupValue= this.element.querySelector('.space-group-value'); - this.pointGroupValue= this.element.querySelector('.point-group-value'); - this.wyckoffValue= this.element.querySelector('.wyckoff-sites-value'); - - this.summaryByFunctionals = null; - - this.leafTitle = this.element.querySelector('.tree-leaf-title'); - - this.summaryBox = this.element.querySelector('.summary-box'); - - this.calcSpecificsBox = this.element.querySelector('.calc-specifics-box'); - - this.treeLeafViewer = new TreeLeafViewer(); - this.element.querySelector('.tree-leaf-viewer-host'). - appendChild(this.treeLeafViewer.element); - - this.vizBox = this.element.querySelector('.viz-box'); - - // For static ones - InfoSys.addToInfoSystem(this.element); - } - - - setMaterialData() { - let data = DataStore.getMaterialData(); - super.setMaterialData(data); - this.isBulk = (data.system_type === 'bulk'); - - this.systemTypeField.textContent= data.system_type; - this.structTypeField.style.display = - (this.isBulk && data.structure_type !== null ? 'block' : 'none'); - this.structPrototypeField.style.display = - (this.isBulk && data.structure_prototype !== null ? 'block' : 'none'); - this.strukturberichtField.style.display = - (this.isBulk && data.strukturbericht_designation !== null ? 'block' : 'none'); - - this.lowerBox.style.display = (this.isBulk ? 'flex' : 'none'); - - if (this.isBulk){ - this.structTypeValue.textContent= data.structure_type; - this.structPrototypeValue.textContent= data.structure_prototype; - this.strukturberichtValue.textContent= data.strukturbericht_designation; - this.spaceGroupValue.textContent = data.space_group_number - +' ('+data.space_group_international_short_symbol+')'; - this.pointGroupValue.textContent = data.point_group; - this.latticeValue.textContent = data.crystal_system; - - // wyckoff processing - let wyckoffMap = new Map(); - let valueSet= new Set(); - - for (var i = 0; i < data.elements.length; i++) { - let element = util.ELEMENTS[data.elements[i].label-1]; - if (wyckoffMap.has(element)){ - wyckoffMap.get(element).add(data.elements[i].wyckoff); - }else { - let newSet = new Set(); - newSet.add(data.elements[i].wyckoff); - wyckoffMap.set(element, newSet); - } - } - let wyckoffHTML= ''; - wyckoffMap.forEach((posSet, element) => { - let firstPos = true; - wyckoffHTML += '<tr> <td>'+element+': </td>'; - posSet.forEach( pos => { - if (firstPos){ - firstPos = false; - wyckoffHTML += '<td>'+pos+'</td></tr>'; - }else - wyckoffHTML += '<tr><td> </td><td>'+pos+'</td></tr>'; - }); - }); - - this.wyckoffValue.innerHTML = '<table>' + wyckoffHTML+'</table>'; - } - - InfoSys.addElementToInfoSystem(this.spaceGroupValue, - 'space-group.value:'+data.space_group_number); - InfoSys.addElementToInfoSystem(this.latticeValue, - 'crystal-system.value:'+data.crystal_system); - InfoSys.addElementToInfoSystem(this.pointGroupValue, - 'point-group.value:'+data.point_group); - //InfoSys.addElementToInfoSystem(this.wyckoffValue, 'wyckoff-position-population.value:'+); - } - - - updateSelection(leafIds /*Set*/) { - //console.log('StructureDetails.updateSelection: ', leafIds); - if (leafIds.size > 0){ - this.summaryBox.style.visibility = 'visible'; - - let summaryCalcSet = new Set(); - leafIds.forEach( leafId => { - let calc; - if (DataStore.getGroups().has(leafId)){ - calc = DataStore.getCalc(DataStore.getGroups().get(leafId).method_representative); - }else - calc = DataStore.getCalc(parseInt(leafId)); - summaryCalcSet.add(calc); - }); - - let calcMapByFunctional = getCalcMapByFunctional(summaryCalcSet); - if (this.summaryByFunctionals === null) - this.summaryByFunctionals = - new SummaryByFunctionalsComponent(calcMapByFunctional, this.summaryBox); - else - this.summaryByFunctionals.build(calcMapByFunctional); - - let counter= 0; - leafIds.forEach( calcId => { - counter++; - if (counter === 1) this.firstId = calcId; - else if (counter === leafIds.size) this.lastId = calcId; - }); - - }else{ - this.summaryBox.style.visibility = 'hidden'; - } - - function getCalcMapByFunctional(summaryCalcSet){ - - let functCalcMap = new Map(); - summaryCalcSet.forEach( calc => { - if (functCalcMap.has(calc.functional_type)){ - functCalcMap.get(calc.functional_type).add(calc); - - }else{ // New functional - let newFunctionalArray = new Set(); - newFunctionalArray.add(calc); - functCalcMap.set(calc.functional_type, newFunctionalArray); - } - }); - //console.log('functCalcMap: ',functCalcMap); - return functCalcMap; - } - } - - - updateMarkedLeaf(leafId){ - - if (leafId !== null){ - this.calcSpecificsBox.style.visibility = 'visible'; - //this.calcSpecificsBox.style.backgroundColor= '#FFF7EB'; - - if (DataStore.getGroups().has(leafId)){ - this.leafTitle.innerHTML = leafId+ - ' ('+DataStore.getGroups().get(leafId).calcs.size+')'; - }else - this.leafTitle.innerHTML = leafId; - - }else - this.calcSpecificsBox.style.visibility = 'hidden'; - //this.calcSpecificsBox.style.backgroundColor= 'white'; - //this.leafTitle.innerHTML = 'NO SELECTION'; - - this.treeLeafViewer.update(leafId, DataStore.getGroups().get(leafId)); - } - - } - - - class TreeLeafViewer{ - - constructor(hostClass){ - this.groupCalcs = null; - this.element = document.createElement('div'); - this.element.innerHTML = ` - <div> - - <div class="group-components" style="display: none"> - <div style="padding: 10px 0 30px 10px; " class="eos-host"> - </div> - - <div style="padding-top: 10px; " class="calc-selector-host"> - </div> - </div> - - <div class="info-fields"> - <div><b>Lattice constants</b></div> - <div class="latt-constants"></div> - <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b>: - <span class="volume-value" ></span> - </div> - <!-- <div><b>Pressure</b>: <span class="" ></span> </div>--> - <div class="density-field"><b>Density</b>: - <div class="stats-fields" > - <span info-sys-data="mass-density">Mass density</span> = - <span class="mass-density-value" ></span> - </div> - <div class="stats-fields" > - <span info-sys-data="atomic-density">Atomic density</span> = - <span class="atomic-density-value" ></span> - </div> - </div> - - <div class="energy-field"><b><span info-sys-data="energies">Energies</span></b> (code-specific)</div> - <div class="energy-descomp"> </div> - - <div class="wyckoff-pos-calc-field" > - <b><span info-sys-data="free-wyckoff-parameters">Wyckoff sites</span></b> - (fractional coordinates) - <div class="wyckoff-pos-calc-table"> </div> - </div> - - </div> - - </div> - `; - - this.groupComponents = this.element.querySelector('.group-components'); - - this.calcSelector = new CalcSelectorBar('calc-selector-bar','60%'); - this.element.querySelector('.calc-selector-host'). - appendChild(this.calcSelector.element); - - this.lattConstantsField = this.element.querySelector('.latt-constants'); - this.volumeField = this.element.querySelector('.volume-field'); - this.volumeValue = this.element.querySelector('.volume-value'); - this.densityField = this.element.querySelector('.density-field'); - this.massDensityValue = this.element.querySelector('.mass-density-value'); - this.atomicDensityValue = this.element.querySelector('.atomic-density-value'); - - this.energyField= this.element.querySelector('.energy-field'); - this.energyDescompValue= this.element.querySelector('.energy-descomp'); - - this.wyckoffPosField = this.element.querySelector('.wyckoff-pos-calc-field'); - this.wyckoffPosTable = this.element.querySelector('.wyckoff-pos-calc-table'); - - this.eosViewer = new EquationOfStateViewer(); - this.eosViewer.attach(this.element.querySelector('.eos-host'),320, 280); - - this.eosViewer.setClickPointListener( calc => { - this.groupCalcUpdate(calc+''); - }); - - InfoSys.addToInfoSystem(this.element); - - this._events(); - } - - - _events() { - - this.calcSelector.setPrevListener(e => { - //console.log('calcSelectorPrevBtn'); - if (this.groupIndex > 0){ - this.groupCalcUpdate(this.groupCalcs[--this.groupIndex]+''); - return this.groupIndex === 0; // the first - } - }); - - this.calcSelector.setNextListener( e => { - //console.log('calcSelectorNextBtn'); - if (this.groupIndex < this.groupCalcs.length-1){ - this.groupCalcUpdate(this.groupCalcs[++this.groupIndex]+''); - return this.groupIndex === this.groupCalcs.length-1; // the last - } - }); - - } - - - update(leafId, groupData){ - - this.representative = leafId; - this.isGroup = false; - //console.log('SET: ',groupData); - if (groupData !== undefined){ - - this.groupCalcs = Array.from(groupData.calcs); - this.groupIndex = this.groupCalcs.indexOf( - DataStore.getCalcReprIntId(this.representative)); - - this.isGroup = true; - this.groupComponents.style.display = 'block'; - - this.eosViewer.clear(); - let pointsX = [], pointsY = [];//, calcIds = []; - - this.groupCalcs.forEach( calcId => { - //calcIds.push(calcId); - let calc = DataStore.getCalc(calcId); - //console.log('CALC: ', calcId, calc); - pointsX.push(calc.cell_volume/1e-30); - let yVal = 555; // Trick: signal value - calc.energy.forEach( e => { - if (e.e_kind === 'Total E'){ - yVal = e.e_val/1.602176565e-19; - } - }); - pointsY.push(yVal); - }); - - // Method re - let mReprCalc = DataStore.getCalc(groupData.method_representative); - //console.log('mReprCalc: ', mReprCalc); - let eZero; - mReprCalc.energy.forEach( e => { - if (e.e_kind === 'Total E') - eZero = e.e_val/1.602176565e-19; - }); - //console.log('CALC: ', pointsX, pointsY, groupCalcsForChart); - this.eosViewer.draw(pointsX, pointsY, this.groupCalcs, eZero); - - }else{ - this.groupComponents.style.display = 'none'; - } - this.groupCalcUpdate(leafId); - } - - - groupCalcUpdate(leafId){ - - if (leafId !== null){ - - let calcId = DataStore.getCalcReprIntId(leafId); - if (this.groupCalcs !== null){ - this.groupIndex = this.groupCalcs.indexOf(calcId); - if (this.groupIndex >= 0){ - let t = calcId+' ('+(this.groupIndex+1)+'/'+this.groupCalcs.length+')'; - this.calcSelector.setState(t, this.groupIndex === 0, - this.groupIndex === this.groupCalcs.length-1); - - this.eosViewer.selectCalc(calcId); - } - } - - //console.log('LEAF UPDATE', calc.id, this.groupIndex); - let is2Dsystem = (DataStore.getMaterialData().system_type === '2D'); - let isBulk = (DataStore.getMaterialData().system_type === 'bulk'); - let calc = DataStore.getCalc(calcId); - let lattParams= util.getNumberArray(calc.lattice_parameters); - - let lattCFieldHTML = ((is2Dsystem || isBulk) ? - `<div>b = ${util.m2Angstrom(lattParams[1])}</div>` : ''); - lattCFieldHTML += (isBulk ? - `<div>c = ${util.m2Angstrom(lattParams[2])}</div>` : ''); - - let lattBetaGammaFieldHTML = ((is2Dsystem || isBulk) ? - `<div>α = ${util.rad2degree(lattParams[3])}</div>` : ''); - - lattBetaGammaFieldHTML += (isBulk ? - `<div>β = ${util.rad2degree(lattParams[4])}</div> - <div>γ = ${util.rad2degree(lattParams[5])}</div>` : ''); - - this.lattConstantsField.innerHTML= ` - <div style="float: left; "> - <div>a = ${util.m2Angstrom(lattParams[0])}</div> - ${lattCFieldHTML} - </div> - <div style="float: left; padding-left: 40px;"> - ${lattBetaGammaFieldHTML} - </div> - <div style="clear: both;padding: 0"></div> - `; - - this.densityField.style.display = (isBulk ? 'block' : 'none'); - this.volumeField.style.display = (isBulk ? 'block' : 'none'); - if (isBulk){ // bulk type - this.volumeValue.innerHTML= util.m3ToAngstrom3(calc.cell_volume); - //this.pressureCalcField.textContent= calc.pressure; - this.atomicDensityValue.innerHTML= util.toAngstromMinus3(calc.atomic_density); - this.massDensityValue.innerHTML= calc.mass_density.toFixed(1)+' kg/m<sup>3</sup>'; - } - - let r= util.serverReq(util.getCalcEnergiesURL(DataStore.getMaterialData().id, calc.id), () => { - let value = false; - if (r.status === 200){ - let energies= JSON.parse(r.response).results; - for (var i = 0; i < energies.length; i++) - if (energies[i].e_kind === 'Total E'){ - value = true; - this.energyDescompValue.innerHTML = - '<div>Total E = '+util.J2eV(energies[i].e_val)+' eV</div>'; - } - } - this.energyField.style.display = (value ? 'block' : 'none'); - this.energyDescompValue.style.display = (value ? 'block' : 'none'); - }); - - let thereIsWyckoffData = - (DataStore.getMaterialData().has_free_wyckoff_parameters - && calc.wyckoff_groups_json.length > 0); - - this.wyckoffPosField.style.display = (thereIsWyckoffData ? 'block' : 'none'); - - if (thereIsWyckoffData){ - let wyckoffMap = new Map(); // Map(element, Array of pairArray[w-pos, coor]) - calc.wyckoff_groups_json.forEach( d => { - // Only entries having items in .variables are included - if ( Object.keys(d.variables).length !== 0 ){ - - let varsHtml = ''; - //d.variables.forEach( v => { varsHtml += '<p>'+v+'</p>'; } ); - for (let v in d.variables) { - varsHtml += ''+v+' = '+d.variables[v].toFixed(2)+'<br>'; - } - - let wyckoffVarsPair = []; - wyckoffVarsPair.push(d.wyckoff_letter); - wyckoffVarsPair.push(varsHtml); - - if (wyckoffMap.has(d.element)){ - wyckoffMap.get(d.element).push(wyckoffVarsPair); - }else { - wyckoffMap.set(d.element, [wyckoffVarsPair]); - } - } - - }); - //console.log('wyckoffMap', wyckoffMap); - - let wyckoffHTML= ''; - wyckoffMap.forEach((posSet, element) => { - - posSet.sort( (a, b) => { - return (a[0] > b[0] ? 1 : -1); - }); - - let firstPos = true; - wyckoffHTML += '<tr > <td style="width: 30%;">'+element+' </td>'; - posSet.forEach( pos => { - if (firstPos){ - firstPos = false; - wyckoffHTML += '<td style="width: 30%; ">'+pos[0]+'</td><td style="width: 40%;">'+pos[1]+'</td></tr>'; - }else - wyckoffHTML += '<tr><td> </td><td>'+pos[0]+'</td><td>'+pos[1]+'</td></tr>'; - }); - }); - - this.wyckoffPosTable.innerHTML = '<table id="calc-wyckoff">' + wyckoffHTML+'</table>'; - - /* - for (var i = 0; i < data.elements.length; i++) { - let element = util.ELEMENTS[data.elements[i].label-1]; - if (wyckoffMap.has(element)){ - wyckoffMap.get(element).add(data.elements[i].wyckoff); - }else { - let newSet = new Set(); - newSet.add(data.elements[i].wyckoff); - wyckoffMap.set(element, newSet); - } - } - */ - - } - - - }else{ - console.log('THIS dOESNT BE REACHED'); - } - } - - } - - - class SummaryByFunctionalsComponent{ - - constructor(calcMapByFunctional, hostElement){ - this.calcMapByFunctional = calcMapByFunctional; - this.hostElement = hostElement; - this.graphTrigger = null; - this.viewType = 'text'; - this.functional = null; - this.hostElement.innerHTML+=` - <div style="float: left" > - <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab" - viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;"> - <rect x="0" y="0" width="2" height="15" /> - <rect x="3" y="5" width="1.8" height="7" /> - <rect x="6" y="3" width="1.8" height="9" /> - <rect x="9" y="6" width="1.8" height="6" /> - <rect x="12" y="2" width="1.8" height="10" /> - <rect x="2" y="13" width="13" height="2" /> - </svg> - <svg xmlns="http://www.w3.org/2000/svg" class="text-tab" - viewBox="0 0 15 15" width="15" height="15" style="fill: #777;"> - <rect x="0" y="1" width="15" height="2.5" /> - <rect x="0" y="6" width="15" height="2.5" /> - <rect x="0" y="11" width="15" height="2.5" /> - </svg> - </div> - - <div class="functional-tabs" style="float: right"> - </div> - - <div style="clear: both;"></div> - - <div class="content-placeholder" > - - <div style="display: block" class="text-panel" > - <div><b>Lattice constants</b>: - <div class="stats-fields latt-constants-field" > - </div> - </div> - <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b> (Å<sup>3</sup>): - <div class="stats-fields volume-value" > </div> - </div> - <div class="density-field"><b>Density</b> : - <div > - <div class="stats-fields" > - <span info-sys-data="mass-density">Mass density</span> (kg/m<sup>3</sup>) = - <span class="mass-density-value" ></span> - </div> - <div class="stats-fields" > - <span info-sys-data="atomic-density">Atomic density</span> (Å<sup>-3</sup>) = - <span class="atomic-density-value" ></span> - </div> - </div> - </div> - </div> - - <div style="display:none" class="chart-panel" > - <div class="charts-placeholder" > </div> - <div class="charts-selector" > - - </div> - </div> - - </div> - `; - this.chartTab = this.hostElement.querySelector('.chart-tab'); - this.textTab = this.hostElement.querySelector('.text-tab'); - this.functionalTabs = this.hostElement.querySelector('.functional-tabs'); - this.chartPanel = this.hostElement.querySelector('.chart-panel'); - this.textPanel = this.hostElement.querySelector('.text-panel'); - this.lattConstantsField = this.hostElement.querySelector('.latt-constants-field'); - this.volumeField = this.hostElement.querySelector('.volume-field'); - this.volumeFieldValue = this.hostElement.querySelector('.volume-value'); - this.densityField = this.hostElement.querySelector('.density-field'); - this.massDensityValue = this.hostElement.querySelector('.mass-density-value'); - this.atomicDensityValue = this.hostElement.querySelector('.atomic-density-value'); - - - this.calcMapByFunctional.forEach( (calcs, functionalName) =>{ - this.functionalTabs.innerHTML += - '<span class="tab" data-tab="'+functionalName+'">'+functionalName+'</span>'; - }); - - - this.statsViewer = new StatsViewer(); - let chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder'); - this.statsViewer.attach(chartsPlaceholder, 350, 200); - - this.chartsSelector = this.hostElement.querySelector('.charts-selector'); - - this.build(calcMapByFunctional); - - this.chartTab.addEventListener( "click", e => { - this.chartTab.style.fill = '#777'; - this.viewType = 'chart'; - this.textTab.style.fill = '#c7c7c7'; - this.chartPanel.style.display = 'block'; - this.textPanel.style.display = 'none'; - }); - - this.textTab.addEventListener( "click", e => { - this.textTab.style.fill = '#777'; - this.viewType = 'text'; - this.chartTab.style.fill = '#c7c7c7'; - this.textPanel.style.display = 'block'; - this.chartPanel.style.display = 'none'; - }); - - this.functionalTabs.addEventListener( "click", e => { - if (e.target.className === 'tab'){ - this.statsViewer.clear(); - this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') - .className = 'tab'; - this.functional = e.target.getAttribute('data-tab'); - this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') - .className = 'tab-selected'; - this._setData(); - } - }); - - this.chartsSelector.addEventListener( "click", e => { - if (e.target.className.indexOf('quantity') === 0){ - this.statsViewer.clear(); - let quantity = e.target.getAttribute('data-quantity'); - let stats = this.functionalQuantityMap.get(this.functional).get(quantity); - this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); - - this.chartsSelector.querySelector('.quantity-selected').className = 'quantity'; - e.target.className = 'quantity-selected'; - } - }); - - } - - _setData(){ - let is2Dsystem = (DataStore.getMaterialData().system_type === '2D'); - let isBulk = (DataStore.getMaterialData().system_type === 'bulk'); - let statsMap = this.functionalQuantityMap.get(this.functional); - - let lattCFieldHTML = ((is2Dsystem || isBulk) ? - `<div>b (Å) = ${statsMap.get('lattice_b').html}</div>` : ''); - - lattCFieldHTML += (isBulk ? - `<div>c (Å) = ${statsMap.get('lattice_c').html}</div>` : ''); - // Set text data - this.lattConstantsField.innerHTML = ` - <div style="float: left; "> - <div>a (Å) = ${statsMap.get('lattice_a').html}</div> - ${lattCFieldHTML} - </div> - <div style="float: left; padding-left: 40px;"> - ${util.getLatticeAnglesValues( - this.calcMapByFunctional.get(this.functional), is2Dsystem, isBulk)} - </div> - <div style="clear: both;padding: 0"></div> - `; - - let chartSelectorHTML= ` - <span class="quantity-selected" data-quantity="lattice_a">a</span> - `; - - if (is2Dsystem || isBulk) - chartSelectorHTML += `<span class="quantity" data-quantity="lattice_b">b</span>`; - - //console.log('statsMap', statsMap, statsMap.get('volume')); - this.densityField.style.display = (isBulk ? 'block' : 'none'); - this.volumeField.style.display = (isBulk ? 'block' : 'none'); - if (isBulk){ // bulk type - this.volumeFieldValue.innerHTML = statsMap.get('volume').html; - this.massDensityValue.innerHTML = statsMap.get('mass_density').html; - this.atomicDensityValue.innerHTML = statsMap.get('atomic_density').html; - chartSelectorHTML += ` - <span class="quantity" data-quantity="lattice_c">c</span> - <span class="quantity" data-quantity="volume">volume</span> - <span class="quantity" data-quantity="mass_density">mass density</span> - <span class="quantity" data-quantity="atomic_density">atomic density</span> - `; - } - - this.chartsSelector.innerHTML = chartSelectorHTML; - - // Set Charts data - let stats = statsMap.get('lattice_a'); - this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); - } - - - - build(calcMapByFunctional){ - this.calcMapByFunctional = calcMapByFunctional; - this.graphTrigger = null; - this.statsViewer.clear(); - //this.functional = null; - //this.functionalTabs.innerHTML = ''; - this.unfoldedElement = null; - this.functionalQuantityMap = new Map(); - - // Hide and deselect all the tabs before knowing the active ones - for (var i = 0; i < this.functionalTabs.children.length; i++) { - //console.log('functionalTabs',this.functionalTabs.children[i]); - this.functionalTabs.children[i].style.display = 'none'; - this.functionalTabs.children[i].className = 'tab'; - } - - // For each active functional - this.calcMapByFunctional.forEach( (calcs, functionalName) =>{ - let statsMap = util.getQuantityStatsMap(calcs); - this.functionalQuantityMap.set(functionalName, statsMap); - //show the active ones - this.functionalTabs.querySelector('[data-tab="'+functionalName+'"]') - .style.display = 'inline'; - }); - - // Select the selected functional - let functionals = Array.from( this.calcMapByFunctional.keys() ); - // If there isn't selected functional or the current functional is not active - if (this.functional === null || functionals.indexOf(this.functional) < 0) - this.functional = functionals[0]; // the first one is selected - this._setData(); - this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') - .className = 'tab-selected'; - - InfoSys.addToInfoSystem(this.hostElement); - } - - } - - - // EXPORTS - module.exports = StructureDetails; - - -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Base class from which other main 'Details' view containers inherit. - Every of these containers shows all the material info related to a type of info: - Structure, Electronic Structure, etc - */ - - "use strict"; - - let util = __webpack_require__(1); - let DataStore = __webpack_require__(2); - - - class DetailsViewBase { - - constructor(domId) { - this.element = document.createElement('div'); - this.element.setAttribute('id',domId); - this.gotoResultsListener= null; - this.gotoOverviewListener= null; - this.element.innerHTML= '<div class="material-title"></div>'; - this.element.style.display= 'none'; - } - - - attachAndSetEvents(element){ - element.appendChild(this.element); - this.materialTitle= this.element.querySelector('.material-title'); - } - - - attachNavTree(navTree){ - navTree.attach(this.navTreeWrapper); - } - - - setVisible(){ - this.element.style.display= 'block'; - } - - - setMaterialData() { - this.materialTitle.innerHTML= util.getMaterialTitle(DataStore.getMaterialData()); - } - - - updateCalcs(calcs){ - } - - - updateMarkedCalc(calc){ - } - - } - - // EXPORTS - module.exports = DetailsViewBase; - - -/***/ }, -/* 21 */ -/***/ function(module, exports) { - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This class implements the bar to select calculations on a material - */ - - - class CalcSelectorBar{ - - constructor(className, width){ - this.first = true; - this.last = false; - this.element = document.createElement('div'); - this.element.className = className; - if (width !== undefined) this.element.style.width = width; - this.element.innerHTML = ` - <div class="prev-sel-btn" style="float: left; width: 20%;"> - <div style="padding-left: 10%;"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.069 11.872" width="20px"> - <path transform="scale(0.7) translate(-346.291 -664.481)" - d="M356.36,666.024l-1.544-1.544-8.525,8.513,8.493,8.447,1.544-1.544-6.8-6.9Z" /> - </svg> - </div> - </div> - <div class="calc-sel-text" style="float: left; width: 60%;"> - NOT Calculation - </div> - <div class="next-sel-btn" style="float: right; width: 20%;"> - <div style="padding-right: 10%;"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="-10.069 -11.872 10.069 11.872" width="20px"> - <g transform="rotate(180) scale(0.7)"> - <path d="M10.069,1.544,8.525,0,0,8.513,8.493,16.96l1.544-1.544-6.8-6.9Z"/> - </g> - </svg> - </div> - </div> - <div style="clear: both;"></div> - `; - - this.prevBtn = this.element.querySelector('.prev-sel-btn'); - this.prevIcon = this.element.querySelector('.prev-sel-btn path'); - this.calcSelectorTxt = this.element.querySelector('.calc-sel-text'); - this.nextBtn = this.element.querySelector('.next-sel-btn'); - this.nextIcon = this.element.querySelector('.next-sel-btn path'); - this._styleButtons(); - this._events(); - } - - - _events() { - this.prevBtn.addEventListener( "click", e => { - e.preventDefault(); - if (this.first) return; - /*** repensar esto es problematico porque necesita poder ser configurado desde fuera **/ - //if (this.last) this.last = false; - this.first = this.prevListener(); - this.last = false; - this._styleButtons(); - }); - - this.nextBtn.addEventListener( "click", e => { - e.preventDefault(); - if (this.last) return; - //if (this.first) this.first = false; - // this.last = this.nextListener(); - this.first = false; - this.last = this.nextListener(); - this._styleButtons(); - }); - } - - - _styleButtons(){ - this.prevIcon.setAttribute("class", - 'calc-selector-icon'+(this.first ? '-disabled' : '')); - this.nextIcon.setAttribute("class", - 'calc-selector-icon'+(this.last ? '-disabled' : '')); - } - - - setPrevListener(listener){ - this.prevListener = listener; - } - - - setNextListener(listener){ - this.nextListener = listener; - } - - - setState(text, first, last){ - this.calcSelectorTxt.innerHTML = text; - this.first = first; - this.last = last; - this._styleButtons(); - } - - } - - - // EXPORTS - module.exports = CalcSelectorBar; - - -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - The StatsViewer class implements a graphical UI component composed of - a points plotter and a frecuency graph. - (classes defined in this file MainGraph and FrequencyGraph) - */ - - - "use strict"; - - let svg = __webpack_require__(9); - let PlotterBase = __webpack_require__(18); - - - class MainGraph extends PlotterBase{ - - constructor() { - super({left: 54, right: 20, top: 20, bottom: 30}); - this.tooltip; - } - - - drawPoints(points){ - for (let i = 0; i < points.length; i++) { - //console.log('drawPoint '+this.xRel, i, this.yRel, points[i]); - let pointElement = svg.addPoint(this.plotArea, - this.xRel*i, -this.yRel*(points[i] - this.yMin), 2, 'stats-viewer-point'); - - pointElement.addEventListener('mouseover', (e) => { - this.tooltip = svg.addText(this.plotArea, e.target.getBBox().x+6, - e.target.getBBox().y-4, (points[i]).toFixed(2), 'start', 'tooltip'); - /*getBBox() Rect data (x, y, w, h) of the element were the event happendd, - coordenate system: the parent element coordinate system */ - }); - pointElement.addEventListener('mouseout', (e) => { - svg.removeElement(this.tooltip); - }); - } - } - - } - - - class FrequencyGraph extends PlotterBase{ - - constructor() { - super({left: 4, right: 10, top: 20, bottom: 30}); - } - - drawBars(points, yMin, yRange){ - let ranges = [0,0,0,0,0,0,0,0,0,0]; - points.forEach(point => { - let rangeIndex = Math.floor(((point-yMin)/yRange)*10); - if (rangeIndex > 9) rangeIndex = 9; - ranges[rangeIndex] += 1; - }); - ranges.forEach((value, index) => { - let yBar= (index + 0.5)*this.yRel; - svg.addLine(this.plotArea, 0, -yBar, value*this.xRel, -yBar, 'bar'); - }); - } - } - - - class StatsViewer{ - - constructor() { - this.mainGraph = new MainGraph(); - this.freqGraph = new FrequencyGraph(); - } - - - attach(element, width, height){ - this.mainGraph.attach(element, width/2 + 24, height); - this.freqGraph.attach(element, width/2 - 24, height); - } - - - drawPoints(points, label, min, max){ - let defMin = min, defMax = max; - if (max === min){ defMin = min-1; defMax = max+1; } - - this.mainGraph.setRangeAndLabels('Calculation', 0, points.length, label, - defMin, defMax); - this.mainGraph.drawAxis(null,(max === min ? 2: 4),(min > 1000 ? 0 : 2)); - this.mainGraph.drawPoints(points); - - let freqRange= (points.length < 10 ? 10 : Math.floor(points.length/10)*10); - this.freqGraph.setRangeAndLabels('Occurrence', 0, freqRange, '', 0, 10); - this.freqGraph.drawAxis(2, 1, 1); - this.freqGraph.drawBars(points, this.mainGraph.yMin, - this.mainGraph.yMax - this.mainGraph.yMin); - } +class StatsViewer{ + constructor() { + this.mainGraph = new MainGraph(); + this.freqGraph = new FrequencyGraph(); + } - clear(){ - this.mainGraph.clear(); - this.freqGraph.clear(); - } - } + attach(element, width, height){ + this.mainGraph.attach(element, width/2 + 24, height); + this.freqGraph.attach(element, width/2 - 24, height); + } - // EXPORTS - module.exports = StatsViewer; + drawPoints(points, label, min, max){ + let defMin = min, defMax = max; + if (max === min){ defMin = min-1; defMax = max+1; } + this.mainGraph.setRangeAndLabels('Calculation', 0, points.length, label, + defMin, defMax); + this.mainGraph.drawAxis(null,(max === min ? 2: 4),(min > 1000 ? 0 : 2)); + this.mainGraph.drawPoints(points); -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - Equation of State plotter implementation - */ - - "use strict"; - - let svg = __webpack_require__(9); - let PlotterBase = __webpack_require__(18); - - - class EquationOfStateViewer extends PlotterBase{ - - constructor() { - super({left: 60, right: 20, top: 30, bottom: 40}); - this.tooltip; - this.calcPointMap= new Map(); - this.pointSelected = null; - } + let freqRange= (points.length < 10 ? 10 : Math.floor(points.length/10)*10); + this.freqGraph.setRangeAndLabels('Occurrence', 0, freqRange, '', 0, 10); + this.freqGraph.drawAxis(2, 1, 1); + this.freqGraph.drawBars(points, this.mainGraph.yMin, + this.mainGraph.yMax - this.mainGraph.yMin); + } - draw(pointsX, pointsY, calcIds, eZero){ - - for (let i = 0; i < pointsY.length; i++) { - if (pointsY[i] !== 555) pointsY[i] -= eZero; - } - - function remove555(points){ - let goodPoints = []; - points.forEach( p => { - if (p !== 555) goodPoints.push(p); - }); - return goodPoints; - } - let goodPointsY = remove555(pointsY); - - let xMin = Math.min.apply(null,pointsX); - let xMax = Math.max.apply(null,pointsX); - let yMin = 0; - let yMax = Math.max.apply(null,goodPointsY); - - if (xMin === xMax) { xMin -= 1; xMax += 1; } - else{ - let gap = xMax - xMin; - xMin -= gap*0.1; xMax += gap*0.1; - } - if (yMin === yMax) { yMin -= 1; yMax += 1; } - else{ - let gap = yMax - yMin; - yMin -= gap*0.15; yMax += gap*0.1; - } - - this.setRangeAndLabels('Volume (ų)', xMin, xMax,'E - Eₘᵢₙ (eV)', yMin, yMax); - this.drawAxis(2, null); - // Y axis labels drawing - let self = this; - function drawYAxisLabel(element, y){ - let pixelY = self.y(y); - svg.addLine(element, 0, pixelY, -3, pixelY, 1); - let numberToPaint= -(pixelY/self.yRel) + self.yMin; - svg.addText(element,-5, pixelY+3, numberToPaint.toFixed(3), 'end', 'statisticsviewersteps'); - } - drawYAxisLabel(this.plotArea, 0); - drawYAxisLabel(this.plotArea, yMax/2); - drawYAxisLabel(this.plotArea, yMax); - - // zero line drawing - svg.addLine(this.plotArea, 0, this.y(0), this.plotRangeX, this.y(0), 'zeroline'); - - svg.addText(this.plotArea, this.x(xMax), - this.y(0)+12, 'Eₘᵢₙ: '+eZero.toFixed(3)+' eV', 'end', 'axis-steps'); - // points drawing - for (let i = 0; i < pointsX.length; i++) { - - let styleClass = (i === 0 ? 'eos-viewer-sel' : 'eos-viewer'); - let r = (i === 0 ? 6 : 3); - let yVal = (pointsY[i] === 555 ? 20 : this.y(pointsY[i])); // Trick - let pointElement = - svg.addPoint(this.plotArea,this.x(pointsX[i]), yVal, r - , styleClass); - if (i === 0) this.pointSelected = pointElement; - - pointElement.addEventListener('mouseover', e => { - this.tooltip = svg.addText(this.plotArea, e.target.getBBox().x+10, - e.target.getBBox().y-10, 'Calc '+calcIds[i], 'middle', 'tooltip'); - }); - pointElement.addEventListener('mouseout', e => { - svg.removeElement(this.tooltip); - }); - pointElement.addEventListener('click', e => { - //console.log('ID',calcIds[i]); - this.clickPointListener(calcIds[i]); - }); - this.calcPointMap.set(calcIds[i], pointElement); - } - } + clear(){ + this.mainGraph.clear(); + this.freqGraph.clear(); + } +} - selectCalc(calcId){ - this.pointSelected.setAttribute('class', 'eos-viewer'); - this.pointSelected.setAttribute('r', 3); - this.pointSelected = this.calcPointMap.get(calcId); - this.pointSelected.setAttribute('class', 'eos-viewer-sel'); - this.pointSelected.setAttribute('r', 6); - } +// EXPORTS +module.exports = StatsViewer; - setClickPointListener(listener){ - this.clickPointListener = listener; - } +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This file implements the user guidance system. This complex system for + showing tips to the user is specified in the project documentation + */ + + + + +//let util = require('util.js'); + +let body = document.querySelector('body'); +let tips = []; +let tipData = []; +let currentTip;// = 1; +let independentTips = []; // if null => showing else if 'off' not showing +let addBox, table, searchBox, propertiesTabs; +let searchBarShowing = false; + + +function createUserMsg(i, width){ + let element= document.createElement('div'); + element.setAttribute('class','user-guidance'); + element.innerHTML = '<img src="img/tip'+i+'.png" width="'+width+'px" />'; + element.style.position = 'absolute'; + element.style.display = 'none'; + body.appendChild(element); + return element; +} + + +function showTip(tipNumber){ + if (tipNumber === 8 && !searchBarShowing) return; // Do nothing + if (tipNumber === 4 && !searchBarShowing) return; // Do nothing + + let r = tipData[tipNumber].domTarget.getBoundingClientRect(); +//console.log("GGGGGGGGG: ",r); + tips[tipNumber].style.top = (r.top + tipData[tipNumber].top + window.pageYOffset)+'px'; + tips[tipNumber].style.left = (r.left + tipData[tipNumber].left)+'px'; + tips[tipNumber].style.display = 'block'; +} + +// Init per user session +function init(addBoxP, tableP, searchBoxP, propertiesTabsP){ + addBox = addBoxP; + table = tableP; + searchBox = searchBoxP; + propertiesTabs = propertiesTabsP; + + independentTips[3] = localStorage.getItem('tip3'); + independentTips[4] = localStorage.getItem('tip4'); + independentTips[7] = localStorage.getItem('tip7'); + + createUserTips(); + let currentTip = localStorage.getItem('currentTip'); + if (currentTip === null){ // First time + updateCurrentTip(1); + }else{ + currentTip = parseInt(currentTip); + if (currentTip < 10){ // regular case + updateCurrentTip(currentTip); + }//else { currentTip === 10 the guidance has finished + } +} + + +function createUserTips(){ + + if (tips.length === 0){ + tips[1] = createUserMsg(1, 220); + tipData[1] = {domTarget: addBox, top: -70, left: -240 }; + tips[2] = createUserMsg(2, 280); + tipData[2] = {domTarget: addBox, top: -110, left: 80 }; + tips[3] = createUserMsg(3, 180); + tipData[3] = {domTarget: table, top: 180, left: 720 }; + tips[4] = createUserMsg(4, 240); + tipData[4] = {domTarget: searchBox, top: 45, left: -250 }; + tips[5] = createUserMsg(5, 210); + tipData[5] = {domTarget: addBox, top: -130, left: 70}; + tips[6] = createUserMsg(6, 240); + tipData[6] = {domTarget: addBox, top: -100, left: 370}; + tips[7] = createUserMsg(7, 220); + tipData[7] = {domTarget: propertiesTabs, top: 160, left: -40}; + tips[8] = createUserMsg(8, 240); + tipData[8] = {domTarget: searchBox, top: 30, left: 760}; + + // Event + tips[1].addEventListener( "click", closeAndShowNext); + tips[2].addEventListener( "click", closeAndShowNext); + tips[3].addEventListener( "click", e => { closeIndependentTip(3) }); + tips[4].addEventListener( "click", e => { closeIndependentTip(4) }); + tips[5].addEventListener( "click", closeAndShowNext); + tips[6].addEventListener( "click", closeAndShowNext); + tips[7].addEventListener( "click", e => { closeIndependentTip(7) }); + tips[8].addEventListener( "click", e => { + e.target.style.display = 'none'; + updateCurrentTip(10); + }); + } +} + + +function setFinal(){ + searchBarShowing = true; + if (currentTip < 10){ + tips[currentTip].style.display = 'none'; + updateCurrentTip(8);//currentTip = 4; + showTip(currentTip); + } + showIndependentTip(4, true); +} + + +function closeIndependentTip( tipNumber){ + //e.preventDefault(); + tips[tipNumber].style.display = 'none'; + localStorage.setItem('tip'+tipNumber, 'off'); + independentTips[tipNumber] = 'off'; +} + + +function closeAndShowNext(e){ + e.preventDefault(); + //console.log("closeAndShowNext",currentTip); + e.target.style.display = 'none'; + + switch (currentTip) { + case 2: currentTip = 5; break; + + case 6: currentTip = 8; break; + + default: // 1 , 5 + currentTip++; + } + updateCurrentTip(currentTip); + showTip(currentTip); +} + + +function showIndependentTip(tipNumber, value){ + //console.log("showIndependentTip",tipNumber); + if (independentTips[tipNumber] === null){ // Tip has not been removed (clicked) + if (value) showTip(tipNumber); + else tips[tipNumber].style.display = 'none'; + } +} + + + + +function show(value, tip3, tip7){ // Global show - the UserGuidance is shown or hidden at all + if (currentTip < 10){ // sequential tips + if (value) showTip(currentTip); + else tips[currentTip].style.display = 'none'; + } + // Independent tips + showIndependentTip(3, value && tip3); + showIndependentTip(4, value); + showIndependentTip(7, value && tip7); +} + + +function updateCurrentTip(value){ + currentTip = value; + localStorage.setItem('currentTip', value); + //console.log('localStorage.currentTip:',localStorage.getItem('currentTip')); +} + + + + +// EXPORTS +module.exports = {init, setFinal, show, showIndependentTip} + + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { - x(x){ - return this.xRel*(x - this.xMin); - } +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - y(y){ - return -this.yRel*(y - this.yMin); - } + /* + This file is the application entry point. + It defines some app level components (Breadcrumb) and + initializes several more (app level events, app routing, authentication) + */ + + + + +let util = __webpack_require__(0); +let LoadingPopup = __webpack_require__(4); +let FlaggingFormPopup = __webpack_require__(18); +let PubSub = __webpack_require__(19); +let Router = __webpack_require__(20); +let MaterialMod = __webpack_require__(9); +let SearchModule = __webpack_require__(33); +let UserGuidance = __webpack_require__(15); +let DataStore = __webpack_require__(1); + + +// main DOM elements +let contentElement = document.getElementById('content'); +let titleElement = document.querySelector('title'); + + +/********* 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 => { + FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus()); +}); + - } +/*********** App Breadcrumb component definition ***************/ +class Breadcrumb { + + constructor() { - // EXPORTS - module.exports = EquationOfStateViewer; + this.element = document.querySelector('#breadcrumb-placeholder'); + this.element.innerHTML = ` + <span class="goto-page Search">Search</span> + <span class="goto-page Overview"> > <span>Overview</span></span> + <span class="Details"> + > + <select class="details-dropdown" > + <option value="structure">Structure</option> + <option value="electronicstruct">Electronic structure</option> + <option value="methodology">Methodology</option> + <option value="thermalprops">Thermal Properties</option> + <!-- elasticconst--> + </select> + </span> + `; + this.overviewSel = this.element.querySelector('.Overview'); + this.detailsSel = this.element.querySelector('.Details'); + this.detailsDropDown = this.element.querySelector('.details-dropdown'); + // Events + this.element.querySelector('.Search').addEventListener( "click", e => { + util.setBrowserHashPath('search'); + }); -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - 'Details' view container that shows all the material info related to - its electronic structure. - - This container is quite complex. - - In the file there are two defined (classes) components used in the container: - - SummaryByFunctionalsComponent: the component (left panel, below part) - showing a band gap summary info by functional. - - BZViewerWrapper: This component is a wrapper for the Brillouin zone viewer - */ - - "use strict"; - - let DetailsViewBase = __webpack_require__(20); - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let CalcSelectorBar = __webpack_require__(21); - let StatsViewer = __webpack_require__(22); - let BSDOSPlotter = __webpack_require__(25); - let PlotterBase = __webpack_require__(18); - let plotter= new PlotterBase(); - let DataStore = __webpack_require__(2); - let LoadingPopup = __webpack_require__(4); - - function setFermiVizContent(fermiBox,url){ - let sceneContent; - if (url === ''){ - fermiBox.innerHTML= '';//fermiBox.removeChild(plotter.canvas); - plotter.attach(fermiBox,undefined,316); - plotter.setNoData(); - } - else{ - sceneContent= '<inline url="'+url+'"> </inline>'; - fermiBox.innerHTML= - `<x3d id="x3dframe" width='290px' height='290px' > - <scene>${sceneContent} </scene> - </x3d>`; - x3dom.reload(); - } + this.overviewSel.addEventListener('click', () => { + util.setBrowserHashPath('material', util.materialId); + }); - } + this.detailsDropDown.addEventListener('change', e => { + util.setBrowserHashPath('material', + DataStore.getMaterialData().id+'/'+e.target.value); + }); - class ElectronicStructDetails extends DetailsViewBase{ + let self = this; + function adjustDropdownOptions() { + let esOption = self.detailsDropDown.querySelector('option[value="electronicstruct"]'); + if (!DataStore.hasElecStructureData) self.detailsDropDown.removeChild(esOption); - constructor() { - super('Electronic Structure'); - this.firstId; - this.lastId; - this.element.innerHTML+=` - <div style="float: left; width: 30%;"> - <div class="view-box"> - <div class="title">Calculations </div> - <div class="navTreeWrapper"></div> + let thOption = self.detailsDropDown.querySelector('option[value="thermalprops"]'); + if (!DataStore.hasThermalData) self.detailsDropDown.removeChild(thOption); + // Remove because we want it's executed once + self.detailsDropDown.removeEventListener('focus', adjustDropdownOptions); + } + this.detailsDropDown.addEventListener('focus', adjustDropdownOptions); + } - <div class="summary-title">Summary</div> - <div class="footer summary-box" style="border-top: 0"> - </div> - </div> - </div> - <div style="float: right; width: 70%;"> - <div class="view-box e-structure-box"> + setState(appModule, param){ + let overviewSelLabel = this.overviewSel.querySelector('span'); + overviewSelLabel.style.fontWeight = 'normal'; - <div class="title">Electronic structure</div> + if (appModule === 'search'){ + this.overviewSel.style.display = 'none'; + this.detailsSel.style.display = 'none'; - <div style="padding-top: 10px;"> - <div class="calc-selector-host"></div> - </div> + /* + if (param === 'results'){ + this.resultsSel.style.display = 'inline'; + this.resultsSel.querySelector('span').style.fontWeight = 'bold'; + this.element.style.visibility = 'visible'; + }else this.element.style.visibility = 'hidden'; + */ + this.element.style.visibility = 'hidden'; + }else if (appModule === 'material'){ + this.element.style.visibility = 'visible'; + this.overviewSel.style.display = 'inline'; - <div> + if (param === undefined){ // Overview page + this.detailsSel.style.display = 'none'; + overviewSelLabel.style.fontWeight = 'bold'; + }else{ // Details page + this.detailsSel.style.display = 'inline'; + this.detailsDropDown.value = param; + } + } + } // setState - <div style="padding: 30px 100px 20px 100px; "> - <div class="info-fields-label" style="float: left; width: 54%; "> - <span info-sys-data="band-structure">Band structure</span> - </div> - <div class="info-fields-label" style="float: left;"> - <span info-sys-data="DOS">DOS</span> - </div> - <div style="clear: both;"></div> +} // class Breadcrumb - <div class="calc-bs-dos-plotter" > - </div> - <div> - <div class="band-gap-field" style="float: left; width: 56%; text-align: right"> - <b><span info-sys-data="band-gap">Band gap</span></b>: - <span class="band-gap-value" ></span> - </div> - <div style="clear: both;"></div> +/*************************** + App setup +***************************/ - </div> - </div> +let breadcrumb = new Breadcrumb(); - <div class="spin-legend" style="font-size: 0.9em; padding: 0 30px 10px; display: none"> - <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg> - Spin <span style='font-size: 1.1em'>⇧</span> - <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg> - Spin <span style='font-size: 1.1em'>⇩</span> - </div> +let searchMod; +let MaterialModule; +let materialModDOM; +let currentModule; // current module DOM being shown - </div> - <div class="footer lower-section"> +function showModuleDOM(module){ + if (currentModule) contentElement.removeChild(currentModule); + currentModule= module; + contentElement.appendChild(currentModule); +} - <div style="float: left"> - <div style="padding: 16px; "> - <div class="info-fields-label" > - <span info-sys-data="brillouin-zone-viewer">Brillouin zone</span> - </div> - <div class="bz-viewer-wrapper" style="width: 400px; height: 400px"> - </div> - </div> - </div> - <div class="band" style="float: right; width: 40%;"> - <div style="padding: 16px; "> - <div class="info-fields-label" > - <!-- <span info-sys-data="fermi-surface">Fermi surface </span> --> - </div> - <div class="fermi-box" > </div> - </div> - </div> +/****** App level events setup ********/ - <div style="clear: both;"></div> +PubSub.subscribe('show-material', data => { + console.log('Handling event show-material: '+data.id+' view: '+data.view); - </div> <!-- footer --> + //titleElement.innerHTML = 'NOMAD Encyclopedia - Material '+data.id; + breadcrumb.setState('material',data.view); - </div> + if (typeof materialModDOM === 'undefined'){ + MaterialModule = new MaterialMod(); + materialModDOM= MaterialModule.element; + } + MaterialModule.setMaterialView(data); + showModuleDOM(materialModDOM); - </div> <!-- view-box --> - `; + // In case the app comes from the search module through the url (back button) + UserGuidance.show(false); - this.navTreeWrapper = - this.element.getElementsByClassName("navTreeWrapper")[0]; + //console.log('User data:',util.getUserData()); + if (util.getUserData() !== null) flaggingTab.style.visibility = 'visible'; +}); - //this.leafTitle = this.element.querySelector('.tree-leaf-title'); - this.calcSelector = new CalcSelectorBar('calc-selector-bar','70%'); - this.element.querySelector('.calc-selector-host'). - appendChild(this.calcSelector.element); - this.rightBox = this.element.querySelector('.e-structure-box'); +PubSub.subscribe('show-search', search => { + console.log('Handling event show-search: '+search); - this.summaryByFunctionals = null; + titleElement.innerHTML = 'NOMAD Encyclopedia - Search'; + breadcrumb.setState('search',search); - this.bsDosPlotter = new BSDOSPlotter(); + if (search === undefined){ + searchMod.showSearchPage(); + LoadingPopup.hide(); // In case it comes from the result page - this.bandGapField = this.element.querySelector('.band-gap-field'); - this.bandGapValue = this.element.querySelector('.band-gap-value'); + }//else if (search === 'results') searchMod.showSearchResults(); - this.spinLegend = this.element.querySelector('.spin-legend'); + showModuleDOM(searchMod.element); - this.lowerSection = this.element.querySelector('.lower-section'); + if (flaggingTab.style.visibility !== 'hidden') + flaggingTab.style.visibility = 'hidden'; +}); - this.fermiBox= this.element.getElementsByClassName('fermi-box')[0]; - // Load the x3dom library - util.loadLib("lib/x3dom.js"); - this.bzViewerWrapper = this.element.querySelector('.bz-viewer-wrapper'); - this.bzViewerWrapper = new BZViewerWrapper(this.bzViewerWrapper); - // For static ones - InfoSys.addToInfoSystem(this.element); +/****** App routing config ******/ - this._events(); - } +Router.add('search', search => PubSub.publish('show-search', search)); +Router.add('material', (matId, view) => PubSub.publish('show-material', {'id': matId, 'view': view})); - _events() { - //super._events(); - this.calcSelector.setPrevListener(e => { - if (this.groupIndex > 0){ - let calcId = this.groupCalcs[--this.groupIndex]; - this._loadGraphData(calcId); - this._updateSelectorState(calcId); - return this.groupIndex === 0; // the first - } - }); - this.calcSelector.setNextListener( e => { - if (this.groupIndex < this.groupCalcs.length-1){ - let calcId = this.groupCalcs[++this.groupIndex]; - this._loadGraphData(calcId); - this._updateSelectorState(calcId); - return this.groupIndex === this.groupCalcs.length-1; // the last - } - }); - } +/****** init ******/ +searchMod = new SearchModule(); - updateSelection(leafIds/*Set*/){ - //console.log('E-StructureDetails UPDATING calcs: ',calcs.values().next().value); - if (leafIds.size > 0){ - this.rightBox.style.visibility = 'visible'; - //console.log('E-StructureDetails UPDATING calcs:leafIds.size > 0 ',leafIds); - let calcMapByFunctional = getCalcMapByFunctional(leafIds); - if (this.summaryByFunctionals === null) - this.summaryByFunctionals = - new SummaryByFunctionalsComponent(calcMapByFunctional, - this.element.querySelector('.summary-box')); - else - this.summaryByFunctionals.build(calcMapByFunctional); - // let bandGapDirect= calcs.values().next().value.band_gap_direct; - //let bandGapType= (bandGapDirect ? "direct" : "indirect"); - - let counter= 0; - leafIds.forEach( calcId => { - counter++; - if (counter === 1) this.firstId = calcId; - if (counter === leafIds.size) this.lastId = calcId; - }); - }else - this.rightBox.style.visibility = 'hidden'; - - function getCalcMapByFunctional(leafIds/*Set*/){ - let functCalcMap = new Map(); - leafIds.forEach( leafId => { - let calc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); - if (calc.has_band_structure){ - //console.log('CALC BS ',calc.band_gap,calc.band_gap_direct); - if (functCalcMap.has(calc.functional_type)){ - functCalcMap.get(calc.functional_type).add(calc); - }else{ // New functional - let newFunctionalArray = new Set(); - newFunctionalArray.add(calc); - functCalcMap.set(calc.functional_type, newFunctionalArray); - } - } - }); - return functCalcMap; - } +//console.log('document.location: '+document.location.hash); +if (document.location.hash === '') document.location += "#/search"; +Router.route(); - } +/********* User authentication ***********/ +let userNameElement = document.querySelector('#user-name'); +let logoutButton = document.querySelector('#logout-button'); - updateMarkedLeaf(leafId){ - //console.log('updateMarkedCalc '+calc); - this.groupCalcs = null; // if the leafId is not a group else this.groupCalcs is array - if (leafId === null){ - //this.leafTitle.innerHTML = 'NO SELECTION'; - this.calcSelector.setState('NO SELECTION', true, true); - return; +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'; + } +} - }else{ - if (DataStore.getGroups().has(leafId)){ - //this.leafTitle.innerHTML = leafId+' ('+DataStore.getGroups().get(leafId).calcs.size+')'; - this.groupCalcs = Array.from(DataStore.getGroups().get(leafId).calcs); - let calcId = DataStore.getCalcReprIntId(leafId); - this.groupIndex = this.groupCalcs.indexOf(calcId); - this._updateSelectorState(calcId); - }else - //this.leafTitle.innerHTML = leafId; - this.calcSelector.setState(leafId, true, true); - } - this._loadGraphData(DataStore.getCalcReprIntId(leafId)); +function setAppLoggedOut(){ + userNameElement.innerHTML = ''; + document.querySelector('#guest-user').style.display = 'inline'; + document.querySelector('#auth-user').style.display = 'none'; + util.setAuthRequestHeader(); - // calc.fermi_surface + if (flaggingTab.style.visibility !== 'hidden') + flaggingTab.style.visibility = 'hidden'; +} - } +function getCookie(name) { + let value = "; " + document.cookie; + let parts = value.split("; " + name + "="); + if (parts.length === 2) return parts.pop().split(";").shift(); +} - _updateSelectorState(leafId){ - let t = leafId+' ('+(this.groupIndex+1)+'/'+this.groupCalcs.length+')'; - this.calcSelector.setState(t, this.groupIndex === 0, - this.groupIndex === this.groupCalcs.length-1); - } +function parseCookie(userData) { + return userData.substring(1, userData.length-1).replace(/\\054/g,',').replace(/\\/g,''); +} - _loadGraphData(calcId){ - - let calc = DataStore.getCalc(calcId); - - if (!this.bsDosPlotter.isAttached()) - this.bsDosPlotter.attach(this.element.querySelector('.calc-bs-dos-plotter') - ,undefined,360); - - if (calc === null || (!calc.has_band_structure && !calc.has_dos)){ - this.bsDosPlotter.setNoData(); - this.bzViewerWrapper.setNoData(); - this.bandGapField.style.display = 'none'; - this.lowerSection.style.display = 'none'; - - }else{ - LoadingPopup.show(); - let matId = DataStore.getMaterialData().id; - - util.serverReq(util.getMaterialCalcURL(matId, calc.id, 'dos'), e1 => { - let dosData = JSON.parse(e1.target.response).dos; - - util.serverReq(util.getMaterialCalcURL(matId, calc.id, 'band_structure'), - e2 => { - let bsData= JSON.parse(e2.target.response).band_structure; - if (bothSpins(bsData, dosData)) - this.spinLegend.style.display = 'block'; - - //***util.addBandGapData(calcData, bsData); - //console.log('CODE NAME:', calc.code_name); - this.bsDosPlotter.setUpAndData(bsData, dosData, calc.code_name ); - - if (calc.has_band_structure){ - - this.bandGapField.style.display = 'block'; - this.bandGapValue.textContent= util.J2eV(calc.band_gap, 2)+' eV '; - // console.log('calc.brillouin_zone_json',calc.brillouin_zone_json); - if (calc.brillouin_zone_json !== null){ - this.lowerSection.style.display = 'block'; - this.bzViewerWrapper.setCalcData(calc.brillouin_zone_json, bsData.segments); - }else{ - this.lowerSection.style.display = 'none'; - this.bzViewerWrapper.setNoData(); - } - - //this.bzViewerWrapper.setCalcData(calc.brillouin_zone_json, bsData.segments); - // if there bandstruc data && calc.band_gap === 0 => show FermiSurface - //if (calc.band_gap === 0) - //setFermiVizContent(this.fermiBox, (calc === null ? '' : util.FERMI_SURFACE_URL)); - }else{ - this.lowerSection.style.display = 'none'; - this.bandGapField.style.display = 'none'; - this.bzViewerWrapper.setNoData(); - } - LoadingPopup.hide(); - }); - }); - - } - - function bothSpins(bsData, dosData){ - if (bsData !== undefined){ - if (bsData.segments[0].band_energies.length === 2) return true; - } - if (dosData !== undefined){ - if (dosData.dos_values.length === 2) return true; - } - return false; - } - } +let userInfoCookie = getCookie('user_info'); +//console.log('Cookies: ', document.cookie, userInfoCookie); - setPrevCalcListener(listener){ - this.prevCalcListener= listener; - } +if (userInfoCookie !== undefined){ + let userInfoData = JSON.parse(parseCookie(userInfoCookie)); + //console.log('userInfoData: ', userInfoData); + setAppAuthenticated(userInfoData); +} - setNextCalcListener(listener){ - this.nextCalcListener= listener; - } +// Logout +logoutButton.addEventListener( "click", e => { - } + document.cookie='user_info=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain='+ + util.USER_COOKIE_DOMAIN+'; path=/'; + //console.log('Logging out document.cookie ',document.cookie); + setAppLoggedOut(); + //console.log('Logging out ',userNameElement.innerHTML); +}); - class SummaryByFunctionalsComponent{ - - constructor(calcMapByFunctional, hostElement){ - this.calcMapByFunctional = calcMapByFunctional; - this.hostElement = hostElement; - this.viewType = 'text'; - this.functional = null; - this.hostElement.innerHTML+=` - <div style="float: left" > - <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab" - viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;"> - <rect x="0" y="0" width="2" height="15" /> - <rect x="3" y="5" width="1.8" height="7" /> - <rect x="6" y="3" width="1.8" height="9" /> - <rect x="9" y="6" width="1.8" height="6" /> - <rect x="12" y="2" width="1.8" height="10" /> - <rect x="2" y="13" width="13" height="2" /> - </svg> - <svg xmlns="http://www.w3.org/2000/svg" class="text-tab" - viewBox="0 0 15 15" width="15" height="15" style="fill: #777;"> - <rect x="0" y="1" width="15" height="2.5" /> - <rect x="0" y="6" width="15" height="2.5" /> - <rect x="0" y="11" width="15" height="2.5" /> - </svg> - </div> - - <div class="functional-tabs" style="float: right"> - </div> - - <div style="clear: both;"></div> - - <div class="content-placeholder" > - - <div style="display: block" class="text-panel" > - - <div><b><span info-sys-data="band gap">Band gap</span></b> (eV): - <div class="stats-fields summary-bandgap-field" > </div> - </div> - </div> - - <div style="display:none" class="chart-panel" > - <div class="charts-placeholder" > </div> - </div> - - </div> - `; - this.chartTab = this.hostElement.querySelector('.chart-tab'); - this.textTab = this.hostElement.querySelector('.text-tab'); - this.functionalTabs = this.hostElement.querySelector('.functional-tabs'); - this.chartPanel = this.hostElement.querySelector('.chart-panel'); - this.textPanel = this.hostElement.querySelector('.text-panel'); - 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.build(calcMapByFunctional); - - this.chartTab.addEventListener( "click", e => { - this.chartTab.style.fill = '#777'; - this.viewType = 'chart'; - this.textTab.style.fill = '#c7c7c7'; - this.chartPanel.style.display = 'block'; - this.textPanel.style.display = 'none'; - }); +/***/ }), +/* 17 */ +/***/ (function(module, exports) { - this.textTab.addEventListener( "click", e => { - this.textTab.style.fill = '#777'; - this.viewType = 'text'; - this.chartTab.style.fill = '#c7c7c7'; - this.textPanel.style.display = 'block'; - this.chartPanel.style.display = 'none'; - }); - this.functionalTabs.addEventListener( "click", e => { - if (e.target.className === 'tab'){ - this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') - .className = 'tab'; - this.functional = e.target.getAttribute('data-tab'); - this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') - .className = 'tab-selected'; - this._setData(); - } - }); +const Conf = {}; - } +Conf.GuestUserToken = + 'eyJhbGciOiJIUzI1NiIsImlhdCI6MTUyMzg4MDE1OSwiZXhwIjoxNjgxNTYwMTU5fQ.ey'+ + 'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4'; - _setData(){ - let stats = this.functionalBandGapMap.get(this.functional); - // Set text data - this.bandgapField.innerHTML = stats.html; +// EXPORTS +module.exports = Conf; - // Set Charts data - this.statsViewer.clear(); - this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); - } +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; - build(calcMapByFunctional){ - - if (calcMapByFunctional.size === 0){ - this.hostElement.style.display = 'none'; - this.hostElement.previousElementSibling.style.display = 'none'; - }else{ - this.hostElement.style.display = 'block'; - this.hostElement.previousElementSibling.style.display = 'block'; - } - - this.unfoldedElement = null; - this.functionalTabs.innerHTML = ''; - this.functionalBandGapMap = new Map(); - //console.log('calcMapByFunctional',calcMapByFunctional, this.hostElement); - calcMapByFunctional.forEach( (calcs, functionalName) =>{ - //let statsMap = util.getQuantityStatsMap(calcs); - let array= []; - calcs.forEach( calc => { - array.push(calc.band_gap/1.602176565e-19); - }); - 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); - stats.label = 'band gap'; - stats.html = util.getAverage(stats.data).toFixed(2)+ - ' <span style="font-size: 0.9em">['+stats.min.toFixed(2) - +' , '+stats.max.toFixed(2)+']</span>'; - - this.functionalBandGapMap.set(functionalName, stats); - - let tabClass= 'tab'; - if (this.functional === null) this.functional = functionalName; - if (this.functional === functionalName){ - this._setData(); - tabClass= 'tab-selected'; - } - - this.functionalTabs.innerHTML += - '<span class="'+tabClass+'" data-tab="'+functionalName+'">'+functionalName+'</span>'; - }); +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This component is the popup window (and the darkened background) with a form + inside used for users to report (flagging) errors on the calculations + */ + + + +let util = __webpack_require__(0); +let DataStore = __webpack_require__(1); + +// main DOM elements +let flaggingPopupBackground = document.querySelector('#flagging-form-popup-bg'); +let flaggingFormPopup = document.querySelector('#flagging-form-popup'); +flaggingFormPopup.innerHTML =` + +<div> <img src="img/cross.svg" height="12px" + style="float: right; cursor: pointer" /> +</div> + +<div class="form-wrapper"> + <div class="popup-title"> Error reporting</div> + <select id="flagging-category" name="category"> + <option value="">Select a category *</option> + <option value="structure">Structure</option> + <option value="electronicstruct">Electronic structure</option> + <option value="methodology">Methodology</option> + <option value="thermalprops">Thermal properties</option> + </select> + + <select id="flagging-subcategory" name="subcategory"> + + </select> - InfoSys.addToInfoSystem(this.hostElement); - } + <textarea id="subject" name="subject" style="height:200px" + placeholder="Write a short explanation about the error detected" ></textarea> + + <div id="form-validation-msg"> </div> + + <div style="display: flex; justify-content: space-evenly;"> + <button style="display: block">Send</button> + </div> + + +</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 validationMsg = flaggingFormPopup.querySelector('#form-validation-msg'); +let sendButton= flaggingFormPopup.querySelector('button'); + +let treeLeaf = null; +let overviewEStructCalcs = null; - } +function _setCurrentPage(pageId){ + subcategoryField.innerHTML = ''; - class BZViewerWrapper{ + if (pageId === null){ + categoryField.disabled = false; + subcategoryField.style.display = 'none'; - constructor(hostElement) { - this.hostElement = hostElement; - this.bzViewer = null; - } + }else{ + categoryField.disabled = true; + subcategoryField.style.display = 'block'; + subcategoryField.appendChild(createOption('Choose the subcategory *', '')); + switch (pageId) { - setCalcData(bzData, bsData){ - if (this.bzViewer === null) - this.bzViewer = new BrillouinZoneViewer(this.hostElement); - this.bzViewer.load(this._getBZDataForViewer(bzData,bsData)); - this.hostElement.style.visibility = 'visible'; - } + case util.MAT_VIEW.structure: + categoryField.selectedIndex = 1; + subcategoryField.appendChild(createOption('Structure representation')); + subcategoryField.appendChild(createOption('Calculation tree')); + subcategoryField.appendChild(createOption('Summary')); + subcategoryField.appendChild(createOption('Specific calculation')); + break; + case util.MAT_VIEW.electronicstruct: + categoryField.selectedIndex = 2; + subcategoryField.appendChild(createOption('Calculation tree')); + subcategoryField.appendChild(createOption('Summary')); + subcategoryField.appendChild(createOption('Band structure')); + subcategoryField.appendChild(createOption('DOS')); + subcategoryField.appendChild(createOption('Brillouin zone')); + break; - setNoData(){ - this.hostElement.style.visibility = 'hidden'; - } + case util.MAT_VIEW.methodology: + categoryField.selectedIndex = 3; + subcategoryField.style.display = 'none'; + break; + case util.MAT_VIEW.thermalprops: + categoryField.selectedIndex = 4; + subcategoryField.appendChild(createOption('Calculation tree')); + subcategoryField.appendChild(createOption('Phonon dispersion')); + subcategoryField.appendChild(createOption('Phonon DOS')); + subcategoryField.appendChild(createOption('Specific heat')); + subcategoryField.appendChild(createOption('Helmholtz free energy')); + break; + } + } - _getBZDataForViewer(bz_json, bs_json){ - let labels = []; - let kPoints = []; - bs_json.forEach( segment => { - labels.push(segment.band_segm_labels); - kPoints.push(segment.band_k_points); - }); +} // function _setCurrentPage - let data = { - vertices: bz_json.vertices, - faces: bz_json.faces, - basis: bz_json.basis, - labels: labels, - segments: kPoints - } - return data; - } - } +function show(pageStatus){ + //console.log('pageStatus : ',pageStatus); + treeLeaf = pageStatus.markedLeaf; + overviewEStructCalcs = pageStatus.eStructCalcs; + // Show/hide some dropdown list options + eStructOption.style.display = (DataStore.hasElecStructureData ? 'block' : 'none'); + thermalOption.style.display = (DataStore.hasThermalData ? 'block' : 'none'); - // EXPORTS - module.exports = ElectronicStructDetails; + _setCurrentPage(pageStatus.pageId); + let ttRect = flaggingFormPopup.getBoundingClientRect(); + let leftPos = (window.innerWidth - ttRect.width)/2; + let topPos = (window.innerHeight -ttRect.height)/2; + flaggingFormPopup.style.left = leftPos+'px'; + flaggingFormPopup.style.top = (topPos-20)+'px'; -/***/ }, -/* 25 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Implements a graphical UI component composed of a DOS plotter and a - Band Structure plotter - */ - - - let BSPlotter = __webpack_require__(14); - let DOSPlotter = __webpack_require__(16); - let svg = __webpack_require__(9); - - - class BSDOSPlotter{ - - constructor() { - this.element = document.createElement('div'); - 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.dosYAxisLabeled = false; - } + flaggingFormPopup.style.visibility = 'visible'; + flaggingPopupBackground.style.visibility = 'visible'; +} - attach(element, width, height){ - element.appendChild(this.element); - //this.bsPlotter.attach(this.element, element.clientWidth/2 + 200 -20/*padding*/, height); - //this.dosPlotter.attach(this.element, element.clientWidth/2 - 200 -20/*padding*/, height); - this.bsPlotter.attach(this.element, height, height); - this.height = height; - this.dosPlotter.attach(this.element, this.height/2+20, height); - this.parentElement= element; - } +function hide(){ + flaggingPopupBackground.style.visibility = 'hidden'; + flaggingFormPopup.style.visibility = 'hidden'; + // reset UI + categoryField.selectedIndex = 0; + subcategoryField.selectedIndex = 0; + flaggingFormPopup.querySelector('textarea').value = ''; + validationMsg.innerHTML = ''; +} - isAttached(){ - return this.parentElement !== null; - } +function createOption(text, value){ + let opt = document.createElement('option'); + opt.value = (value === undefined ? text : value); + opt.innerHTML = text; + return opt; +} - setUpAndData(dispData, dosData, codeName){ +closeButton.addEventListener('click', e => { + hide(); +}); - this.hasDispData = (dispData !== undefined && dispData !== null); - this.hasDosData = (dosData !== undefined && dosData !== null); - // Create a new DOS graph with/without left axis and labels - let newDosYAxisLabeled; - if (this.hasDosData && !this.hasDispData) newDosYAxisLabeled = true; - else newDosYAxisLabeled = false; +sendButton.addEventListener('click', e => { - 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 width = this.height/2 + newLeftMargin; - this.dosPlotter.attach(this.element, width, this.height); - } - this.dosYAxisLabeled = newDosYAxisLabeled; + let categoryChosen = categoryField.options[categoryField.selectedIndex]; - if (this.hasDispData){ - this.bsPlotter.setBandStructureData(dispData); - if (this.hasDosData) - this.bsPlotter.setRepaintListener( (yZoom, yOffset) => { - this.dosPlotter.setYZoomAndOffset(yZoom, yOffset); - this.dosPlotter.repaint(); - }); - }else - this.bsPlotter.setNoData(); + if (!categoryField.disabled && categoryChosen.value === ''){ // Overview case + validationMsg.innerHTML = 'The category fields must be set'; - if (this.hasDosData){ + }else if (categoryField.disabled && subcategoryField.value === '' // Detaisl pages case + && categoryChosen.value !== util.MAT_VIEW.methodology){ + validationMsg.innerHTML = 'The subcategory fields must be set'; - this.dosPlotter.setPoints(dosData, codeName); + }else{ + validationMsg.innerHTML = 'Sending report...'; + let textareaText = flaggingFormPopup.querySelector('textarea').value; + let materialId = DataStore.getMaterialData().id; + let userdata = util.getUserData(); - if (this.hasDispData){ - this.bsPlotter.setExternalYAxisMax(this.dosPlotter.getYAxisMax()); - this.dosPlotter.setRepaintListener( (yZoom, yOffset) => { - this.bsPlotter.setYZoomAndOffset(yZoom, yOffset); - this.bsPlotter.repaint(); - }); - // Remove y axis label - this.dosPlotter.svg.removeChild(this.dosPlotter.yLabelText); - this.dosPlotter.yLabelText = null; - } + let titleText = 'User issue | Material '+materialId; + let descriptionText = '**Server:** '+util.getServerLocation()+ + '\\n\\n**User:** '+userdata.username+', '+userdata.email; - }else - this.dosPlotter.setNoData(); + // Overview page + if ( !categoryField.disabled){ + descriptionText += '\\n\\n**Category:** Overview / '+categoryChosen.text; - this.dosPlotter.setYAxisLabelsVisibility(newDosYAxisLabeled); - } + 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; - setNoData(){ - this.bsPlotter.setNoData(); - this.dosPlotter.setNoData(); - } + 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; + } + } - } + descriptionText += '\\n\\n**User text:** '+textareaText; + let queryJson =`{ + "title": "${titleText}", + "description": "${descriptionText}"}`; + console.log('Flagging POST request Json: ',queryJson);//, util.getFlaggingURL()); - // EXPORTS - module.exports = BSDOSPlotter; + util.serverReqPOST(util.getFlaggingURL(), queryJson, e => { + console.log('response',e); + if (e.target.status === 200) hide(); + }); -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - 'Details' view container that shows a table with the methodology info used - to get the material calculations. - - In the file there is a defined (class) component used as a filter widget for - the table: FilterInGroupsComponent. - */ - - "use strict"; - - let DetailsViewBase = __webpack_require__(20); - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let DataStore = __webpack_require__(2); - let SortingButton = __webpack_require__(27); - - - const REPOSITORY_LINK = - 'https://repository.nomad-coe.eu/NomadRepository-1.1/views/calculation.zul?pid='; - - const calcTypesMap = new Map([ - ['single point', 'Single point'], - ['GW calculation', 'GW'], - ['geometry optimization', 'Geometry optimization'], - ['molecular dynamics', 'Molecular dynamics'], - ['phonon calculation', 'Phonon'], - ['equation of state', 'Equation of state'], - ['parameter variation', 'Parameter variation'],//['Convergence', 'convergence'], - ['QHA calculation', 'QHA'] - ]); - - const densityFunctionalMap = new Map([ - ['LDA', 'LDA'], - ['GGA', 'GGA'], - ['meta-GGA', 'meta-GGA'], - ['hybrid-GGA', 'hybrid-GGA'], - ['meta-hybrid-GGA', 'meta-hybrid-GGA'], - ['HF', 'HF'] - ]); - - const codeMap = new Map([ - ['exciting', 'exciting'], - ['VASP', 'VASP'], - ['FHI-aims', 'FHI-aims'] - ]); - - const potentialMap = new Map([ - ['full all electron', 'Full potential' ], - ['pseudopotential', 'Pseudo potential' ] - ]); - - const basicSetMap = new Map([ - ['numeric AOs', 'Numeric AOs' ], - ['gaussians', 'Gaussians' ], - ['(L)APW+lo', '(L)APW+lo'], - ['plane waves', 'Plane waves'] - ]); - - - - class FilterInGroupsComponent { - - constructor(className) { - this.element = document.createElement('div'); - this.element.className = className; - this.filtersOn = []; - this.folded = true; - this.element.innerHTML+=` - <div> - <div style="display: flex; justify-content: flex-end;"> - <div class="filter-groups-c-folded" > - <span style="vertical-align: top;">Filtering </span> - </div> - <div class="filter-c-btn" > - <img src="${util.IMAGE_DIR}folded.png" /> - <!--<button class="on">filter</button> --> - </div> - </div> - <div class="filter-groups-c-unfolded" style="display: none"> - <table style="width: 100%"> - <thead> - <tr> - <th style="width: 13%;"> </th> - <th style="width: 17%;"> - <span>Type</span> - </th> - <th style="width: 19%;"> - <span info-sys-data="functional-type">Density functional</span> - </th> - <th style="width: 13%;"> - <span info-sys-data="code-name">Code</span> - </th> - <th style="width: 17%;"> - <span info-sys-data="pseudopotential-type">Potential</span> - </th> - <th style="width: 12%;"> - <span info-sys-data="basis-set-type">Basis set</span> - </th> - <th style="width: 9%;"> </th> - </tr> - </thead> - <tbody> - <tr id="filter-items-row"></tr> - </tbody> - </table> - </div> - </div> - `; - //this.foldedPanel = this.element.querySelector('.filter-groups-c-folded'); - this.unfoldedPanel = this.element.querySelector('.filter-groups-c-unfolded'); - this.filterItemsRow = this.element.querySelector('#filter-items-row'); - - this.foldBtn = this.element.querySelector('img'); - - this.foldBtn.addEventListener('click', e => { - this.folded = !this.folded; - this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' : - util.IMAGE_DIR+'unfolded.png'); - //this.foldedPanel.style.display = (this.folded ? 'block' : 'none'); - this.unfoldedPanel.style.display = (this.folded ? 'none' : 'block'); - }); + } - // Add listener for checkboxes events - this.element.addEventListener('click', (e) => { +}); - if (e.target.tagName === 'INPUT'){ - let index = this.filtersOn.indexOf(e.target.value); - if (index >= 0) this.filtersOn.splice( index, 1 ); - else this.filtersOn.push(e.target.value); - this.itemListener(this.filtersOn); - //console.log('this.filtersOn',this.filtersOn); - } - }); - } - addGroupsItems(calcs){ - let lCalcTypesMap = new Map(); - let lDensityFunctionalMap = new Map(); - let lCodeMap = new Map(); - let lPotentialMap = new Map(); - let lBasicSetMap = new Map(); - 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, densityFunctionalMap.get(c.functional)); - if (!lCodeMap.has(c.code)) - lCodeMap.set(c.code, codeMap.get(c.code)); - if (!lPotentialMap.has(c.potential)) - lPotentialMap.set(c.potential, potentialMap.get(c.potential)); - if (!lBasicSetMap.has(c.basisSet)) - lBasicSetMap.set(c.basisSet, basicSetMap.get(c.basisSet)); - }); - this.filterItemsRow.innerHTML = '<td></td>'; // calculation Id column - this.filtersOn = []; - this.addGroupItems(lCalcTypesMap); - this.addGroupItems(lDensityFunctionalMap); - this.addGroupItems(lCodeMap); - this.addGroupItems(lPotentialMap); - this.addGroupItems(lBasicSetMap); - this.filterItemsRow.innerHTML += '<td></td>'; // link column - } +// EXPORTS +module.exports = { show, hide }; - addGroupItems(groupItemsMap){ - let html = '<td> '; - groupItemsMap.forEach( (itemName, itemId) => { - this.filtersOn.push(itemId); - html += '<input type="checkbox" value="'+itemId+'" checked>'+ - '<span style="vertical-align: 20%">'+itemName+'</span> <br> '; - }); - this.filterItemsRow.innerHTML += html+ '</td>'; - } +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This file implements the design pattern pub/sub, which allows one module + to broadcast messages to other modules. + */ + + + +var messages = new Map(); +var lastUid = -1; + + /** + * subscribe( message, func ) -> String + * - message (String): The message to subscribe to + * - func (Function): The function to call when a new message is published + * Subscribes the passed function to the passed message. Every returned token + * is unique and should be stored if you need to unsubscribe + **/ +function subscribe( message, func ){ + //console.log('SUBSCRIBING Message '+message); + if ( typeof func !== 'function'){ + return false; + } + // message is not registered yet + if ( !messages.has( message ) ){ + messages.set( message, new Map()); + } + // forcing token as String, to allow for future expansions without breaking usage + // and allow for easy use as key names for the 'messages' object + var token = 'uid_' + String(++lastUid); + messages.get(message).set(token, func); //messages[message][token] = func; + //print(); + // return token for unsubscribing + return token; +} + + +function publish( message, data){ + + var hasSubscribers = messages.has( message ) + && (messages.get( message ).size > 0); + + if ( !hasSubscribers ){ + return false; + } + + var deliver = function (){ + //deliverMessage(message, data); + var subscribers = messages.get(message); + //console.log('DELIVERING Message '+message); + subscribers.forEach(function(func, token) { + func(data); + //console.log('EXE funct '+ func); + }); + }; + + setTimeout( deliver, 0 ); // async + return true; +} + + +function print(){ + console.log('PubSub data: '); + messages.forEach(function(functions, msg) { + console.log(msg + ': '); + + functions.forEach(function(func, token) { + console.log(' '+token + ': ' + func); + }); + }); +} + +// EXPORTS +module.exports = { subscribe: subscribe, publish: publish }; + + +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; - setItemListener(listener){ - this.itemListener = listener; - } - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* + This file implements the app Routing system: the feature that allows + the page navigation in a single-page environment. + */ - class MethodologyDetails extends DetailsViewBase { - - constructor() { - super('Methodology'); - - this.sortedCalcs = []; - this.markedCalc = null; - - this.element.innerHTML+=` - - <div> - <div class="view-box"> - <div class="title">Methodology</div> - - <div class="filter-placeholder"></div> - - <div class="dataTableWrapper"></div> - </div> - </div> - `; - - // There is no this.navTreeWrapper = this.element.querySelector('.navTreeWrapper'); - - this.dataTableWrapper = - this.element.querySelector('.dataTableWrapper'); - - this.dataTableWrapper.innerHTML+=` - <table id="methodology-data"> - <thead> - <tr> - <th style="width: 13%;"> - <span>Calculation ID</span> - <span class="sorting-button"></span> - </th> - <th style="width: 17%;"> - <span>Type</span> - <span class="sorting-button"></span> - </th> - <th style="width: 19%;"> - <span info-sys-data="functional-type">Density functional</span> - <span class="sorting-button"></span> - </th> - <th style="width: 13%;"> - <span info-sys-data="code-name">Code</span> - <span class="sorting-button"></span> - </th> - <th style="width: 17%;"> - <span info-sys-data="pseudopotential-type">Potential</span> - <span class="sorting-button"></span> - </th> - <th style="width: 12%;"> - <span info-sys-data="basis-set-type">Basis set</span> - <span class="sorting-button"></span> - </th> - <th style="width: 9%;"> - <span info-sys-data="basis-set-type">Link</span> - - </th> - </tr> - </thead> - <tbody> - </tbody> - </table> - `; - - this.tbody = this.dataTableWrapper.querySelector("tbody"); - this.moreInfoRow = document.createElement('tr'); // - this.moreInfoRow.className= 'moreinfo'; - this.moreInfoCalcId = null; - - // filtering feature initialitation - this.filterComponent = new FilterInGroupsComponent('meth-filter-component'); - this.element.querySelector('.filter-placeholder'). - appendChild(this.filterComponent.element); - - this.filterComponent.setItemListener( propsSel/*ected*/ => { - - let rows = this.tbody.querySelectorAll('tr'); - for (let i = 0; i < rows.length; ++i){ - let calcProps = rows[i].getAttribute('data-calc-props').split(','); - //console.log('FILTERING calcProps: ',calcProps); - let propsPresent = true; - calcProps.forEach( e => { - if (propsSel.indexOf(e) < 0) propsPresent = false; - }); - - if (propsPresent) rows[i].style.display = 'table-row'; - else rows[i].style.display = 'none'; - } - }); - // row sorting feature initialitation - this.sortingButtonWrappers = - this.dataTableWrapper.querySelectorAll('.sorting-button'); - - this.sortingButtons = []; - - let sortingButtonsMap = new Map([ - [ 'id', undefined ], - [ 'type', undefined ], - [ 'functional', undefined ], - [ 'code', undefined ], - [ 'potential', undefined ], - [ 'basisSet', undefined ] ]); - - let keysIter = sortingButtonsMap.keys(); - this.sortingButtonWrappers.forEach( e => { - let field = keysIter.next().value; - let component = new SortingButton(field); - e.appendChild(component.element); - this.sortingButtons.push(component); - - component.setListener( (descendingOrder, field) => { - //console.log(descendingOrder, field); - this.sortingButtons.forEach( el => { - if (el !== component) el.init(); - }); - this._sortRowsCalcDataBy(descendingOrder, field); - this._render(); - }); - }); - // For static ones - InfoSys.addToInfoSystem(this.element); - this._events(); - } +let routes = new Map(); - _events() { - //super._events(); +function add(route, func){ + routes.set(route, func); +} - this.dataTableWrapper.addEventListener('click', (e) => { - let rowElement = e.target.parentElement; - if (rowElement.className.indexOf('data-row') < 0) - rowElement = rowElement.parentElement; +window.addEventListener("hashchange", route); - if (rowElement.className.indexOf('data-row') >= 0){ - let id= rowElement.getAttribute('data-calc-id'); +function route(){ + let hashPath= document.location.hash.substring(2); + let command, param, subparam; - if (this.moreInfoCalcId !== null){ // If more-info panel unfolded - this.moreInfoRow.parentElement.removeChild(this.moreInfoRow); - if (this.moreInfoCalcId === id){ - this.moreInfoCalcId = null; - return; - } - } - this.moreInfoCalcId = id; - let moreInfoCalc, calcGroupType; - this.sortedCalcs.forEach( leafId => { //console.log(leafId, id); - if (leafId === id){ - moreInfoCalc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); - calcGroupType = DataStore.getGroupType(leafId); + // remove the ending / + if (hashPath.lastIndexOf('/') === (hashPath.length-1)) + hashPath = hashPath.substring(0,hashPath.length-1); - } - }); - this.moreInfoRow.innerHTML= ' <td></td><td colspan="7"> '+ - getHTMLCalcType(calcGroupType, moreInfoCalc)+' </td>'; - rowElement.parentElement.insertBefore(this.moreInfoRow, rowElement.nextElementSibling); - } + 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); + if (routes.has(command)) { + routes.get(command)(param, subparam); + } +}; - function getHTMLCalcType(calcGroupType, calc){ - let result = ''; - let calcType = calcGroupType; - if (calcGroupType === null ) calcType = calc.run_type; - //console.log('getHTMLCalcType',calcType); - switch (calcType){ +function print(){ + console.log('Router data: '); + routes.forEach(function(func, url) { + console.log(url + ': ' + func); + }); +} - case calcTypesMap.get('Single point'): - case calcTypesMap.get('Geometry optimization'): - //console.log(calc.pseudopotential_type, calc.scf_threshold, calc.basis_set_short_name); - result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + - getValueHTML('scf threshold',calc.scf_threshold) + - getValueHTML('basis set short name',calc.basis_set_short_name) + - getSmearingHTML(calc.smearing); - break; +// EXPORTS +module.exports = { add: add, route: route }; - case calcTypesMap.get('GW'): - result = getValueHTML('gw starting point',calc.gw_starting_point) + - getValueHTML('gw type',calc.gw_type); - break; - case calcTypesMap.get('Equation of state'): - result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + - getValueHTML('basis set short name',calc.basis_set_short_name) + - getValueHTML('scf threshold',calc.scf_threshold) + - getSmearingHTML(calc.smearing) + - getValueHTML('k point grid description',calc.k_point_grid_description); - break; +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + The NavTree class define the UI tree component showing the material calculations. + There is only one instance that is shared by several 'Details' views. + */ + + + +let util = __webpack_require__(0); +let DataStore = __webpack_require__(1); + + + +function buildCalcTree(calcs, calcsInGroups){ + + let calcTree = new Map(); + calcsInGroups.forEach( (groupData, groupId) => { + + let representative = DataStore.getCalc(DataStore.getCalcReprIntId(groupId)); + let codeNameTrimed= representative.code_name.trim(); + let functionalType = representative.functional_type; + + /***** Exception: disable grouping for some codes + if (codeNameTrimed !== 'VASP' && codeNameTrimed !== 'FHI-aims') */ + if (calcTree.has(functionalType)){ + let functionalMap= calcTree.get(functionalType); + + if (functionalMap.has(codeNameTrimed)){ + // Get an array and push a new calc + functionalMap.get(codeNameTrimed).push(groupId); + }else // New code name + functionalMap.set(codeNameTrimed,[groupId]); + + }else{ // New functional + let newFunctionalMap= new Map(); + newFunctionalMap.set(codeNameTrimed,[groupId]); + calcTree.set(functionalType,newFunctionalMap); + } + }); + + // Simple calcs are added to the tree + calcs.forEach( calc => { + //if (!DataStore.isInAnyGroup(calc.id)) addToCalcTree(calc, calc.id+''); + //****** Exception: disable grouping for some codes + if (!DataStore.isInAnyNotDisabledGroup(calc.id)) + addToCalcTree(calc, calc.id+''); + }); + + + return calcTree; + + function addToCalcTree(calc, id){ + let codeNameTrimed= calc.code_name.trim(); + let functionalType = calc.functional_type; + if (calcTree.has(functionalType)){ + let functionalMap= calcTree.get(functionalType); + + if (functionalMap.has(codeNameTrimed)){ + // Get an array and push a new calc + functionalMap.get(codeNameTrimed).push(id); + }else // New code name + functionalMap.set(codeNameTrimed,[id]); + + }else{ // New functional + let newFunctionalMap= new Map(); + newFunctionalMap.set(codeNameTrimed,[id]); + calcTree.set(functionalType,newFunctionalMap); + } + } +} + + +/* Maybe these functions (getNextNode, getPreviousNode) can be removed +searching by node class calc-l and node-selected. Think about this */ + +function getNextNode(nodeBox){ + let nextCalcNodeBox= nodeBox.nextElementSibling.nextElementSibling; + // console.log("nextCALC.getNextNode " +nextCalcNodeBox); + if (nextCalcNodeBox === null){ + + if (nodeBox.parentElement.nextElementSibling !== null) + nextCalcNodeBox= nodeBox.parentElement.nextElementSibling/*nextCodeNodeBox*/ + .nextElementSibling/*nextCodeBox*/.children[0]/*nextCalc*/; + + else if (nodeBox.parentElement.parentElement.nextElementSibling !== null) + nextCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/ + .nextElementSibling.nextElementSibling/*nextFunctionalBox*/ + .children[1]/*nextCodeBox*/.children[0]/*nextCalc*/; + else //reaching the final node + nextCalcNodeBox= null; + } + return nextCalcNodeBox; +} + + +function getPreviousNode(nodeBox){ + let prevCalcNodeBox; + if (nodeBox.previousElementSibling !== null) + prevCalcNodeBox= nodeBox.previousElementSibling.previousElementSibling; + // console.log("nextCALC.getNextNode " +nextCalcNodeBox); + else{ + + if (nodeBox.parentElement.previousElementSibling.previousElementSibling !== null) + prevCalcNodeBox= nodeBox.parentElement.previousElementSibling/*prevCodeNodeBox*/ + .previousElementSibling/*prevCodeBox*/.children[0]/*prevCalc*/; + else if (nodeBox.parentElement.parentElement.previousElementSibling.previousElementSibling !== null) + prevCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/ + .previousElementSibling.previousElementSibling/*prevFunctionalBox*/ + .lastElementChild/*prevCodeBox*/.lastElementChild.previousElementSibling/*prevCalc*/; + else //reaching the final node + prevCalcNodeBox= null; + } + return prevCalcNodeBox; +} + + + +class NavTree { + + constructor() { + this.selectedCalcs= new Set(); + + this.element = document.createElement('div'); + this.element.setAttribute('id','navigation-tree'); + this.parentElement= null; + this.markedNode = null; + this._events(); + } + + // detach if necessary and attach + attach(element){ + if (this.parentElement !== null) + this.parentElement.removeChild(this.element); + this.parentElement= element; + this.parentElement.appendChild(this.element); + } + + + build(materialName){ + // Reset + this.selectedCalcs.clear(); + this.markedNode = null; + this.element.innerHTML= ''; + this.calcsInGroups = DataStore.getGroups(); + + + function getNodeHTML(level, label, unfolded, counter = 0){ + let foldingValue= 'node-'+(unfolded ? 'unfolded' : 'folded'); + let counterTag= (counter === 0 ? '' : + '<span class="node-counter">('+counter+')</span>'); + return ` + <div class="${level}"> + <span class="${foldingValue}"></span> + <span class="node-checkbox"></span> + <span class="node-label" >${label}</span> + ${counterTag} + </div> + `; + } + + function getCalcGraphInfoAvalabilityHTML(calc){ + let html= ''; + if (calc.has_band_structure) html += '<span class="tooltip">B<span class="tooltiptext">Band structure</span></span> '; + if (calc.has_dos) html += '<span class="tooltip">D<span class="tooltiptext">Density of states</span></span> '; + if (calc.has_fermi_surface) html += '<span class="tooltip">F<span class="tooltiptext">Fermi surface</span></span>'; + if (calc.has_thermal_properties) html += '<span class="tooltip">T<span class="tooltiptext">Phonons</span></span>'; + + return ' <span class="calc-graph-aval">'+html+'</span>'; + } + + // Init map to store calculations data + let calcs = DataStore.getCalculations(); + + let tree= buildCalcTree(calcs, this.calcsInGroups); + let rootElement= document.createElement('div'); + this.element.appendChild(rootElement); + rootElement.innerHTML= getNodeHTML('material-l',materialName,true); + + let functionalLevelBox= document.createElement('div'); + rootElement.appendChild(functionalLevelBox); + + tree.forEach((codeMap, functionalName) => { + //console.log(codeMap + " " + value); + functionalLevelBox.innerHTML+= getNodeHTML('functional-l',functionalName,true); + + let codeLevelBox= document.createElement('div'); + functionalLevelBox.appendChild(codeLevelBox); + + codeMap.forEach( (calcArray, codeName) => { + codeLevelBox.innerHTML+= getNodeHTML('code-l', codeName, false, calcArray.length); + + let calcLevelBox= document.createElement('div'); + codeLevelBox.appendChild(calcLevelBox); + calcLevelBox.style.display= 'none'; + + for (var i = 0; i < calcArray.length; i++) { + let graphInfoAvalabilityHTML = getCalcGraphInfoAvalabilityHTML( + DataStore.getCalc(DataStore.getCalcReprIntId(calcArray[i]))); + + let calcIcon = '', calcNumber = ''; + if (DataStore.isGroup(calcArray[i])){ + calcIcon = '<img class="folder-icon" src="'+util.IMAGE_DIR+'folder.png" />' + calcNumber = '('+this.calcsInGroups.get(calcArray[i]).calcs.size+')'; + } + + + calcLevelBox.innerHTML += ` + <div class="calc-l" data-calc-id="${calcArray[i]}" > + <span></span> + <span class="node-checkbox"></span> + + <span class="node-label" > + ${calcIcon} ${calcArray[i]} ${calcNumber} ${graphInfoAvalabilityHTML} + </span> + + <div style="float: right; padding: 1px 10px 0 0; display: none;"> + <img src="${util.IMAGE_DIR}next.png" /> + </div> + + </div> + <div> </div>`; + } + }); + }); + } // build method + + + selectAll(initMarkedLeafId){ + let materialNodeBox= this.element.children[0].children[0]; + this._recursiveNodeSelection(materialNodeBox, true); + keepTreeIntegrity(materialNodeBox, true); + // No calc marked + } - case calcTypesMap.get('Parameter variation'): - result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + - getValueHTML('scf threshold',calc.scf_threshold); - break; - case calcTypesMap.get('Phonon'): - result = getValueHTML('type', 'finite differences'); - break; + getMarkedLeaf(){ + if (this.markedNode === null) return null; + else return this.markedNode.getAttribute('data-calc-id'); + } - } - if (result.trim() === '') return 'NO ADDITIONAL DATA'; - else return result; - } - } + setMarkedLeafIfNoneMarked(leafId){ // If leafId === null first node selected + if (this.getMarkedLeaf() === null){ // If none marked + if (leafId === null) this._markFirstSelectedNode(); + else{ + let nodeBox = this.element.querySelector('div[data-calc-id="'+leafId+'"]'); + this._setMarkedCalc(nodeBox); + } + } + } - updateSelection( leafIds/* Not used */ ){ - this.sortedCalcs = []; + _events() { + this.element.addEventListener('click',(e) => { + let classString = e.target.className; - DataStore.getCalculations().forEach( c => { + // drop down/up event + if (classString.indexOf('folded') >= 0){ + this._foldTreeNode(e.target); - let calcType = c.run_type; - if (DataStore.getGroups().has(c.id)){//leafId)) - calcType = DataStore.getGroupType(c.id);//leafId); - if (calcType === 'convergence') calcType = 'parameter variation'; - //console.log('Group',DataStore.getGroupType(leafId)); - } + // descendant selection/deselection event + }else if ((classString.indexOf('node-checkbox') >= 0)){ - this.sortedCalcs.push({ - id: c.id, - type: calcType, - functional: c.functional_type, - code: c.code_name, - potential: c.core_electron_treatment, - basisSet: c.basis_set_type - }); - }); + let selectMode= (e.target.parentElement.className.indexOf('selected') < 0); + this._recursiveNodeSelection(e.target.parentElement, selectMode); + keepTreeIntegrity(e.target.parentElement, selectMode); + this.treeSelectionListener(this.selectedCalcs); + this._keepCalcMarked(selectMode); //if (this.calcMarked) - this.filterComponent.addGroupsItems(this.sortedCalcs); + }else if (/*this.calcMarked && */(classString.indexOf('node-label') >= 0 ) + && (e.target.parentElement.className === 'calc-l node-selected')){ + this._setMarkedCalc(e.target.parentElement/*nodeBox*/); + } + }); - this._sortRowsCalcDataBy(true, 'id'); - //console.log('sortedCalcs:', this.sortedCalcs); + } - this._render(); - } + _foldTreeNode(dropDowmElement){ + let siblingElement= dropDowmElement.parentElement.nextElementSibling; + let classString = dropDowmElement.className; - _sortRowsCalcDataBy(descendingOrder, field){ + if (classString.indexOf('-folded') >= 0) { + dropDowmElement.className= dropDowmElement.className.replace('folded','unfolded'); + siblingElement.style.display= 'block'; + }else { + dropDowmElement.className= dropDowmElement.className.replace('unfolded','folded'); + siblingElement.style.display= 'none'; + } + } + + + _recursiveNodeSelection(nodeBox, select){ - 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 nodeCheckBox= nodeBox.children[1]; + if (select){ + nodeBox.className += ' node-selected'; + }else{ // deselect + let index= nodeBox.className.indexOf(' node-selected'); + nodeBox.className= nodeBox.className.substring(0,index); + } + if (nodeBox.className.indexOf('calc-l') >= 0) { // leaf node + let id= nodeBox.getAttribute('data-calc-id');//let id= parseInt(nodeBox.getAttribute('data-calc-id')); + if (select) this.selectedCalcs.add(id); + else this.selectedCalcs.delete(id); - updateMarkedLeaf(leafId){ } + }else { // Not leaf node + let nextLevelBox = nodeBox.nextElementSibling; // next levelBox + // Two children per banch: the first one is the node label box + // and the second one the next level with the descendants + for (let i = 0; i < nextLevelBox.children.length; i++ ) { + this._recursiveNodeSelection(nextLevelBox.children[i++], select); + } + } + } - _render(){ - let html = ''; - this.sortedCalcs.forEach( rowCalcData => { //leafId => { - //html+= getRowHtml(leafId, calc, calcType); - html+= getRowHtml(rowCalcData); - }); - this.tbody.innerHTML = html; - - InfoSys.addToInfoSystem(this.tbody); - - function getRowHtml(rowCalcData/*leafId, calc, calcType*/){ - - let calc = DataStore.getCalc( /*DataStore.getCalcReprIntId(*/rowCalcData.id); - let calcType = rowCalcData.type; - let calcProps = calcType+','+calc.functional_type+','+calc.code_name+ - ','+calc.core_electron_treatment+','+calc.basis_set_type; - - let repositoryLinkHtml = ''; - if (calc.calculation_pid !== null && calc.calculation_pid !== undefined) - repositoryLinkHtml = - '<a href="'+REPOSITORY_LINK+calc.calculation_pid+'" target="blank"> ' - +'<img src="img/download.svg" height="20px" /> </a>'; - - return ` - <tr data-calc-id="${calc.id/*leafId*/}" data-calc-props="${calcProps}" class="data-row"> - <td>${calc.id/*leafId*/}</td> - <td> - <span info-sys-data="calculation-type.value:${calcType}"> - ${calcType}</span> - </td> - <td> - <span info-sys-data="functional-type.value:${calc.functional_type}"> - ${calc.functional_type}</span> - ${getOptValue(calc.functional_long_name)} - </td> - <td> - <span info-sys-data="code-name.value:${calc.code_name}"> - ${calc.code_name}</span> - ${getOptValue(calc.code_version)} - </td> - - <td> - <span info-sys-data="core-electron-treatment.value:${calc.core_electron_treatment}"> - ${getPotentialValue(calc.core_electron_treatment)}</span> - </td> - <td> - <span info-sys-data="basis-set-type.value:${calc.basis_set_type}"> - ${calc.basis_set_type}</span> - ${getOptValue(calc.basis_set_quality_quantifier)} - </td> - - <td style="padding-top: 8px;padding-bottom: 4px;">${repositoryLinkHtml} - </td> - - </tr>`; - } - - function getOptValue(value){ - if (value === undefined || value === null) return ''; - else return '('+value+')'; - } - - function getPotentialValue(value){ - if (value === 'pseudopotential') return 'pseudopotential'; - else if (value === 'full all electron') return 'full potential'; - else return value; - } - } - } + setTreeSelectionListener(listener){ + this.treeSelectionListener= listener; + } + + + setLeafMarkedListener(listener){ + this.leafMarkedListener = listener; + } - function getValueHTML(text,value){ - if (value === undefined || value === null) return ''; - else return '<b>'+text+'</b>: '+value+'<br>'; - } + getTreeSelectedCalcs(){ + return this.selectedCalcs; + } - function getSmearingHTML(value){ - let values = value.substring(1,value.length-1).split(','); - return (values[0] === 'none' ? '' : '<b>smearing kind</b>: '+values[0]+' , ')+ - (values[1] === '0' ? '' : '<b>smearing width</b>: '+values[1]); - } + _keepCalcMarked(select){ + if (select && (this.markedNode === null)){ + this._markFirstSelectedNode(); - // EXPORTS - module.exports = MethodologyDetails; + }else if (!select){ + let id= parseInt(this.markedNode.getAttribute('data-calc-id')); + if (this.selectedCalcs.size === 0){ + this.markedNode.className= this.markedNode.className.replace('-marked',''); + this.markedNode= null; + this.leafMarkedListener(null); + }else if (!this.selectedCalcs.has(id)){ + this._markFirstSelectedNode(); + } + } + } // _keepCalcMarked -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Sorting button component implementation - */ - - - let util = __webpack_require__(1); - - class SortingButton { - - constructor(id) { - - this.id = id; - this.ascending = true; - - this.element = document.createElement('span'); - this.element.innerHTML+=` - <img src="img/sorting_init.png" width="12px" - style="margin-bottom: -1px; cursor: pointer"/> - `; - this.image = this.element.querySelector('img'); - - if (id === 'id') - this.image.setAttribute('src','img/sorting_ascending.png'); - - this.element.addEventListener('click', e => { - this.ascending = !this.ascending; - this.image.setAttribute('src', - 'img/sorting_'+(this.ascending ? 'ascending' : 'descending')+'.png'); - this.listener(this.ascending, this.id); - }); - } + _markFirstSelectedNode(){ + let calcNodeBoxes = this.element.getElementsByClassName('calc-l'); + for (var i = 0; i < calcNodeBoxes.length; i++) + if (calcNodeBoxes[i].className.indexOf('node-selected') >= 0){ + this._setMarkedCalc(calcNodeBoxes[i]); + return; + } + } - init(){ - this.image.setAttribute('src','img/sorting_init.png'); - } + _setMarkedCalc(nodeBox){ + if (this.markedNode !== null){ + this.markedNode.className= this.markedNode.className.replace('-marked',''); + this.markedNode.querySelector('div').style.display = 'none'; + let folderIcon = this.markedNode.querySelector('.folder-icon'); + if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder.png'; + } + nodeBox.className += '-marked'; + let folderIcon = nodeBox.querySelector('.folder-icon'); + if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder-sel.png'; + nodeBox.querySelector('div').style.display = 'block'; + this.markedNode= nodeBox; - setListener(listener){ - this.listener = listener; - } + // The parent tree node is unfolded in order to show the leaf selected + let foldingElement = nodeBox.parentElement.previousElementSibling.firstElementChild; + if (foldingElement.className === 'node-folded'){ + foldingElement.className = 'node-unfolded'; + foldingElement.parentElement.nextElementSibling.style.display= 'block'; + } - } + if (this.leafMarkedListener !== undefined) + this.leafMarkedListener(nodeBox.getAttribute('data-calc-id')); + } - // EXPORTS - module.exports = SortingButton; + showCalcsGraphDataAvalability(bool){ + let elements= this.element.getElementsByClassName('calc-graph-aval'); + for (var i = 0; i < elements.length; i++) + elements[i].style.display= (bool ? 'inline' : 'none'); + } + setHeight(heightPx){ + this.element.style.height = heightPx+'px'; + } -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - 'Details' view container that shows the material calculations info related to - their vibrational and thermal properties. - */ - - "use strict"; - - let DetailsViewBase = __webpack_require__(20); - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let DataStore = __webpack_require__(2); - let LoadingPopup = __webpack_require__(4); - - let HeatCapPlotter = __webpack_require__(17); - let HelmholtzPlotter = __webpack_require__(29); - let PhononDispDOSPlotter = __webpack_require__(30); - - - class ThermalPropsDetails extends DetailsViewBase{ - - constructor() { - super('Thermal Properties'); - this.firstId; - this.lastId; - this.element.innerHTML+=` - <div style="float: left; width: 30%;"> - <div class="view-box"> - <div class="title">Calculations </div> - <div class="navTreeWrapper"></div> - </div> - </div> - - <div style="float: right; width: 70%;"> - <div class="view-box thermal-properties-box"> - - <div class="title">Vibrational and thermal properties</div> - - <div style="padding-top: 10px;"> - <div class="tree-leaf-title"></div> - </div> - - <div class="calc-disp-dos-plotter" style="padding: 30px 100px; "> - <div class="info-fields-label" style="float: left; width: 52%; "> - <span info-sys-data="phonon-dispersion">Phonon dispersion </span> - </div> - <div class="info-fields-label" style="float: left;"> - <span info-sys-data="phonon-DOS">Phonon DOS </span> - </div> - <div style="clear: both;"></div> - </div> - - - <div class="band" > - <div style="padding: 30px 50px; display: flex; justify-content: space-around; "> - - <div > - <div class="info-fields-label" > - <span info-sys-data="specific-heat-cv">Specific heat</span> - </div> - <div class="heat-plotter" > </div> - </div> - - <div> - <div class="info-fields-label" > - <span info-sys-data="helmholtz-free-energy">Helmholtz free energy</span> - </div> - <div class="helmholtz-plotter" > </div> - </div> - - </div> - </div> - - </div> - </div> - `; - - this.navTreeWrapper = - this.element.getElementsByClassName("navTreeWrapper")[0]; - - this.rightBox = this.element.querySelector('.thermal-properties-box'); - this.leafTitle = this.element.querySelector('.tree-leaf-title'); - - this.dispDosPlotter = new PhononDispDOSPlotter(); - this.heatPlotter= new HeatCapPlotter(); - this.helmholtzPlotter= new HelmholtzPlotter(); - - InfoSys.addToInfoSystem(this.element); - } +} // class NavTree - _events() { - super._events(); - } +function keepTreeIntegrity(nodeBox, select){ - updateSelection(leafIds/*Set*/){ - //console.log('E-StructureDetails UPDATING calcs: ',calcs.values().next().value); - if (leafIds.size > 0){ - this.rightBox.style.visibility = 'visible'; - let counter= 0; - leafIds.forEach( calcId => { - counter++; - if (counter === 1) this.firstId = calcId; - else if (counter === leafIds.size) this.lastId = calcId; - }); - }else - this.rightBox.style.visibility = 'hidden'; - //console.log('METHODOLOGY '+this.tbody.innerHTML); - } + if (nodeBox.className.indexOf('material-l') >= 0) return; + let levelBox= nodeBox.parentElement; + for (let i = 0; i < levelBox.children.length; i++ ) { + let siblingNodeBox = levelBox.children[i++]; + if (siblingNodeBox !== nodeBox && + siblingNodeBox/*.children[1]*/.className.indexOf('selected') < 0) + return; + } + let parentNodeBox= levelBox.previousElementSibling; + if (select){ + parentNodeBox.className += ' node-selected'; + }else{ + let index= parentNodeBox.className.indexOf(' node-selected'); + parentNodeBox.className= parentNodeBox.className.substring(0,index); + } + keepTreeIntegrity(parentNodeBox, select); +} - updateMarkedLeaf(leafId){ - //console.log('updateMarkedCalc ',calc); - if (leafId === null){ - this.leafTitle.innerHTML = 'NO SELECTION'; - return; - //this.bandGapField.textContent= ''; - }else{ - if (DataStore.getGroups().has(leafId)){ - this.leafTitle.innerHTML = leafId+ - ' ('+DataStore.getGroups().get(leafId).calcs.size+')'; - }else - this.leafTitle.innerHTML = leafId; - } - - let calc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); - - if (!this.dispDosPlotter.isAttached()){ - this.dispDosPlotter.attach(this.element.querySelector('.calc-disp-dos-plotter'),undefined,360); - this.heatPlotter.attach(this.element.querySelector('.heat-plotter'),317,317); - this.helmholtzPlotter.attach(this.element.querySelector('.helmholtz-plotter'),317,317); - } - if (calc === null || (!calc.has_phonon_dos && !calc.has_phonon_dispersion - && !calc.has_thermal_properties)){ - this.dispDosPlotter.setNoData(); - this.heatPlotter.setNoData(); - this.helmholtzPlotter.setNoData(); - }else{ - LoadingPopup.show(); - let matId = DataStore.getMaterialData().id; - - - - util.serverReq(util.getMaterialCalcURL(matId, calc.id,'phonon_dos'), e=> { - let dosData= JSON.parse(e.target.response).phonon_dos; - - util.serverReq(util.getMaterialCalcURL(matId, calc.id, - 'phonon_dispersion'), e2 => { - let dispersionData= JSON.parse(e2.target.response).phonon_dispersion; - this.dispDosPlotter.setUpAndData(dispersionData, dosData); - - if (calc.has_thermal_properties){ - - util.serverReq(util.getMaterialCalcURL(matId, calc.id, - 'specific_heat_cv'), e3 => { - let sHeatData= JSON.parse(e3.target.response).specific_heat_cv; - this.heatPlotter.setData(sHeatData); - }); - - util.serverReq(util.getMaterialCalcURL(matId, calc.id, - 'helmholtz_free_energy'), e4 => { - let helmholtzData = JSON.parse(e4.target.response).helmholtz_free_energy; - this.helmholtzPlotter.setData(helmholtzData); - }); - - }else{ - this.heatPlotter.setNoData(); - this.helmholtzPlotter.setNoData(); - } - - LoadingPopup.hide(); - }); - }); - - } - } +// EXPORTS +module.exports = NavTree; - setPrevCalcListener(listener){ - this.prevCalcListener= listener; - } +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { - setNextCalcListener(listener){ - this.nextCalcListener= listener; - } +"use strict"; - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - // EXPORTS - module.exports = ThermalPropsDetails; + /* + This file implements the Overview view component in the Material Module. + */ -/***/ }, -/* 29 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - Helmholtz free energy plotter implementation - */ - - "use strict"; - - let svg = __webpack_require__(9); - let PlotterBase = __webpack_require__(18); - - - class HelmholtzPlotter extends PlotterBase{ - - constructor() { - super({left: 60, right: 16, top: 10, bottom: 32}); - this.tooltip; - } - setData(data){ - this.clear(); - // up to 600K data is taken - let indexOf600K = data.temperature.indexOf(600)+1; - let values = data.value.slice(0, indexOf600K); - let temperatures = data.temperature.slice(0, indexOf600K); - let yMaxValue = Math.max.apply(null, values); - let yMinValue = Math.min.apply(null, values); - let yAxisMaxValue = Math.ceil(yMaxValue/1000)*1000; - let yAxisMinValue = Math.floor(yMinValue/1000)*1000; +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let LoadingPopup = __webpack_require__(4); - this.setRangeAndLabels('T (K)', 0, 600, - 'F (J/kg)', yAxisMinValue, yAxisMaxValue); - this.drawAxis(4, null, 0); +let BSPlotter = __webpack_require__(7); +let DOSPlotter = __webpack_require__(11); +let HeatCapPlotter = __webpack_require__(12); +let MaterialMod = __webpack_require__(9); +let DataStore = __webpack_require__(1); - let polylinePoints = ''; - temperatures.forEach( (t, i) => { - let y = values[i];///1e-25; - polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(y - this.yMin); - }); - svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1'); - } +const ELEMENT_INCLUDED_MSG = 'ELEMENT ALREADY INCLUDED'; - } +class Overview { - // EXPORTS - module.exports = HelmholtzPlotter; + constructor() { + this.element = document.createElement('div'); + this.element.setAttribute('id','overview'); + this.materialId; + this.element.innerHTML=` -/***/ }, -/* 30 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* - Implements a graphical UI component composed of - a phonon DOS plotter (PhononDOSPlotter class, implemented in thsi file) and a - BSPlotter (showing Phonon Dispersion) - */ - - - let BSPlotter = __webpack_require__(14); - let InteractivePlotterBase = __webpack_require__(15); - let svg = __webpack_require__(9); - let util = __webpack_require__(1); - - - class PhononDispDOSPlotter{ - - constructor() { - this.element = document.createElement('div'); - //this.element.setAttribute('id','elementable'); - this.parentElement= null; - this.dispPlotter= new BSPlotter(); - this.dispPlotter.setPhononMode(); - - this.dosPlotter= new PhononDOSPlotter(); - } + <div class="material-title"> + </div> + <div style="float: left; width: 40%;"> + + <div id="structure-ov" class="view-box"> + <div class="title">Structure + <img style="float: right" class="to-detail" src="img/more.svg" /> + <div style="clear: both;"></div> + </div> - attach(element, width, height){ - element.appendChild(this.element); - this.dispPlotter.attach(this.element, height, height); - this.dosPlotter.attach(this.element, undefined, height); - this.parentElement= element; - } + <div class="viz-box" style="height: 260px; position: relative"></div> + <div class="footer"> + <div ><b><span>System type</span></b>: + <span class="system-type-field" ></span> + </div> + <div class="space-group-field" style="display: none"> + <b><span info-sys-data="space-group">Space group</span></b>: + <span class="space-group-value" ></span> + </div> + <div class="structure-type-field" style="display: none"> + <b><span info-sys-data="structure-type">Structure type</span></b>: + <span class="structure-type-value" ></span> + </div> + </div> + </div> - isAttached(){ - return this.parentElement !== null; - } +<!-- ***** Elastic Constants Box - setUpAndData(dispData, dosData){ - - this.hasDispData = (dispData !== undefined && dispData !== null); - this.hasDosData = (dosData !== undefined && dosData !== null); - - if (this.hasDispData){ - this.dispPlotter.setBandStructureData(dispData); - if (this.hasDosData) - this.dispPlotter.setRepaintListener( (yZoom, yOffset) => { - this.dosPlotter.setYZoomAndOffset(yZoom, yOffset); - this.dosPlotter.repaint(); - }); - }else - this.dispPlotter.setNoData(); - - if (this.hasDosData){ - this.dosPlotter.setPoints(dosData); - if (this.hasDispData) - this.dosPlotter.setRepaintListener( (yZoom, yOffset) => { - this.dispPlotter.setYZoomAndOffset(yZoom, yOffset); - this.dispPlotter.repaint(); - }); - }else - this.dosPlotter.setNoData(); - } + <div id="elastic-ov" class="view-box"> + <div class="title">Elastic constants + <img style="float: right" class="to-detail" src="img/more.svg" /> + <div style="clear: both;"></div> + </div> - setNoData(){ - this.dispPlotter.setNoData(); - this.dosPlotter.setNoData(); - } + <div class="info-fields"> + Not analyzed yet + </div> - } + </div> +--> + <div id="methodology-ov" class="view-box"> + <div class="title">Methodology + <img style="float: right" class="to-detail" src="img/more.svg" /> + <div style="clear: both;"></div> + </div> - class PhononDOSPlotter extends InteractivePlotterBase{ + <div class="info-fields"> - constructor() { - super({left: 4, right: 16, top: 0, bottom: 30}); - this.outOfRangeColorActivated = false; - } + <div class="info-fields-label" > Available calculations </div> - // detach if necessary and attach - attach(element, width, height){ - super.attach(element, height/2+this.margins.left, height); - } + <div style="float: left; width: 45%" > + <b><span info-sys-data="functional-type">Functional</span></b> + <div class="functional-field" > </div> + </div> + <div style="float: right; width: 45%" > + <b><span info-sys-data="code-name">Code</span></b> + <div class="code-field"> </div> + </div> + <div style="clear: both;"></div> + </div> + </div> - setPoints(points){ - - this.pointsSpin1 = []; - this.pointsSpin2 = []; - this._reset(); - - let pSpin1= points.dos_values[0]; - let pSpin2 = null; - if (points.dos_values.length === 2) pSpin2 = points.dos_values[1]; - let pointsY= points.dos_energies; - let pointsXInPlotRange = []; - let pointsYInPlotRange = []; - - for (var i = 0; i < pointsY.length; i++) { - let frecuency = pointsY[i]*5.034117012222e22; - let dosSpin1 = pSpin1[i]*0.029979; - let dosSpin2; - if (pSpin2 !== null) dosSpin2 = pSpin2[i]*0.029979246; - //console.log('POINTS : ',frecuency); - pointsXInPlotRange.push(dosSpin1); - if (pSpin2 !== null) pointsXInPlotRange.push(dosSpin2); - pointsYInPlotRange.push(frecuency); - //console.log('POINTS : ',pointsX[i], energy); - this.pointsSpin1.push({x: dosSpin1, y: frecuency}); - if (pSpin2 !== null) this.pointsSpin2.push({x: dosSpin2, y: frecuency}); - } - - let maxDosVal = Math.max.apply(null, pointsXInPlotRange); - let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange); - let minEnergyVal = Math.min.apply(null, pointsYInPlotRange); - - // x axis steps generation - let t = util.generateDiagramSteps(maxDosVal); - let xSteps = t[0], exp = t[1]; - - //console.log('formattedPoints paintPointsLine : ', this.formattedPoints.length); - this.setAxisRangeAndLabels(null,0,xSteps[xSteps.length-1]/*maxDosVal*1.2*/, - null,-50,320,minEnergyVal, maxEnergyVal, 100); - - svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom, - 'DOS (states/cm⁻¹)', 'middle', 'axis-steps-big'); - - 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); - //console.log('step ',xSteps[i], stepX); - svg.addText(this.axisGroup, stepX, 13, - (i === 0 ? '0' : xSteps[i].toFixed(exp)), 'middle', 'axis-steps-smaller'); - } - - this.repaint(); - } + </div> + <div style="float: right; width: 60%;"> - repaintData(){ - let polylinePoints = ''; - for (var i = 0; i < this.pointsSpin1.length; i++) { - polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+ - ' '+this.transformY(this.pointsSpin1[i].y); - //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); + <div id="e-structure-ov" class="view-box" > <!--style="height: 610px; "--> + <div class="title">Electronic structure + <img style="float: right" class="to-detail" src="img/more.svg" /> + <div style="clear: both;"></div> + </div> + + <div > <!-- style="margin: 12% 0; " --> + + <div style="float: left; width: 60%; "> + <div style="padding: 20px 0 20px 30px"> + <div class="info-fields-label"> + <span info-sys-data="band-structure">Band structure</span> + </div> + <div> + <div id="band-plotter" > </div> + </div> + + <div class="footer-bs-calc"></div> + </div> + </div> + + <div style="float: left; width: 40%; "> + <div style="padding: 20px 30px 20px 60px"> + <div class="info-fields-label"> + <span info-sys-data="DOS">DOS</span> + </div> + + <div> + <div id="dos-plotter" > </div> + </div> + <div class="footer-dos-calc"></div> + </div> + </div> + + + <div style="clear: both;"></div> + + <div class="spin-legend" style="font-size: 0.9em; padding: 6px 30px 10px; display: none"> + <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg> + Spin <span style='font-size: 1.1em'>⇧</span> + + <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg> + Spin <span style='font-size: 1.1em'>⇩</span> + </div> + + </div> + + <!-- + <div class="footer"> + <b>Band gap</b>: <span class="e-struct-field" ></span> + </div> + --> + </div> - polylinePoints = ''; - for (var i = 0; i < this.pointsSpin2.length; i++) { - polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+ - ' '+this.transformY(this.pointsSpin2[i].y); - //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y); - } - svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); + <div id="thermal-props-ov" class="view-box" > + <div class="title">Vibrational and thermal properties + <img style="float: right" class="to-detail thermal-props" src="img/more.svg" /> + <div style="clear: both;"></div> + </div> - } - } + <div style="padding: 36px; "> + <div class="info-fields-label"> + <span info-sys-data="heat-capacity-cv">Specific heat</span> + </div> - // EXPORTS - module.exports = PhononDispDOSPlotter; + <div> + <div id="heat-plotter" > </div> + </div> + <div class="footer-heat-calc" style="text-align: center"></div> + </div> -/***/ }, -/* 31 */ -/***/ function(module, exports, __webpack_require__) { + </div> - + </div> - /* - Pending to implement - */ - "use strict"; + <div style="clear: both;"></div> + `; - let DetailsViewBase = __webpack_require__(20); - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - //let DataStore = require('./DataStore.js'); + this.materialTitle= this.element.getElementsByClassName('material-title')[0]; + this.systemType= this.element.querySelector('.system-type-field'); + this.spaceGroupField = this.element.querySelector('.space-group-field'); + this.spaceGroupValue = this.element.querySelector('.space-group-value'); + this.structTypeField= this.element.querySelector('.structure-type-field'); + this.structTypeValue= this.element.querySelector('.structure-type-value'); + //this.band_gap = this.element.getElementsByClassName('e-struct-field')[0]; + //fields= this.element.getElementsByClassName('method-field'); + this.functional= this.element.querySelector('.functional-field');//fields[0]; + this.code= this.element.querySelector('.code-field');//fields[1]; - class ElasticConstDetails extends DetailsViewBase { + let fields= this.element.getElementsByClassName('to-detail'); + this.structureDetailBtn= fields[0]; + this.electronicStructDetailBtn= fields[2]; + this.methodologyDetailBtn= fields[1]; + this.thermalDetailBtn= fields[3]; +/* + this.elasticDetailBtn= fields[1]; + this.methodologyDetailBtn= fields[2]; + this.electronicStructDetailBtn= fields[3]; + this.thermalDetailBtn= fields[4]; + */ - constructor() { - super('Elastic constants'); + this.vizBox = this.element.getElementsByClassName('viz-box')[0]; + //this.cellViewer= null; - this.sortedLeafs = []; - this.markedCalc = null; + this.bandPlotter= null; + this.bsCalcIdBox = this.element.getElementsByClassName('footer-bs-calc')[0]; + this.dosPlotter= null; + this.dosCalcIdBox = this.element.getElementsByClassName('footer-dos-calc')[0]; + this.heatPlotter= null; + this.heatCalcIdBox = this.element.querySelector('.footer-heat-calc'); - this.element.innerHTML+=` + this.spinLegend = this.element.querySelector('.spin-legend'); - <div style="float: left; width: 27%;"> - <div class="view-box"> - <div class="title">Calculation </div> - <div class="navTreeWrapper"></div> - </div> - </div> + // For static ones + InfoSys.addToInfoSystem(this.element); - <div style="float: right; width: 73%;"> - <div class="view-box"> - <div class="title">Elastic constants</div> + // Store the state of the calcs chosen on the Elec. Structure box + this.eStructCalcs = { bs: null, dos: null}; + } - <div>Parameters</div> - <div ></div> + attachAndSetEvents(element){ + element.appendChild(this.element); + this._events(); + } - </div> - </div> - <div style="clear: both;"></div> - `; + _events() { - this.navTreeWrapper = - this.element.querySelector('.navTreeWrapper'); + this.structureDetailBtn.addEventListener( "click", (e) => { + util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.structure); + }); + this.electronicStructDetailBtn.addEventListener( "click", (e) => { + util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.electronicstruct); + }); - // For static ones - InfoSys.addToInfoSystem(this.element); + this.methodologyDetailBtn.addEventListener( "click", (e) => { + util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.methodology); + }); - this._events(); - } + this.thermalDetailBtn.addEventListener( "click", (e) => { + util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.thermalprops); + }); + +/* + this.elasticDetailBtn.addEventListener( "click", (e) => { + util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.elasticconst); + }); +*/ - _events() { - //super._events(); - /* - this.dataTableWrapper.addEventListener('click', (e) => { + //******* Optimize, genralize: + //this.element.querySelectorAll('.to-detail+.'+detailsId).addEventListener( "click", (e) => { + // util.setBrowserHashPath('material', this.materialId+'/'+detailsId); + } - let rowElement = e.target.parentElement; - if (rowElement.className.indexOf('data-row') < 0) - rowElement = rowElement.parentElement; - //console.log("TABLE EVENT ",rowElement); - if (rowElement.className.indexOf('data-row') >= 0){ - let id= rowElement.getAttribute('data-calc-id'); + getEStructChosenCalcs(){ + return this.eStructCalcs; + } - } + setDetailViewsListener(listener){ + this.detailViewsListener= listener; + } + setVisible(){ + this.element.style.display= 'block'; + } + + + setMaterialData() { + + let data = DataStore.getMaterialData(); + this.materialTitle.innerHTML= util.getMaterialTitle(data); + this.materialId = data.id; + + let isBulk = (data.system_type === 'bulk'); + this.systemType.textContent= data.system_type; + this.structTypeField.style.display = + (isBulk && data.structure_type !== null ? 'block' : 'none'); + this.spaceGroupField.style.display = (isBulk ? 'block' : 'none'); + + if (isBulk){ + this.structTypeValue.textContent= data.structure_type; + this.spaceGroupValue.textContent = data.space_group_number+ + ' ('+data.space_group_international_short_symbol+')'; + InfoSys.addElementToInfoSystem(this.spaceGroupValue, + 'space-group.value:'+data.space_group_number); + } + + } + + + _evaluateCalc(calc){ + let value = 0; + if (calc.functional_type === 'GGA') value += 100; + if (calc.has_band_structure && calc.has_dos) value += 10; + switch (calc.code_name.trim()) { + case 'FHI-aims': value += 3; break; + case 'VASP': value += 2; break; + case 'Quantum Espresso': value += 1; break; + } + return value; + } + + + setCalcsData(markedTreeLeafs) { + //console.log('setCalcsData: '+JSON.stringify(data)); + let calcs = DataStore.getCalculations(); + + let functionalMap = new Map(); + let codeMap = new Map(); + let calcWithBS = null, calcWithDOS = null, calcWithHeat = null; + let calcBSEvaluation = -1, calcDOSEvaluation = -1; + + for (let i = 0; i < calcs.length; i++) { + + if (functionalMap.has(calcs[i].functional_type)){ + let num= functionalMap.get(calcs[i].functional_type); + // Because it's a number we can't use the returned value as a reference + functionalMap.set(calcs[i].functional_type,++num); + }else + functionalMap.set(calcs[i].functional_type,1); + + let codeNameTrimed= calcs[i].code_name.trim(); + if (codeMap.has(codeNameTrimed)){ + let num= codeMap.get(codeNameTrimed); + // Because it's a number we can't use the returned value as a reference + codeMap.set(codeNameTrimed,++num); + }else + codeMap.set(codeNameTrimed,1); + + let calcEvaluation = this._evaluateCalc(calcs[i]); + //console.log('calcEvaluation',calcEvaluation); + + if (calcs[i].has_band_structure && calcEvaluation > calcBSEvaluation){ + calcBSEvaluation = calcEvaluation; + calcWithBS = calcs[i]; + } + + if (calcs[i].has_dos && calcEvaluation > calcDOSEvaluation){ + calcDOSEvaluation = calcEvaluation; + calcWithDOS = calcs[i]; + } + //console.log('BS DOS Evaluation',calcBSEvaluation, calcDOSEvaluation); + + if (calcWithHeat === null && calcs[i].has_thermal_properties) + calcWithHeat = calcs[i]; + } + + if (calcWithBS !== null) this.eStructCalcs.bs = calcWithBS.id; + if (calcWithDOS !== null) this.eStructCalcs.dos = calcWithDOS.id; + + let tempCalcId = null; + if (calcWithBS !== null) tempCalcId = calcWithBS.id; + else if (calcWithDOS !== null) tempCalcId = calcWithDOS.id; + + if (tempCalcId === null) markedTreeLeafs.eStruct = null; // no graph data + else if (DataStore.isInAnyNotDisabledGroup(tempCalcId)){ + markedTreeLeafs.eStruct = DataStore.getGroupLeafId(tempCalcId); + }else + markedTreeLeafs.eStruct = +tempCalcId; + + if (calcWithHeat === null) markedTreeLeafs.thermalProps = null; + else if (DataStore.isInAnyNotDisabledGroup(calcWithHeat.id)){ + markedTreeLeafs.thermalProps = DataStore.getGroupLeafId(calcWithHeat.id); + }else + markedTreeLeafs.thermalProps = +calcWithHeat.id; + //console.log('Overview - thermalPropsDetailsTreeLeaf: ', markedTreeLeafs.thermalProps); + + //this.band_gap.innerHTML= util.getBandGapStatsValue(calcs); + + let functionalHTML= ''; + functionalMap.forEach((number,functional) => { + functionalHTML+= '<span info-sys-data="functional-type.value:'+functional+ + '">'+number+' '+functional+'</span> <br> '; + }); + + this.functional.innerHTML= functionalHTML; + InfoSys.addToInfoSystem(this.functional); + + let codeHTML= ''; + codeMap.forEach((number,codeName) => { + codeHTML+= '<span info-sys-data="code-name.value:'+codeName+ + '">'+number+' '+codeName+'</span> <br> '; + //codeHTML+= number+' '+codeName+' <br> '; + }); + this.code.innerHTML= codeHTML; + InfoSys.addToInfoSystem(this.code); + + + if (calcWithBS === null && calcWithDOS === null){ + document.getElementById('e-structure-ov').style.display = 'none'; + DataStore.hasElecStructureData = false; + }else{ + document.getElementById('e-structure-ov').style.display = 'block'; + DataStore.hasElecStructureData = true; + + if (this.bandPlotter === null){ + this.bandPlotter= new BSPlotter(); + this.bandPlotter.attach(document.getElementById('band-plotter'),undefined,316); + } + if (this.dosPlotter === null){ + this.dosPlotter= new DOSPlotter({left: 40, right: 16, top: 0, bottom: 30}); + this.dosPlotter.attach(document.getElementById('dos-plotter'),undefined,317); + } + + if (calcWithBS === null){ + this.bandPlotter.setNoData(); + this.bsCalcIdBox.innerHTML = ''; + }else{ + let url = util.getMaterialCalcURL(calcWithBS.material,calcWithBS.id, + 'band_structure'); + LoadingPopup.show(); + util.serverReq(url, e => { + if (e.target.status === 200){ + let bandStructData= JSON.parse(e.target.response).band_structure; + this.bandPlotter.setBandStructureData(bandStructData); + this.bsCalcIdBox.innerHTML = 'From calculation <b>'+calcWithBS.id+ + '</b><br><span style="font-size: 0.8em">('+calcWithBS.functional_type+' - '+calcWithBS.code_name+')</span>'; + if (bandStructData.segments[0].band_energies.length === 2) + this.spinLegend.style.display = 'block'; + } + LoadingPopup.hide(); + }); + } + + if (calcWithDOS === null){ + this.dosPlotter.setNoData(); + this.dosCalcIdBox.innerHTML = ''; + }else{ + let url = util.getMaterialCalcURL(calcWithDOS.material,calcWithDOS.id, + 'dos'); + LoadingPopup.show(); + util.serverReq(url, e => { + if (e.target.status === 200){ + let dosData= JSON.parse(e.target.response).dos; + + this.dosPlotter.setPoints(dosData);//paintPointsLine(dosData); + this.dosCalcIdBox.innerHTML = 'From calculation <b>'+calcWithDOS.id+ + '</b><br><span style="font-size: 0.8em">('+calcWithDOS.functional_type+' - '+calcWithDOS.code_name+')</span>'; + if (dosData.dos_values.length === 2) + this.spinLegend.style.display = 'block'; + } + LoadingPopup.hide(); + }); + } + } + + if (calcWithHeat === null){ + document.getElementById('thermal-props-ov').style.display = 'none'; + DataStore.hasThermalData = false; + }else{ + document.getElementById('thermal-props-ov').style.display = 'block'; + DataStore.hasThermalData = true; + + if (this.heatPlotter === null){ + this.heatPlotter= new HeatCapPlotter(); + this.heatPlotter.attach(document.getElementById('heat-plotter'),undefined,317); + } + + if (calcWithHeat === null){ + this.heatPlotter.setNoData(); + this.heatCalcIdBox.innerHTML = ''; + }else{ + let url = util.getMaterialCalcURL(calcWithHeat.material, calcWithHeat.id, + 'specific_heat_cv'); + LoadingPopup.show(); + util.serverReq(url, e => { + if (e.target.status === 200){ + let heatData= JSON.parse(e.target.response).specific_heat_cv; + //console.log(heatData); + this.heatPlotter.setData(heatData); + this.heatCalcIdBox.innerHTML = 'From calculation <b>'+calcWithHeat.id+'</b>'+ + '</b> <span style="font-size: 0.8em">('+calcWithHeat.functional_type+' - '+calcWithHeat.code_name+')</span>'; + } + LoadingPopup.hide(); + }); + } + } + + } // setCalcsData function + +} + +// EXPORTS +module.exports = Overview; + + +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + 'Details' view container that shows all the material info related to + its structure. + + This container is extremely complex. + + In the file there are two defined (classes) components used in the container: + - TreeLeafViewer: The panel on the right showing the data of the tree leaf marked + - SummaryByFunctionalsComponent: the component (central panel, below part) + showing a summary for the selected item on the tree (by functional) + */ + + + +let DetailsViewBase = __webpack_require__(6); +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let CalcSelectorBar = __webpack_require__(13); +let StatsViewer = __webpack_require__(14); +let EquationOfStateViewer = __webpack_require__(24); +let DataStore = __webpack_require__(1); +let LoadingPopup = __webpack_require__(4); + + + +class StructureDetails extends DetailsViewBase { + + constructor() { + super('Structure'); + this.groupsData; // the calcs groups data is necesary to be shown + + this.firstId; + this.lastId; + + this.element.innerHTML+=` + + <div style="float: left; width: 36%;"> + <div class="view-box"> + <div class="title">Structure </div> + <div class="viz-box" style="height: 400px; position: relative"></div> + + <div class="footer-flex-wrapper"> + + <div class="fields-container"> + <div><b><span>System type</span></b>: + <span class="struct-field" ></span> + </div> + <div class="structure-type-field" style="display: none"> + <b><span info-sys-data="structure-type">Structure type</span></b>: + <span class="structure-type-value" ></span> + </div> + <div class="structure-prototype-field" style="display: none"> + <b><span info-sys-data="structure-prototype">Structure prototype</span></b>: + <span class="structure-prototype-value" ></span> + </div> + <div class="strukturbericht-field" style="display: none"> + <b><span info-sys-data="strukturbericht">Strukturbericht designation</span></b>: + <span class="strukturbericht-value" ></span> + </div> + </div> + + <div class="footer-flex" style="display: none"> + + <div class="fields-container" + style="flex-basis: 70%; border-right: 1px solid #E4E4E4; "> + + <div> + <b><span info-sys-data="crystal-system">Lattice</span></b>: + <span class="lattice-value" ></span> + </div> + <div> + <b><span info-sys-data="space-group">Space group</span></b>: + <span class="space-group-value" ></span> + </div> + <div> + <b><span info-sys-data="point-group">Point group</span></b>: + <span class="point-group-value" ></span> + </div> + </div> + + <div style="flex-basis: 30%; margin-left: 30px;"> + <div class="fields-container"> + <div><b><span info-sys-data="wyckoff-position-population">Wyckoff sites</span></b></div> + <div class="wyckoff-sites-value"> </div> + </div> + </div> + + </div> + + </div> + + </div> + </div> + + <div style="float: left; width: 36%;"> + <div class="view-box"> + <div class="title">Calculations</div> + <div class="navTreeWrapper"></div> + + <div class="summary-title">Summary </div> + <div style="font-size: 0.85em; text-align: center; padding: 4px;">Based on the calculations selected above</div> + + <div class="info-fields summary-box"> + <!-- Lattice constants Cell volume, Density panel dynamically generated + --> + </div> + </div> + </div> + + <div style="float: right; width: 28%;"> + <div class="calc-specifics-box"> + + <div style="padding-top: 10px; " > + <div class="tree-leaf-title"></div> + </div> + + <div class="tree-leaf-viewer-host"></div> + + </div> + </div> + + </div> + </div> + `; + + this.navTreeWrapper = + this.element.getElementsByClassName("navTreeWrapper")[0]; + + let fields= this.element.getElementsByClassName('struct-field'); + this.systemTypeField= fields[0]; + this.structTypeField= this.element.querySelector('.structure-type-field'); + this.structTypeValue= this.element.querySelector('.structure-type-value'); + this.structPrototypeField= this.element.querySelector('.structure-prototype-field'); + this.structPrototypeValue= this.element.querySelector('.structure-prototype-value'); + this.strukturberichtField= this.element.querySelector('.strukturbericht-field'); + this.strukturberichtValue= this.element.querySelector('.strukturbericht-value'); + + this.lowerBox = this.element.querySelector('.footer-flex'); + this.latticeValue= this.element.querySelector('.lattice-value'); + this.spaceGroupValue= this.element.querySelector('.space-group-value'); + this.pointGroupValue= this.element.querySelector('.point-group-value'); + this.wyckoffValue= this.element.querySelector('.wyckoff-sites-value'); + + this.summaryByFunctionals = null; + + this.leafTitle = this.element.querySelector('.tree-leaf-title'); + + this.summaryBox = this.element.querySelector('.summary-box'); + + this.calcSpecificsBox = this.element.querySelector('.calc-specifics-box'); + + this.treeLeafViewer = new TreeLeafViewer(); + this.element.querySelector('.tree-leaf-viewer-host'). + appendChild(this.treeLeafViewer.element); + + this.vizBox = this.element.querySelector('.viz-box'); + + // For static ones + InfoSys.addToInfoSystem(this.element); + } + + + setMaterialData() { + let data = DataStore.getMaterialData(); + super.setMaterialData(data); + this.isBulk = (data.system_type === 'bulk'); + + this.systemTypeField.textContent= data.system_type; + this.structTypeField.style.display = + (this.isBulk && data.structure_type !== null ? 'block' : 'none'); + this.structPrototypeField.style.display = + (this.isBulk && data.structure_prototype !== null ? 'block' : 'none'); + this.strukturberichtField.style.display = + (this.isBulk && data.strukturbericht_designation !== null ? 'block' : 'none'); + + this.lowerBox.style.display = (this.isBulk ? 'flex' : 'none'); + + if (this.isBulk){ + this.structTypeValue.textContent= data.structure_type; + this.structPrototypeValue.textContent= data.structure_prototype; + this.strukturberichtValue.textContent= data.strukturbericht_designation; + this.spaceGroupValue.textContent = data.space_group_number + +' ('+data.space_group_international_short_symbol+')'; + this.pointGroupValue.textContent = data.point_group; + this.latticeValue.textContent = data.crystal_system; + + // wyckoff processing + let wyckoffMap = new Map(); + let valueSet= new Set(); + + for (var i = 0; i < data.elements.length; i++) { + let element = util.ELEMENTS[data.elements[i].label-1]; + if (wyckoffMap.has(element)){ + wyckoffMap.get(element).add(data.elements[i].wyckoff); + }else { + let newSet = new Set(); + newSet.add(data.elements[i].wyckoff); + wyckoffMap.set(element, newSet); + } + } + let wyckoffHTML= ''; + wyckoffMap.forEach((posSet, element) => { + let firstPos = true; + wyckoffHTML += '<tr> <td>'+element+': </td>'; + posSet.forEach( pos => { + if (firstPos){ + firstPos = false; + wyckoffHTML += '<td>'+pos+'</td></tr>'; + }else + wyckoffHTML += '<tr><td> </td><td>'+pos+'</td></tr>'; + }); + }); + + this.wyckoffValue.innerHTML = '<table>' + wyckoffHTML+'</table>'; + } + + InfoSys.addElementToInfoSystem(this.spaceGroupValue, + 'space-group.value:'+data.space_group_number); + InfoSys.addElementToInfoSystem(this.latticeValue, + 'crystal-system.value:'+data.crystal_system); + InfoSys.addElementToInfoSystem(this.pointGroupValue, + 'point-group.value:'+data.point_group); + //InfoSys.addElementToInfoSystem(this.wyckoffValue, 'wyckoff-position-population.value:'+); + } + + + updateSelection(leafIds /*Set*/) { + //console.log('StructureDetails.updateSelection: ', leafIds); + if (leafIds.size > 0){ + this.summaryBox.style.visibility = 'visible'; + + let summaryCalcSet = new Set(); + leafIds.forEach( leafId => { + let calc; + if (DataStore.getGroups().has(leafId)){ + calc = DataStore.getCalc(DataStore.getGroups().get(leafId).method_representative); + }else + calc = DataStore.getCalc(parseInt(leafId)); + summaryCalcSet.add(calc); + }); + + let calcMapByFunctional = getCalcMapByFunctional(summaryCalcSet); + if (this.summaryByFunctionals === null) + this.summaryByFunctionals = + new SummaryByFunctionalsComponent(calcMapByFunctional, this.summaryBox); + else + this.summaryByFunctionals.build(calcMapByFunctional); + + let counter= 0; + leafIds.forEach( calcId => { + counter++; + if (counter === 1) this.firstId = calcId; + else if (counter === leafIds.size) this.lastId = calcId; + }); + + }else{ + this.summaryBox.style.visibility = 'hidden'; + } + + function getCalcMapByFunctional(summaryCalcSet){ + + let functCalcMap = new Map(); + summaryCalcSet.forEach( calc => { + if (functCalcMap.has(calc.functional_type)){ + functCalcMap.get(calc.functional_type).add(calc); + + }else{ // New functional + let newFunctionalArray = new Set(); + newFunctionalArray.add(calc); + functCalcMap.set(calc.functional_type, newFunctionalArray); + } + }); + //console.log('functCalcMap: ',functCalcMap); + return functCalcMap; + } + } + + + updateMarkedLeaf(leafId){ + + if (leafId !== null){ + this.calcSpecificsBox.style.visibility = 'visible'; + //this.calcSpecificsBox.style.backgroundColor= '#FFF7EB'; + + if (DataStore.getGroups().has(leafId)){ + this.leafTitle.innerHTML = leafId+ + ' ('+DataStore.getGroups().get(leafId).calcs.size+')'; + }else + this.leafTitle.innerHTML = leafId; + + }else + this.calcSpecificsBox.style.visibility = 'hidden'; + //this.calcSpecificsBox.style.backgroundColor= 'white'; + //this.leafTitle.innerHTML = 'NO SELECTION'; + + this.treeLeafViewer.update(leafId, DataStore.getGroups().get(leafId)); + } + +} + + +class TreeLeafViewer{ + + constructor(hostClass){ + this.groupCalcs = null; + this.element = document.createElement('div'); + this.element.innerHTML = ` + <div> + + <div class="group-components" style="display: none"> + <div style="padding: 10px 0 30px 10px; " class="eos-host"> + </div> + + <div style="padding-top: 10px; " class="calc-selector-host"> + </div> + </div> + + <div class="info-fields"> + <div><b>Lattice constants</b></div> + <div class="latt-constants"></div> + <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b>: + <span class="volume-value" ></span> + </div> + <!-- <div><b>Pressure</b>: <span class="" ></span> </div>--> + <div class="density-field"><b>Density</b>: + <div class="stats-fields" > + <span info-sys-data="mass-density">Mass density</span> = + <span class="mass-density-value" ></span> + </div> + <div class="stats-fields" > + <span info-sys-data="atomic-density">Atomic density</span> = + <span class="atomic-density-value" ></span> + </div> + </div> + + <div class="energy-field"><b><span info-sys-data="energies">Energies</span></b> (code-specific)</div> + <div class="energy-descomp"> </div> + + <div class="wyckoff-pos-calc-field" > + <b><span info-sys-data="free-wyckoff-parameters">Wyckoff sites</span></b> + (fractional coordinates) + <div class="wyckoff-pos-calc-table"> </div> + </div> + + </div> + + </div> + `; + + this.groupComponents = this.element.querySelector('.group-components'); + + this.calcSelector = new CalcSelectorBar('calc-selector-bar','60%'); + this.element.querySelector('.calc-selector-host'). + appendChild(this.calcSelector.element); + + this.lattConstantsField = this.element.querySelector('.latt-constants'); + this.volumeField = this.element.querySelector('.volume-field'); + this.volumeValue = this.element.querySelector('.volume-value'); + this.densityField = this.element.querySelector('.density-field'); + this.massDensityValue = this.element.querySelector('.mass-density-value'); + this.atomicDensityValue = this.element.querySelector('.atomic-density-value'); + + this.energyField= this.element.querySelector('.energy-field'); + this.energyDescompValue= this.element.querySelector('.energy-descomp'); + + this.wyckoffPosField = this.element.querySelector('.wyckoff-pos-calc-field'); + this.wyckoffPosTable = this.element.querySelector('.wyckoff-pos-calc-table'); + + this.eosViewer = new EquationOfStateViewer(); + this.eosViewer.attach(this.element.querySelector('.eos-host'),320, 280); + + this.eosViewer.setClickPointListener( calc => { + this.groupCalcUpdate(calc+''); + }); + + InfoSys.addToInfoSystem(this.element); + + this._events(); + } + + + _events() { + + this.calcSelector.setPrevListener(e => { + //console.log('calcSelectorPrevBtn'); + if (this.groupIndex > 0){ + this.groupCalcUpdate(this.groupCalcs[--this.groupIndex]+''); + return this.groupIndex === 0; // the first + } + }); + + this.calcSelector.setNextListener( e => { + //console.log('calcSelectorNextBtn'); + if (this.groupIndex < this.groupCalcs.length-1){ + this.groupCalcUpdate(this.groupCalcs[++this.groupIndex]+''); + return this.groupIndex === this.groupCalcs.length-1; // the last + } + }); + + } + + + update(leafId, groupData){ + + this.representative = leafId; + this.isGroup = false; + //console.log('SET: ',groupData); + if (groupData !== undefined){ + + this.groupCalcs = Array.from(groupData.calcs); + this.groupIndex = this.groupCalcs.indexOf( + DataStore.getCalcReprIntId(this.representative)); + + this.isGroup = true; + this.groupComponents.style.display = 'block'; + + this.eosViewer.clear(); + let pointsX = [], pointsY = [];//, calcIds = []; + + this.groupCalcs.forEach( calcId => { + //calcIds.push(calcId); + let calc = DataStore.getCalc(calcId); + //console.log('CALC: ', calcId, calc); + pointsX.push(calc.cell_volume/1e-30); + let yVal = 555; // Trick: signal value + calc.energy.forEach( e => { + if (e.e_kind === 'Total E'){ + yVal = e.e_val/1.602176565e-19; + } + }); + pointsY.push(yVal); + }); + + // Method re + let mReprCalc = DataStore.getCalc(groupData.method_representative); + //console.log('mReprCalc: ', mReprCalc); + let eZero; + mReprCalc.energy.forEach( e => { + if (e.e_kind === 'Total E') + eZero = e.e_val/1.602176565e-19; + }); + //console.log('CALC: ', pointsX, pointsY, groupCalcsForChart); + this.eosViewer.draw(pointsX, pointsY, this.groupCalcs, eZero); + + }else{ + this.groupComponents.style.display = 'none'; + } + this.groupCalcUpdate(leafId); + } + + + groupCalcUpdate(leafId){ + + if (leafId !== null){ + + let calcId = DataStore.getCalcReprIntId(leafId); + if (this.groupCalcs !== null){ + this.groupIndex = this.groupCalcs.indexOf(calcId); + if (this.groupIndex >= 0){ + let t = calcId+' ('+(this.groupIndex+1)+'/'+this.groupCalcs.length+')'; + this.calcSelector.setState(t, this.groupIndex === 0, + this.groupIndex === this.groupCalcs.length-1); + + this.eosViewer.selectCalc(calcId); + } + } + + //console.log('LEAF UPDATE', calc.id, this.groupIndex); + let is2Dsystem = (DataStore.getMaterialData().system_type === '2D'); + let isBulk = (DataStore.getMaterialData().system_type === 'bulk'); + let calc = DataStore.getCalc(calcId); + let lattParams= util.getNumberArray(calc.lattice_parameters); + + let lattCFieldHTML = ((is2Dsystem || isBulk) ? + `<div>b = ${util.m2Angstrom(lattParams[1])}</div>` : ''); + lattCFieldHTML += (isBulk ? + `<div>c = ${util.m2Angstrom(lattParams[2])}</div>` : ''); + + let lattBetaGammaFieldHTML = ((is2Dsystem || isBulk) ? + `<div>α = ${util.rad2degree(lattParams[3])}</div>` : ''); + + lattBetaGammaFieldHTML += (isBulk ? + `<div>β = ${util.rad2degree(lattParams[4])}</div> + <div>γ = ${util.rad2degree(lattParams[5])}</div>` : ''); + + this.lattConstantsField.innerHTML= ` + <div style="float: left; "> + <div>a = ${util.m2Angstrom(lattParams[0])}</div> + ${lattCFieldHTML} + </div> + <div style="float: left; padding-left: 40px;"> + ${lattBetaGammaFieldHTML} + </div> + <div style="clear: both;padding: 0"></div> + `; + + this.densityField.style.display = (isBulk ? 'block' : 'none'); + this.volumeField.style.display = (isBulk ? 'block' : 'none'); + if (isBulk){ // bulk type + this.volumeValue.innerHTML= util.m3ToAngstrom3(calc.cell_volume); + //this.pressureCalcField.textContent= calc.pressure; + this.atomicDensityValue.innerHTML= util.toAngstromMinus3(calc.atomic_density); + this.massDensityValue.innerHTML= calc.mass_density.toFixed(1)+' kg/m<sup>3</sup>'; + } + + let r= util.serverReq(util.getCalcEnergiesURL(DataStore.getMaterialData().id, calc.id), () => { + let value = false; + if (r.status === 200){ + let energies= JSON.parse(r.response).results; + for (var i = 0; i < energies.length; i++) + if (energies[i].e_kind === 'Total E'){ + value = true; + this.energyDescompValue.innerHTML = + '<div>Total E = '+util.J2eV(energies[i].e_val)+' eV</div>'; + } + } + this.energyField.style.display = (value ? 'block' : 'none'); + this.energyDescompValue.style.display = (value ? 'block' : 'none'); + }); + + let thereIsWyckoffData = + (DataStore.getMaterialData().has_free_wyckoff_parameters + && calc.wyckoff_groups_json.length > 0); + + this.wyckoffPosField.style.display = (thereIsWyckoffData ? 'block' : 'none'); + + if (thereIsWyckoffData){ + let wyckoffMap = new Map(); // Map(element, Array of pairArray[w-pos, coor]) + calc.wyckoff_groups_json.forEach( d => { + // Only entries having items in .variables are included + if ( Object.keys(d.variables).length !== 0 ){ + + let varsHtml = ''; + //d.variables.forEach( v => { varsHtml += '<p>'+v+'</p>'; } ); + for (let v in d.variables) { + varsHtml += ''+v+' = '+d.variables[v].toFixed(2)+'<br>'; + } + + let wyckoffVarsPair = []; + wyckoffVarsPair.push(d.wyckoff_letter); + wyckoffVarsPair.push(varsHtml); + + if (wyckoffMap.has(d.element)){ + wyckoffMap.get(d.element).push(wyckoffVarsPair); + }else { + wyckoffMap.set(d.element, [wyckoffVarsPair]); + } + } + + }); + //console.log('wyckoffMap', wyckoffMap); + + let wyckoffHTML= ''; + wyckoffMap.forEach((posSet, element) => { + + posSet.sort( (a, b) => { + return (a[0] > b[0] ? 1 : -1); + }); + + let firstPos = true; + wyckoffHTML += '<tr > <td style="width: 30%;">'+element+' </td>'; + posSet.forEach( pos => { + if (firstPos){ + firstPos = false; + wyckoffHTML += '<td style="width: 30%; ">'+pos[0]+'</td><td style="width: 40%;">'+pos[1]+'</td></tr>'; + }else + wyckoffHTML += '<tr><td> </td><td>'+pos[0]+'</td><td>'+pos[1]+'</td></tr>'; + }); + }); + + this.wyckoffPosTable.innerHTML = '<table id="calc-wyckoff">' + wyckoffHTML+'</table>'; + +/* + for (var i = 0; i < data.elements.length; i++) { + let element = util.ELEMENTS[data.elements[i].label-1]; + if (wyckoffMap.has(element)){ + wyckoffMap.get(element).add(data.elements[i].wyckoff); + }else { + let newSet = new Set(); + newSet.add(data.elements[i].wyckoff); + wyckoffMap.set(element, newSet); + } + } + */ + + } + + + }else{ + console.log('THIS dOESNT BE REACHED'); + } + } + +} + + +class SummaryByFunctionalsComponent{ + + constructor(calcMapByFunctional, hostElement){ + this.calcMapByFunctional = calcMapByFunctional; + this.hostElement = hostElement; + this.graphTrigger = null; + this.viewType = 'text'; + this.functional = null; + this.hostElement.innerHTML+=` + <div style="float: left" > + <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab" + viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;"> + <rect x="0" y="0" width="2" height="15" /> + <rect x="3" y="5" width="1.8" height="7" /> + <rect x="6" y="3" width="1.8" height="9" /> + <rect x="9" y="6" width="1.8" height="6" /> + <rect x="12" y="2" width="1.8" height="10" /> + <rect x="2" y="13" width="13" height="2" /> + </svg> + <svg xmlns="http://www.w3.org/2000/svg" class="text-tab" + viewBox="0 0 15 15" width="15" height="15" style="fill: #777;"> + <rect x="0" y="1" width="15" height="2.5" /> + <rect x="0" y="6" width="15" height="2.5" /> + <rect x="0" y="11" width="15" height="2.5" /> + </svg> + </div> + + <div class="functional-tabs" style="float: right"> + </div> + + <div style="clear: both;"></div> + + <div class="content-placeholder" > + + <div style="display: block" class="text-panel" > + <div><b>Lattice constants</b>: + <div class="stats-fields latt-constants-field" > + </div> + </div> + <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b> (Å<sup>3</sup>): + <div class="stats-fields volume-value" > </div> + </div> + <div class="density-field"><b>Density</b> : + <div > + <div class="stats-fields" > + <span info-sys-data="mass-density">Mass density</span> (kg/m<sup>3</sup>) = + <span class="mass-density-value" ></span> + </div> + <div class="stats-fields" > + <span info-sys-data="atomic-density">Atomic density</span> (Å<sup>-3</sup>) = + <span class="atomic-density-value" ></span> + </div> + </div> + </div> + </div> + + <div style="display:none" class="chart-panel" > + <div class="charts-placeholder" > </div> + <div class="charts-selector" > + + </div> + </div> + + </div> + `; + this.chartTab = this.hostElement.querySelector('.chart-tab'); + this.textTab = this.hostElement.querySelector('.text-tab'); + this.functionalTabs = this.hostElement.querySelector('.functional-tabs'); + this.chartPanel = this.hostElement.querySelector('.chart-panel'); + this.textPanel = this.hostElement.querySelector('.text-panel'); + this.lattConstantsField = this.hostElement.querySelector('.latt-constants-field'); + this.volumeField = this.hostElement.querySelector('.volume-field'); + this.volumeFieldValue = this.hostElement.querySelector('.volume-value'); + this.densityField = this.hostElement.querySelector('.density-field'); + this.massDensityValue = this.hostElement.querySelector('.mass-density-value'); + this.atomicDensityValue = this.hostElement.querySelector('.atomic-density-value'); + + + this.calcMapByFunctional.forEach( (calcs, functionalName) =>{ + this.functionalTabs.innerHTML += + '<span class="tab" data-tab="'+functionalName+'">'+functionalName+'</span>'; + }); + + + this.statsViewer = new StatsViewer(); + let chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder'); + this.statsViewer.attach(chartsPlaceholder, 350, 200); + + this.chartsSelector = this.hostElement.querySelector('.charts-selector'); + + this.build(calcMapByFunctional); + + this.chartTab.addEventListener( "click", e => { + this.chartTab.style.fill = '#777'; + this.viewType = 'chart'; + this.textTab.style.fill = '#c7c7c7'; + this.chartPanel.style.display = 'block'; + this.textPanel.style.display = 'none'; + }); + + this.textTab.addEventListener( "click", e => { + this.textTab.style.fill = '#777'; + this.viewType = 'text'; + this.chartTab.style.fill = '#c7c7c7'; + this.textPanel.style.display = 'block'; + this.chartPanel.style.display = 'none'; + }); + + this.functionalTabs.addEventListener( "click", e => { + if (e.target.className === 'tab'){ + this.statsViewer.clear(); + this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') + .className = 'tab'; + this.functional = e.target.getAttribute('data-tab'); + this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') + .className = 'tab-selected'; + this._setData(); + } + }); + + this.chartsSelector.addEventListener( "click", e => { + if (e.target.className.indexOf('quantity') === 0){ + this.statsViewer.clear(); + let quantity = e.target.getAttribute('data-quantity'); + let stats = this.functionalQuantityMap.get(this.functional).get(quantity); + this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); + + this.chartsSelector.querySelector('.quantity-selected').className = 'quantity'; + e.target.className = 'quantity-selected'; + } + }); + + } + + _setData(){ + let is2Dsystem = (DataStore.getMaterialData().system_type === '2D'); + let isBulk = (DataStore.getMaterialData().system_type === 'bulk'); + let statsMap = this.functionalQuantityMap.get(this.functional); + + let lattCFieldHTML = ((is2Dsystem || isBulk) ? + `<div>b (Å) = ${statsMap.get('lattice_b').html}</div>` : ''); + + lattCFieldHTML += (isBulk ? + `<div>c (Å) = ${statsMap.get('lattice_c').html}</div>` : ''); + // Set text data + this.lattConstantsField.innerHTML = ` + <div style="float: left; "> + <div>a (Å) = ${statsMap.get('lattice_a').html}</div> + ${lattCFieldHTML} + </div> + <div style="float: left; padding-left: 40px;"> + ${util.getLatticeAnglesValues( + this.calcMapByFunctional.get(this.functional), is2Dsystem, isBulk)} + </div> + <div style="clear: both;padding: 0"></div> + `; + + let chartSelectorHTML= ` + <span class="quantity-selected" data-quantity="lattice_a">a</span> + `; + + if (is2Dsystem || isBulk) + chartSelectorHTML += `<span class="quantity" data-quantity="lattice_b">b</span>`; + + //console.log('statsMap', statsMap, statsMap.get('volume')); + this.densityField.style.display = (isBulk ? 'block' : 'none'); + this.volumeField.style.display = (isBulk ? 'block' : 'none'); + if (isBulk){ // bulk type + this.volumeFieldValue.innerHTML = statsMap.get('volume').html; + this.massDensityValue.innerHTML = statsMap.get('mass_density').html; + this.atomicDensityValue.innerHTML = statsMap.get('atomic_density').html; + chartSelectorHTML += ` + <span class="quantity" data-quantity="lattice_c">c</span> + <span class="quantity" data-quantity="volume">volume</span> + <span class="quantity" data-quantity="mass_density">mass density</span> + <span class="quantity" data-quantity="atomic_density">atomic density</span> + `; + } + + this.chartsSelector.innerHTML = chartSelectorHTML; + + // Set Charts data + let stats = statsMap.get('lattice_a'); + this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); + } + + + + build(calcMapByFunctional){ + this.calcMapByFunctional = calcMapByFunctional; + this.graphTrigger = null; + this.statsViewer.clear(); + //this.functional = null; + //this.functionalTabs.innerHTML = ''; + this.unfoldedElement = null; + this.functionalQuantityMap = new Map(); + + // Hide and deselect all the tabs before knowing the active ones + for (var i = 0; i < this.functionalTabs.children.length; i++) { + //console.log('functionalTabs',this.functionalTabs.children[i]); + this.functionalTabs.children[i].style.display = 'none'; + this.functionalTabs.children[i].className = 'tab'; + } + + // For each active functional + this.calcMapByFunctional.forEach( (calcs, functionalName) =>{ + let statsMap = util.getQuantityStatsMap(calcs); + this.functionalQuantityMap.set(functionalName, statsMap); + //show the active ones + this.functionalTabs.querySelector('[data-tab="'+functionalName+'"]') + .style.display = 'inline'; + }); + + // Select the selected functional + let functionals = Array.from( this.calcMapByFunctional.keys() ); + // If there isn't selected functional or the current functional is not active + if (this.functional === null || functionals.indexOf(this.functional) < 0) + this.functional = functionals[0]; // the first one is selected + this._setData(); + this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') + .className = 'tab-selected'; + + InfoSys.addToInfoSystem(this.hostElement); + } + +} + + +// EXPORTS +module.exports = StructureDetails; + + +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + Equation of State plotter implementation + */ + + + +let svg = __webpack_require__(2); +let PlotterBase = __webpack_require__(5); + + +class EquationOfStateViewer extends PlotterBase{ + + constructor() { + super({left: 60, right: 20, top: 30, bottom: 40}); + this.tooltip; + this.calcPointMap= new Map(); + this.pointSelected = null; + } + + + draw(pointsX, pointsY, calcIds, eZero){ + + for (let i = 0; i < pointsY.length; i++) { + if (pointsY[i] !== 555) pointsY[i] -= eZero; + } + + function remove555(points){ + let goodPoints = []; + points.forEach( p => { + if (p !== 555) goodPoints.push(p); + }); + return goodPoints; + } + let goodPointsY = remove555(pointsY); + + let xMin = Math.min.apply(null,pointsX); + let xMax = Math.max.apply(null,pointsX); + let yMin = 0; + let yMax = Math.max.apply(null,goodPointsY); + + if (xMin === xMax) { xMin -= 1; xMax += 1; } + else{ + let gap = xMax - xMin; + xMin -= gap*0.1; xMax += gap*0.1; + } + if (yMin === yMax) { yMin -= 1; yMax += 1; } + else{ + let gap = yMax - yMin; + yMin -= gap*0.15; yMax += gap*0.1; + } + + this.setRangeAndLabels('Volume (ų)', xMin, xMax,'E - Eₘᵢₙ (eV)', yMin, yMax); + this.drawAxis(2, null); + // Y axis labels drawing + let self = this; + function drawYAxisLabel(element, y){ + let pixelY = self.y(y); + svg.addLine(element, 0, pixelY, -3, pixelY, 1); + let numberToPaint= -(pixelY/self.yRel) + self.yMin; + svg.addText(element,-5, pixelY+3, numberToPaint.toFixed(3), 'end', 'statisticsviewersteps'); + } + drawYAxisLabel(this.plotArea, 0); + drawYAxisLabel(this.plotArea, yMax/2); + drawYAxisLabel(this.plotArea, yMax); + + // zero line drawing + svg.addLine(this.plotArea, 0, this.y(0), this.plotRangeX, this.y(0), 'zeroline'); + + svg.addText(this.plotArea, this.x(xMax), + this.y(0)+12, 'Eₘᵢₙ: '+eZero.toFixed(3)+' eV', 'end', 'axis-steps'); + // points drawing + for (let i = 0; i < pointsX.length; i++) { + + let styleClass = (i === 0 ? 'eos-viewer-sel' : 'eos-viewer'); + let r = (i === 0 ? 6 : 3); + let yVal = (pointsY[i] === 555 ? 20 : this.y(pointsY[i])); // Trick + let pointElement = + svg.addPoint(this.plotArea,this.x(pointsX[i]), yVal, r + , styleClass); + if (i === 0) this.pointSelected = pointElement; + + pointElement.addEventListener('mouseover', e => { + this.tooltip = svg.addText(this.plotArea, e.target.getBBox().x+10, + e.target.getBBox().y-10, 'Calc '+calcIds[i], 'middle', 'tooltip'); + }); + pointElement.addEventListener('mouseout', e => { + svg.removeElement(this.tooltip); + }); + pointElement.addEventListener('click', e => { + //console.log('ID',calcIds[i]); + this.clickPointListener(calcIds[i]); }); - */ + this.calcPointMap.set(calcIds[i], pointElement); } + } - - updateSelection( leafIds /*Set*/){ - console.log('ElasticDetails updateSelection ',leafIds); - - } + selectCalc(calcId){ + this.pointSelected.setAttribute('class', 'eos-viewer'); + this.pointSelected.setAttribute('r', 3); + this.pointSelected = this.calcPointMap.get(calcId); + this.pointSelected.setAttribute('class', 'eos-viewer-sel'); + this.pointSelected.setAttribute('r', 6); + } - updateMarkedLeaf(leafId){ + setClickPointListener(listener){ + this.clickPointListener = listener; + } - /* Do nothing for now... - this.markedCalc = leafId; - let rowElement= this.element.querySelector('.data-row-marked'); - if (rowElement !== null) rowElement.className= 'data-row'; - if (this.markedCalc !== null){ - let rowElement1= document.querySelector('tr[data-calc-id="'+this.markedCalc+'"]'); - if (rowElement1 !== null) rowElement1.className= 'data-row-marked'; - } - */ - } + x(x){ + return this.xRel*(x - this.xMin); + } - } + y(y){ + return -this.yRel*(y - this.yMin); + } - // EXPORTS - module.exports = ElasticConstDetails; +} -/***/ }, -/* 32 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the Search Module of the application. - It's a container UI component that shows the seach part of the application: - front search interface and search results page. - */ - - "use strict"; - - let util = __webpack_require__(1); - let UserGuidance = __webpack_require__(33); - let ElementTable = __webpack_require__(34); - let MaterialList = __webpack_require__(35); - //let PropertiesBox = require('./PropertiesBox.view.js'); - let FilterPanel = __webpack_require__(36); - let SwitchComponent = __webpack_require__(13); - - - - // local utility functions - function getTagHtml(tag, isFormula){ - return `<span class="search-label" data-el="${tag}" > - <img src="img/${isFormula ? 'info' : 'tag'}.svg" height="16px" class="remove-label" - style="vertical-align: bottom"/> - ${isFormula ? util.getSubscriptedFormula(tag) : tag} - <img src="img/cross.svg" height="6px" class="remove-label" - style="vertical-align: middle; padding: 4px 3px 6px 5px;" /> - </span>`; - } +// EXPORTS +module.exports = EquationOfStateViewer; - function replaceDashes(s){ - return s.split('-').join('_'); - } + +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + 'Details' view container that shows all the material info related to + its electronic structure. + + This container is quite complex. + + In the file there are two defined (classes) components used in the container: + - SummaryByFunctionalsComponent: the component (left panel, below part) + showing a band gap summary info by functional. + - BZViewerWrapper: This component is a wrapper for the Brillouin zone viewer + */ + + + +let DetailsViewBase = __webpack_require__(6); +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let CalcSelectorBar = __webpack_require__(13); +let StatsViewer = __webpack_require__(14); +let BSDOSPlotter = __webpack_require__(26); +let PlotterBase = __webpack_require__(5); +let plotter= new PlotterBase(); +let DataStore = __webpack_require__(1); +let LoadingPopup = __webpack_require__(4); + +function setFermiVizContent(fermiBox,url){ + let sceneContent; + if (url === ''){ + fermiBox.innerHTML= '';//fermiBox.removeChild(plotter.canvas); + plotter.attach(fermiBox,undefined,316); + plotter.setNoData(); + } + else{ + sceneContent= '<inline url="'+url+'"> </inline>'; + fermiBox.innerHTML= + `<x3d id="x3dframe" width='290px' height='290px' > + <scene>${sceneContent} </scene> + </x3d>`; + x3dom.reload(); + } + +} + +class ElectronicStructDetails extends DetailsViewBase{ + + constructor() { + super('Electronic Structure'); + this.firstId; + this.lastId; + this.element.innerHTML+=` + <div style="float: left; width: 30%;"> + <div class="view-box"> + <div class="title">Calculations </div> + <div class="navTreeWrapper"></div> + + + <div class="summary-title">Summary</div> + <div class="footer summary-box" style="border-top: 0"> + </div> + </div> + </div> + + <div style="float: right; width: 70%;"> + <div class="view-box e-structure-box"> + + <div class="title">Electronic structure</div> + + <div style="padding-top: 10px;"> + <div class="calc-selector-host"></div> + </div> + + + <div> + + <div style="padding: 30px 100px 20px 100px; "> + <div class="info-fields-label" style="float: left; width: 54%; "> + <span info-sys-data="band-structure">Band structure</span> + </div> + <div class="info-fields-label" style="float: left;"> + <span info-sys-data="DOS">DOS</span> + </div> + <div style="clear: both;"></div> + + <div class="calc-bs-dos-plotter" > + </div> + + <div> + <div class="band-gap-field" style="float: left; width: 56%; text-align: right"> + <b><span info-sys-data="band-gap">Band gap</span></b>: + <span class="band-gap-value" ></span> + </div> + <div style="clear: both;"></div> + + </div> + </div> + + <div class="spin-legend" style="font-size: 0.9em; padding: 0 30px 10px; display: none"> + <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg> + Spin <span style='font-size: 1.1em'>⇧</span> + <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg> + Spin <span style='font-size: 1.1em'>⇩</span> + </div> + + </div> + + <div class="footer lower-section"> + + <div style="float: left"> + <div style="padding: 16px; "> + <div class="info-fields-label" > + <span info-sys-data="brillouin-zone-viewer">Brillouin zone</span> + </div> + <div class="bz-viewer-wrapper" style="width: 400px; height: 400px"> + </div> + </div> + </div> + + <div class="band" style="float: right; width: 40%;"> + <div style="padding: 16px; "> + <div class="info-fields-label" > + <!-- <span info-sys-data="fermi-surface">Fermi surface </span> --> + </div> + <div class="fermi-box" > </div> + </div> + </div> + + <div style="clear: both;"></div> + + </div> <!-- footer --> + + </div> + + </div> <!-- view-box --> + `; + + this.navTreeWrapper = + this.element.getElementsByClassName("navTreeWrapper")[0]; + + //this.leafTitle = this.element.querySelector('.tree-leaf-title'); + this.calcSelector = new CalcSelectorBar('calc-selector-bar','70%'); + this.element.querySelector('.calc-selector-host'). + appendChild(this.calcSelector.element); + + this.rightBox = this.element.querySelector('.e-structure-box'); + + this.summaryByFunctionals = null; + + this.bsDosPlotter = new BSDOSPlotter(); + + this.bandGapField = this.element.querySelector('.band-gap-field'); + this.bandGapValue = this.element.querySelector('.band-gap-value'); + + this.spinLegend = this.element.querySelector('.spin-legend'); + + this.lowerSection = this.element.querySelector('.lower-section'); + + this.fermiBox= this.element.getElementsByClassName('fermi-box')[0]; + // Load the x3dom library + util.loadLib("lib/x3dom.js"); + + this.bzViewerWrapper = this.element.querySelector('.bz-viewer-wrapper'); + this.bzViewerWrapper = new BZViewerWrapper(this.bzViewerWrapper); + + // For static ones + InfoSys.addToInfoSystem(this.element); + + this._events(); + } + + _events() { + //super._events(); + + this.calcSelector.setPrevListener(e => { + if (this.groupIndex > 0){ + let calcId = this.groupCalcs[--this.groupIndex]; + this._loadGraphData(calcId); + this._updateSelectorState(calcId); + return this.groupIndex === 0; // the first + } + }); + + this.calcSelector.setNextListener( e => { + if (this.groupIndex < this.groupCalcs.length-1){ + let calcId = this.groupCalcs[++this.groupIndex]; + this._loadGraphData(calcId); + this._updateSelectorState(calcId); + return this.groupIndex === this.groupCalcs.length-1; // the last + } + }); + } + + + updateSelection(leafIds/*Set*/){ + //console.log('E-StructureDetails UPDATING calcs: ',calcs.values().next().value); + if (leafIds.size > 0){ + this.rightBox.style.visibility = 'visible'; + //console.log('E-StructureDetails UPDATING calcs:leafIds.size > 0 ',leafIds); + let calcMapByFunctional = getCalcMapByFunctional(leafIds); + if (this.summaryByFunctionals === null) + this.summaryByFunctionals = + new SummaryByFunctionalsComponent(calcMapByFunctional, + this.element.querySelector('.summary-box')); + else + this.summaryByFunctionals.build(calcMapByFunctional); + // let bandGapDirect= calcs.values().next().value.band_gap_direct; + //let bandGapType= (bandGapDirect ? "direct" : "indirect"); + + let counter= 0; + leafIds.forEach( calcId => { + counter++; + if (counter === 1) this.firstId = calcId; + if (counter === leafIds.size) this.lastId = calcId; + }); + }else + this.rightBox.style.visibility = 'hidden'; + + function getCalcMapByFunctional(leafIds/*Set*/){ + let functCalcMap = new Map(); + leafIds.forEach( leafId => { + let calc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); + if (calc.has_band_structure){ + //console.log('CALC BS ',calc.band_gap,calc.band_gap_direct); + if (functCalcMap.has(calc.functional_type)){ + functCalcMap.get(calc.functional_type).add(calc); + }else{ // New functional + let newFunctionalArray = new Set(); + newFunctionalArray.add(calc); + functCalcMap.set(calc.functional_type, newFunctionalArray); + } + } + }); + return functCalcMap; + } + + } + + + + updateMarkedLeaf(leafId){ + //console.log('updateMarkedCalc '+calc); + this.groupCalcs = null; // if the leafId is not a group else this.groupCalcs is array + if (leafId === null){ + //this.leafTitle.innerHTML = 'NO SELECTION'; + this.calcSelector.setState('NO SELECTION', true, true); + return; + + }else{ + if (DataStore.getGroups().has(leafId)){ + //this.leafTitle.innerHTML = leafId+' ('+DataStore.getGroups().get(leafId).calcs.size+')'; + this.groupCalcs = Array.from(DataStore.getGroups().get(leafId).calcs); + let calcId = DataStore.getCalcReprIntId(leafId); + this.groupIndex = this.groupCalcs.indexOf(calcId); + this._updateSelectorState(calcId); + }else + //this.leafTitle.innerHTML = leafId; + this.calcSelector.setState(leafId, true, true); + } + + this._loadGraphData(DataStore.getCalcReprIntId(leafId)); + + // calc.fermi_surface + + } + + + _updateSelectorState(leafId){ + let t = leafId+' ('+(this.groupIndex+1)+'/'+this.groupCalcs.length+')'; + this.calcSelector.setState(t, this.groupIndex === 0, + this.groupIndex === this.groupCalcs.length-1); + } + + + _loadGraphData(calcId){ + + let calc = DataStore.getCalc(calcId); + + if (!this.bsDosPlotter.isAttached()) + this.bsDosPlotter.attach(this.element.querySelector('.calc-bs-dos-plotter') + ,undefined,360); + + if (calc === null || (!calc.has_band_structure && !calc.has_dos)){ + this.bsDosPlotter.setNoData(); + this.bzViewerWrapper.setNoData(); + this.bandGapField.style.display = 'none'; + this.lowerSection.style.display = 'none'; + + }else{ + LoadingPopup.show(); + let matId = DataStore.getMaterialData().id; + + util.serverReq(util.getMaterialCalcURL(matId, calc.id, 'dos'), e1 => { + let dosData = JSON.parse(e1.target.response).dos; + + util.serverReq(util.getMaterialCalcURL(matId, calc.id, 'band_structure'), + e2 => { + let bsData= JSON.parse(e2.target.response).band_structure; + if (bothSpins(bsData, dosData)) + this.spinLegend.style.display = 'block'; + + //***util.addBandGapData(calcData, bsData); + //console.log('CODE NAME:', calc.code_name); + this.bsDosPlotter.setUpAndData(bsData, dosData, calc.code_name ); + + if (calc.has_band_structure){ + + this.bandGapField.style.display = 'block'; + this.bandGapValue.textContent= util.J2eV(calc.band_gap, 2)+' eV '; + // console.log('calc.brillouin_zone_json',calc.brillouin_zone_json); + if (calc.brillouin_zone_json !== null){ + this.lowerSection.style.display = 'block'; + this.bzViewerWrapper.setCalcData(calc.brillouin_zone_json, bsData.segments); + }else{ + this.lowerSection.style.display = 'none'; + this.bzViewerWrapper.setNoData(); + } + + //this.bzViewerWrapper.setCalcData(calc.brillouin_zone_json, bsData.segments); + // if there bandstruc data && calc.band_gap === 0 => show FermiSurface + //if (calc.band_gap === 0) + //setFermiVizContent(this.fermiBox, (calc === null ? '' : util.FERMI_SURFACE_URL)); + }else{ + this.lowerSection.style.display = 'none'; + this.bandGapField.style.display = 'none'; + this.bzViewerWrapper.setNoData(); + } + LoadingPopup.hide(); + }); + }); + + } + + function bothSpins(bsData, dosData){ + if (bsData !== undefined){ + if (bsData.segments[0].band_energies.length === 2) return true; + } + if (dosData !== undefined){ + if (dosData.dos_values.length === 2) return true; + } + return false; + } + } + + + setPrevCalcListener(listener){ + this.prevCalcListener= listener; + } + + + setNextCalcListener(listener){ + this.nextCalcListener= listener; + } + +} + + + +class SummaryByFunctionalsComponent{ + + constructor(calcMapByFunctional, hostElement){ + this.calcMapByFunctional = calcMapByFunctional; + this.hostElement = hostElement; + this.viewType = 'text'; + this.functional = null; + this.hostElement.innerHTML+=` + <div style="float: left" > + <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab" + viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;"> + <rect x="0" y="0" width="2" height="15" /> + <rect x="3" y="5" width="1.8" height="7" /> + <rect x="6" y="3" width="1.8" height="9" /> + <rect x="9" y="6" width="1.8" height="6" /> + <rect x="12" y="2" width="1.8" height="10" /> + <rect x="2" y="13" width="13" height="2" /> + </svg> + <svg xmlns="http://www.w3.org/2000/svg" class="text-tab" + viewBox="0 0 15 15" width="15" height="15" style="fill: #777;"> + <rect x="0" y="1" width="15" height="2.5" /> + <rect x="0" y="6" width="15" height="2.5" /> + <rect x="0" y="11" width="15" height="2.5" /> + </svg> + </div> + + <div class="functional-tabs" style="float: right"> + </div> + + <div style="clear: both;"></div> + + <div class="content-placeholder" > + + <div style="display: block" class="text-panel" > + + <div><b><span info-sys-data="band gap">Band gap</span></b> (eV): + <div class="stats-fields summary-bandgap-field" > </div> + </div> + </div> + + <div style="display:none" class="chart-panel" > + <div class="charts-placeholder" > </div> + </div> + + </div> + `; + this.chartTab = this.hostElement.querySelector('.chart-tab'); + this.textTab = this.hostElement.querySelector('.text-tab'); + this.functionalTabs = this.hostElement.querySelector('.functional-tabs'); + this.chartPanel = this.hostElement.querySelector('.chart-panel'); + this.textPanel = this.hostElement.querySelector('.text-panel'); + 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.build(calcMapByFunctional); + + this.chartTab.addEventListener( "click", e => { + this.chartTab.style.fill = '#777'; + this.viewType = 'chart'; + this.textTab.style.fill = '#c7c7c7'; + this.chartPanel.style.display = 'block'; + this.textPanel.style.display = 'none'; + }); + + this.textTab.addEventListener( "click", e => { + this.textTab.style.fill = '#777'; + this.viewType = 'text'; + this.chartTab.style.fill = '#c7c7c7'; + this.textPanel.style.display = 'block'; + this.chartPanel.style.display = 'none'; + }); + + this.functionalTabs.addEventListener( "click", e => { + if (e.target.className === 'tab'){ + this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') + .className = 'tab'; + this.functional = e.target.getAttribute('data-tab'); + this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]') + .className = 'tab-selected'; + this._setData(); + } + }); + + } + + _setData(){ + let stats = this.functionalBandGapMap.get(this.functional); + // Set text data + + this.bandgapField.innerHTML = stats.html; + + // Set Charts data + this.statsViewer.clear(); + this.statsViewer.drawPoints(stats.data, stats.label, stats.min, stats.max); + } + + + + build(calcMapByFunctional){ + + if (calcMapByFunctional.size === 0){ + this.hostElement.style.display = 'none'; + this.hostElement.previousElementSibling.style.display = 'none'; + }else{ + this.hostElement.style.display = 'block'; + this.hostElement.previousElementSibling.style.display = 'block'; + } + + this.unfoldedElement = null; + this.functionalTabs.innerHTML = ''; + this.functionalBandGapMap = new Map(); + //console.log('calcMapByFunctional',calcMapByFunctional, this.hostElement); + calcMapByFunctional.forEach( (calcs, functionalName) =>{ + //let statsMap = util.getQuantityStatsMap(calcs); + let array= []; + calcs.forEach( calc => { + array.push(calc.band_gap/1.602176565e-19); + }); + 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); + stats.label = 'band gap'; + stats.html = util.getAverage(stats.data).toFixed(2)+ + ' <span style="font-size: 0.9em">['+stats.min.toFixed(2) + +' , '+stats.max.toFixed(2)+']</span>'; + + this.functionalBandGapMap.set(functionalName, stats); + + let tabClass= 'tab'; + if (this.functional === null) this.functional = functionalName; + if (this.functional === functionalName){ + this._setData(); + tabClass= 'tab-selected'; + } + + this.functionalTabs.innerHTML += + '<span class="'+tabClass+'" data-tab="'+functionalName+'">'+functionalName+'</span>'; + }); + + InfoSys.addToInfoSystem(this.hostElement); + } + +} + + + +class BZViewerWrapper{ + + constructor(hostElement) { + this.hostElement = hostElement; + this.bzViewer = null; + } + + + setCalcData(bzData, bsData){ + if (this.bzViewer === null) + this.bzViewer = new BrillouinZoneViewer(this.hostElement); + this.bzViewer.load(this._getBZDataForViewer(bzData,bsData)); + this.hostElement.style.visibility = 'visible'; + } + + + setNoData(){ + this.hostElement.style.visibility = 'hidden'; + } + _getBZDataForViewer(bz_json, bs_json){ + let labels = []; + let kPoints = []; + bs_json.forEach( segment => { + labels.push(segment.band_segm_labels); + kPoints.push(segment.band_k_points); + }); - class NewSearchMod { + let data = { + vertices: bz_json.vertices, + faces: bz_json.faces, + basis: bz_json.basis, + labels: labels, + segments: kPoints + } + return data; + } - constructor() { +} - this.userGuidance = true; // can enabled/disabled - //this.showingSearchBox = false; - this.searchQuery = []; - this.queryTypes = []; //**** Types associated to query elements - // Types: element (E), formula (F), symbol (S) and prop names - this.searchFilters = []; +// EXPORTS +module.exports = ElectronicStructDetails; - this.currentOperator = 'AND'; - this.element = document.createElement('div'); - this.element.setAttribute("id",'search-module'); - this.element.innerHTML= - ` - <div class="search-filter-side"> +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { + + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + Implements a graphical UI component composed of a DOS plotter and a + Band Structure plotter + */ - <div>Filters</div> - <!-- <div id="filter-panel-placeholder"> </div> --> +let BSPlotter = __webpack_require__(7); +let DOSPlotter = __webpack_require__(11); +let svg = __webpack_require__(2); - </div> - <div class="search-main-side"> +class BSDOSPlotter{ - <div class="search-box" > - <div class="search-query-wrapper" > - <div class="search-query-box" style="float: left;"> - </div> - <button class="clean-btn" style="float: right;">Clear all</button> - </div> + constructor() { + this.element = document.createElement('div'); + 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.dosYAxisLabeled = false; + } - <button class="search-btn" >Search</button> - </div> + attach(element, width, height){ + element.appendChild(this.element); + //this.bsPlotter.attach(this.element, element.clientWidth/2 + 200 -20/*padding*/, height); + //this.dosPlotter.attach(this.element, element.clientWidth/2 - 200 -20/*padding*/, height); + this.bsPlotter.attach(this.element, height, height); + this.height = height; + this.dosPlotter.attach(this.element, this.height/2+20, height); + this.parentElement= element; + } - <input type="checkbox" id="multiples-of-formula" value=""> - Include multiples of formula - <br> - <input type="checkbox" id="allow-other-elements" value=""> - Allow other elements - <br> + isAttached(){ + return this.parentElement !== null; + } - <div class="add-buttons" > - <div class="tab-buttons" style="width: 70%; display: inline-block"> - <button class="element-add-btn" id="add-tab-selected">Element</button> - <button class="formula-add-btn" style="padding: 10px 20px;" >Formula</button> - <button class="name-add-btn" >Name</button> - </div> - <div class="bool-buttons" style="width: 28%; display: inline-block" > - OR <span id="and-or-switch" ></span> AND - <button >NOT</button> - <button class="open-parentheses" >(</button> - <button class="close-parentheses">)</button> - <!--<input type="checkbox" name="and-or" class="not-symbol-btn" />NOT--> - </div> - </div> + setUpAndData(dispData, dosData, codeName){ + this.hasDispData = (dispData !== undefined && dispData !== null); + this.hasDosData = (dosData !== undefined && dosData !== null); - <div class="add-box"> - <div> - <div class="triangle element-tri" style="margin: -10px 64px; visibility: visible"></div> - <div class="triangle formula-tri" style="margin: -10px 30px; visibility: hidden"></div> - <div class="triangle name-tri" style="margin: -10px 70px; visibility: hidden"></div> - </div> + // Create a new DOS graph with/without left axis and labels + let newDosYAxisLabeled; + if (this.hasDosData && !this.hasDispData) newDosYAxisLabeled = true; + else newDosYAxisLabeled = false; - <div class="add-panel"> - </div> - </div> + 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 width = this.height/2 + newLeftMargin; + this.dosPlotter.attach(this.element, width, this.height); + } + this.dosYAxisLabeled = newDosYAxisLabeled; - <div class="results-panel" > <!-- style="display: none"--> - </div> + if (this.hasDispData){ + this.bsPlotter.setBandStructureData(dispData); + if (this.hasDosData) + this.bsPlotter.setRepaintListener( (yZoom, yOffset) => { + this.dosPlotter.setYZoomAndOffset(yZoom, yOffset); + this.dosPlotter.repaint(); + }); + }else + this.bsPlotter.setNoData(); - </div> <!-- search-main-side --> - `; + if (this.hasDosData){ - this.filterSidePanel = this.element.querySelector('.search-filter-side'); - this.searchQueryBox= this.element.getElementsByClassName("search-query-box")[0]; - this.searchBox = this.element.querySelector('.search-box'); + this.dosPlotter.setPoints(dosData, codeName); - this.mainButton= this.element.querySelector('.search-btn'); - this.cleanButton= this.element.querySelector('.clean-btn'); - this.addButtonsBox= this.element.querySelector('.add-buttons'); - this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn'); - this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn'); - this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn'); + if (this.hasDispData){ + this.bsPlotter.setExternalYAxisMax(this.dosPlotter.getYAxisMax()); - this.addPanel= this.element.querySelector('.add-panel'); + this.dosPlotter.setRepaintListener( (yZoom, yOffset) => { + this.bsPlotter.setYZoomAndOffset(yZoom, yOffset); + this.bsPlotter.repaint(); + }); + // Remove y axis label + this.dosPlotter.svg.removeChild(this.dosPlotter.yLabelText); + this.dosPlotter.yLabelText = null; + } + + }else + this.dosPlotter.setNoData(); - let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch'); - this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element); - andOrSwitch.setListener( e => { - this.currentOperator = ( e ? 'AND' : 'OR'); - }); + this.dosPlotter.setYAxisLabelsVisibility(newDosYAxisLabeled); + } - this.openParenthButton = this.element.querySelector('.open-parentheses'); - this.closeParenthButton = this.element.querySelector('.close-parentheses'); - this.openParenthButton.addEventListener( 'click', e => { - this._addParenthesesInSearchQuery(true); - }); - this.closeParenthButton.addEventListener( 'click', e => { - this._addParenthesesInSearchQuery(false); - }); - this.elementTable= new ElementTable(); - this.elementTable.setClickListener(elementArray => { - this.addElementsInSearchQuery(elementArray); - this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code - }); - this.elementTable.setDeselectListener(e => this.removeElementORFormulaInSearchQuery(e)); + setNoData(){ + this.bsPlotter.setNoData(); + this.dosPlotter.setNoData(); + } +} - this.formulaBox = new FormulaBox(); - this.formulaBox.setAddFormulaListener(formula => { - if (formula.trim() !== ''){ - this.addTagInSearchQuery(formula, 'F'); - //this.formulaBox.disable(true); - this.addMatNameButton.disabled = true; - this.materialNameBox.disable(true); - } - }); +// EXPORTS +module.exports = BSDOSPlotter; - this.materialNameBox = new MaterialNameBox(); - this.materialNameBox.setAddMaterialNameListener( name => { - if (name.trim() !== ''){ - this.addTagInSearchQuery(name, 'MN'); - this.addElementButton.disabled = true; - this.addFormulaButton.disabled = true; - this.formulaBox.disable(true); - this.materialNameBox.disable(true); - } - }); +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + 'Details' view container that shows a table with the methodology info used + to get the material calculations. + + In the file there is a defined (class) component used as a filter widget for + the table: FilterInGroupsComponent. + */ + + + +let DetailsViewBase = __webpack_require__(6); +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let DataStore = __webpack_require__(1); +let SortingButton = __webpack_require__(28); + + +const REPOSITORY_LINK = + 'https://repository.nomad-coe.eu/NomadRepository-1.1/views/calculation.zul?pid='; + +const calcTypesMap = new Map([ + ['single point', 'Single point'], + ['GW calculation', 'GW'], + ['geometry optimization', 'Geometry optimization'], + ['molecular dynamics', 'Molecular dynamics'], + ['phonon calculation', 'Phonon'], + ['equation of state', 'Equation of state'], + ['parameter variation', 'Parameter variation'],//['Convergence', 'convergence'], + ['QHA calculation', 'QHA'] +]); + +const densityFunctionalMap = new Map([ + ['LDA', 'LDA'], + ['GGA', 'GGA'], + ['meta-GGA', 'meta-GGA'], + ['hybrid-GGA', 'hybrid-GGA'], + ['meta-hybrid-GGA', 'meta-hybrid-GGA'], + ['HF', 'HF'] +]); + +const codeMap = new Map([ + ['exciting', 'exciting'], + ['VASP', 'VASP'], + ['FHI-aims', 'FHI-aims'] +]); + +const potentialMap = new Map([ + ['full all electron', 'Full potential' ], + ['pseudopotential', 'Pseudo potential' ] +]); + +const basicSetMap = new Map([ + ['numeric AOs', 'Numeric AOs' ], + ['gaussians', 'Gaussians' ], + ['(L)APW+lo', '(L)APW+lo'], + ['plane waves', 'Plane waves'] +]); + + + +class FilterInGroupsComponent { + + constructor(className) { + this.element = document.createElement('div'); + this.element.className = className; + this.filtersOn = []; + this.folded = true; + this.element.innerHTML+=` + <div> + <div style="display: flex; justify-content: flex-end;"> + <div class="filter-groups-c-folded" > + <span style="vertical-align: top;">Filtering </span> + </div> + <div class="filter-c-btn" > + <img src="${util.IMAGE_DIR}folded.png" /> + <!--<button class="on">filter</button> --> + </div> + </div> + <div class="filter-groups-c-unfolded" style="display: none"> + <table style="width: 100%"> + <thead> + <tr> + <th style="width: 13%;"> </th> + <th style="width: 17%;"> + <span>Type</span> + </th> + <th style="width: 19%;"> + <span info-sys-data="functional-type">Density functional</span> + </th> + <th style="width: 13%;"> + <span info-sys-data="code-name">Code</span> + </th> + <th style="width: 17%;"> + <span info-sys-data="pseudopotential-type">Potential</span> + </th> + <th style="width: 12%;"> + <span info-sys-data="basis-set-type">Basis set</span> + </th> + <th style="width: 9%;"> </th> + </tr> + </thead> + <tbody> + <tr id="filter-items-row"></tr> + </tbody> + </table> + </div> + </div> + `; + //this.foldedPanel = this.element.querySelector('.filter-groups-c-folded'); + this.unfoldedPanel = this.element.querySelector('.filter-groups-c-unfolded'); + this.filterItemsRow = this.element.querySelector('#filter-items-row'); + + this.foldBtn = this.element.querySelector('img'); + + this.foldBtn.addEventListener('click', e => { + this.folded = !this.folded; + this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' : + util.IMAGE_DIR+'unfolded.png'); + //this.foldedPanel.style.display = (this.folded ? 'block' : 'none'); + this.unfoldedPanel.style.display = (this.folded ? 'none' : 'block'); + }); + + // Add listener for checkboxes events + this.element.addEventListener('click', (e) => { + + if (e.target.tagName === 'INPUT'){ + let index = this.filtersOn.indexOf(e.target.value); + if (index >= 0) this.filtersOn.splice( index, 1 ); + else this.filtersOn.push(e.target.value); + this.itemListener(this.filtersOn); + //console.log('this.filtersOn',this.filtersOn); + } + }); + } + + + addGroupsItems(calcs){ + let lCalcTypesMap = new Map(); + let lDensityFunctionalMap = new Map(); + let lCodeMap = new Map(); + let lPotentialMap = new Map(); + let lBasicSetMap = new Map(); + 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, densityFunctionalMap.get(c.functional)); + if (!lCodeMap.has(c.code)) + lCodeMap.set(c.code, codeMap.get(c.code)); + if (!lPotentialMap.has(c.potential)) + lPotentialMap.set(c.potential, potentialMap.get(c.potential)); + if (!lBasicSetMap.has(c.basisSet)) + lBasicSetMap.set(c.basisSet, basicSetMap.get(c.basisSet)); + }); + this.filterItemsRow.innerHTML = '<td></td>'; // calculation Id column + this.filtersOn = []; + this.addGroupItems(lCalcTypesMap); + this.addGroupItems(lDensityFunctionalMap); + this.addGroupItems(lCodeMap); + this.addGroupItems(lPotentialMap); + this.addGroupItems(lBasicSetMap); + this.filterItemsRow.innerHTML += '<td></td>'; // link column + } + + + addGroupItems(groupItemsMap){ + let html = '<td> '; + groupItemsMap.forEach( (itemName, itemId) => { + this.filtersOn.push(itemId); + html += '<input type="checkbox" value="'+itemId+'" checked>'+ + '<span style="vertical-align: 20%">'+itemName+'</span> <br> '; + }); + this.filterItemsRow.innerHTML += html+ '</td>'; + } + + + setItemListener(listener){ + this.itemListener = listener; + } +} + + + +class MethodologyDetails extends DetailsViewBase { + + constructor() { + super('Methodology'); + + this.sortedCalcs = []; + this.markedCalc = null; + + this.element.innerHTML+=` + + <div> + <div class="view-box"> + <div class="title">Methodology</div> + + <div class="filter-placeholder"></div> + + <div class="dataTableWrapper"></div> + </div> + </div> + `; + + // There is no this.navTreeWrapper = this.element.querySelector('.navTreeWrapper'); + + this.dataTableWrapper = + this.element.querySelector('.dataTableWrapper'); + + this.dataTableWrapper.innerHTML+=` + <table id="methodology-data"> + <thead> + <tr> + <th style="width: 13%;"> + <span>Calculation ID</span> + <span class="sorting-button"></span> + </th> + <th style="width: 17%;"> + <span>Type</span> + <span class="sorting-button"></span> + </th> + <th style="width: 19%;"> + <span info-sys-data="functional-type">Density functional</span> + <span class="sorting-button"></span> + </th> + <th style="width: 13%;"> + <span info-sys-data="code-name">Code</span> + <span class="sorting-button"></span> + </th> + <th style="width: 17%;"> + <span info-sys-data="pseudopotential-type">Potential</span> + <span class="sorting-button"></span> + </th> + <th style="width: 12%;"> + <span info-sys-data="basis-set-type">Basis set</span> + <span class="sorting-button"></span> + </th> + <th style="width: 9%;"> + <span info-sys-data="basis-set-type">Link</span> + + </th> + </tr> + </thead> + <tbody> + </tbody> + </table> + `; + + this.tbody = this.dataTableWrapper.querySelector("tbody"); + this.moreInfoRow = document.createElement('tr'); // + this.moreInfoRow.className= 'moreinfo'; + this.moreInfoCalcId = null; + + // filtering feature initialitation + this.filterComponent = new FilterInGroupsComponent('meth-filter-component'); + this.element.querySelector('.filter-placeholder'). + appendChild(this.filterComponent.element); + + this.filterComponent.setItemListener( propsSel/*ected*/ => { + + let rows = this.tbody.querySelectorAll('tr'); + for (let i = 0; i < rows.length; ++i){ + let calcProps = rows[i].getAttribute('data-calc-props').split(','); + //console.log('FILTERING calcProps: ',calcProps); + let propsPresent = true; + calcProps.forEach( e => { + if (propsSel.indexOf(e) < 0) propsPresent = false; + }); + + if (propsPresent) rows[i].style.display = 'table-row'; + else rows[i].style.display = 'none'; + } + }); + + // row sorting feature initialitation + this.sortingButtonWrappers = + this.dataTableWrapper.querySelectorAll('.sorting-button'); + + this.sortingButtons = []; + + let sortingButtonsMap = new Map([ + [ 'id', undefined ], + [ 'type', undefined ], + [ 'functional', undefined ], + [ 'code', undefined ], + [ 'potential', undefined ], + [ 'basisSet', undefined ] ]); + + let keysIter = sortingButtonsMap.keys(); + this.sortingButtonWrappers.forEach( e => { + let field = keysIter.next().value; + let component = new SortingButton(field); + e.appendChild(component.element); + this.sortingButtons.push(component); + + component.setListener( (descendingOrder, field) => { + //console.log(descendingOrder, field); + this.sortingButtons.forEach( el => { + if (el !== component) el.init(); + }); + this._sortRowsCalcDataBy(descendingOrder, field); + this._render(); + }); + }); + + // For static ones + InfoSys.addToInfoSystem(this.element); + + this._events(); + } + + + _events() { + //super._events(); + + this.dataTableWrapper.addEventListener('click', (e) => { + + let rowElement = e.target.parentElement; + if (rowElement.className.indexOf('data-row') < 0) + rowElement = rowElement.parentElement; + + if (rowElement.className.indexOf('data-row') >= 0){ + let id= rowElement.getAttribute('data-calc-id'); + + if (this.moreInfoCalcId !== null){ // If more-info panel unfolded + this.moreInfoRow.parentElement.removeChild(this.moreInfoRow); + if (this.moreInfoCalcId === id){ + this.moreInfoCalcId = null; + return; + } + } + this.moreInfoCalcId = id; + let moreInfoCalc, calcGroupType; + this.sortedCalcs.forEach( leafId => { //console.log(leafId, id); + if (leafId === id){ + moreInfoCalc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); + calcGroupType = DataStore.getGroupType(leafId); + + } + }); + this.moreInfoRow.innerHTML= ' <td></td><td colspan="7"> '+ + getHTMLCalcType(calcGroupType, moreInfoCalc)+' </td>'; + rowElement.parentElement.insertBefore(this.moreInfoRow, rowElement.nextElementSibling); + } + + }); + + + function getHTMLCalcType(calcGroupType, calc){ + + let result = ''; + let calcType = calcGroupType; + if (calcGroupType === null ) calcType = calc.run_type; + //console.log('getHTMLCalcType',calcType); + switch (calcType){ + + case calcTypesMap.get('Single point'): + + case calcTypesMap.get('Geometry optimization'): + //console.log(calc.pseudopotential_type, calc.scf_threshold, calc.basis_set_short_name); + result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + + getValueHTML('scf threshold',calc.scf_threshold) + + getValueHTML('basis set short name',calc.basis_set_short_name) + + getSmearingHTML(calc.smearing); + break; + + case calcTypesMap.get('GW'): + result = getValueHTML('gw starting point',calc.gw_starting_point) + + getValueHTML('gw type',calc.gw_type); + break; + + case calcTypesMap.get('Equation of state'): + result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + + getValueHTML('basis set short name',calc.basis_set_short_name) + + getValueHTML('scf threshold',calc.scf_threshold) + + getSmearingHTML(calc.smearing) + + getValueHTML('k point grid description',calc.k_point_grid_description); + break; + + case calcTypesMap.get('Parameter variation'): + result = getValueHTML('pseudopotential type',calc.pseudopotential_type) + + getValueHTML('scf threshold',calc.scf_threshold); + break; + + case calcTypesMap.get('Phonon'): + result = getValueHTML('type', 'finite differences'); + break; + + } + if (result.trim() === '') return 'NO ADDITIONAL DATA'; + else return result; + } + + } + + + updateSelection( leafIds/* Not used */ ){ + + this.sortedCalcs = []; + + DataStore.getCalculations().forEach( c => { + + let calcType = c.run_type; + if (DataStore.getGroups().has(c.id)){//leafId)) + calcType = DataStore.getGroupType(c.id);//leafId); + if (calcType === 'convergence') calcType = 'parameter variation'; + //console.log('Group',DataStore.getGroupType(leafId)); + } + + this.sortedCalcs.push({ + id: c.id, + type: calcType, + functional: c.functional_type, + code: c.code_name, + potential: c.core_electron_treatment, + basisSet: c.basis_set_type + }); + }); + + this.filterComponent.addGroupsItems(this.sortedCalcs); + + this._sortRowsCalcDataBy(true, 'id'); + //console.log('sortedCalcs:', this.sortedCalcs); + + this._render(); + } + + + _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; + }); + } - this.filterPanel = new FilterPanel(); - this.filterSidePanel.appendChild(this.filterPanel.element); - this.materialList= new MaterialList(); - this.resultsPage = this.element.querySelector('.results-panel'); - this.materialList.attachAndSetEvents(this.resultsPage); + updateMarkedLeaf(leafId){ } - this.currentTab = 'element'; - this.addPanel.appendChild(this.elementTable.element); + _render(){ + let html = ''; + this.sortedCalcs.forEach( rowCalcData => { //leafId => { + //html+= getRowHtml(leafId, calc, calcType); + html+= getRowHtml(rowCalcData); + }); + this.tbody.innerHTML = html; - this._events(); - } + InfoSys.addToInfoSystem(this.tbody); + + function getRowHtml(rowCalcData/*leafId, calc, calcType*/){ + let calc = DataStore.getCalc( /*DataStore.getCalcReprIntId(*/rowCalcData.id); + let calcType = rowCalcData.type; + let calcProps = calcType+','+calc.functional_type+','+calc.code_name+ + ','+calc.core_electron_treatment+','+calc.basis_set_type; + + let repositoryLinkHtml = ''; + if (calc.calculation_pid !== null && calc.calculation_pid !== undefined) + repositoryLinkHtml = + '<a href="'+REPOSITORY_LINK+calc.calculation_pid+'" target="blank"> ' + +'<img src="img/download.svg" height="20px" /> </a>'; - _events() { - // External event - Search button press - this.mainButton.addEventListener( "click", (e) => { - //console.log("this.labels: "+JSON.stringify(this.labels)); + return ` + <tr data-calc-id="${calc.id/*leafId*/}" data-calc-props="${calcProps}" class="data-row"> + <td>${calc.id/*leafId*/}</td> + <td> + <span info-sys-data="calculation-type.value:${calcType}"> + ${calcType}</span> + </td> + <td> + <span info-sys-data="functional-type.value:${calc.functional_type}"> + ${calc.functional_type}</span> + ${getOptValue(calc.functional_long_name)} + </td> + <td> + <span info-sys-data="code-name.value:${calc.code_name}"> + ${calc.code_name}</span> + ${getOptValue(calc.code_version)} + </td> - if (this.searchQuery.lenght === 0){ - util.showUserMsg('No query'); - }else{ - let searchExpressionQuery; + <td> + <span info-sys-data="core-electron-treatment.value:${calc.core_electron_treatment}"> + ${getPotentialValue(calc.core_electron_treatment)}</span> + </td> + <td> + <span info-sys-data="basis-set-type.value:${calc.basis_set_type}"> + ${calc.basis_set_type}</span> + ${getOptValue(calc.basis_set_quality_quantifier)} + </td> - //************** Material name or complex search expression - //if (this.queryTypes[i] === 'MN'){ - //queryObj.push(this._getESSimpleMatch('material_name', item)); + <td style="padding-top: 8px;padding-bottom: 4px;">${repositoryLinkHtml} + </td> + </tr>`; + } - if (this.element.querySelector('#allow-other-elements').checked) - searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements( - this.searchQuery, this.queryTypes); - // Regular case: search containing only the elements in the search expression - else searchExpressionQuery = - this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes); + function getOptValue(value){ + if (value === undefined || value === null) return ''; + else return '('+value+')'; + } + + function getPotentialValue(value){ + if (value === 'pseudopotential') return 'pseudopotential'; + else if (value === 'full all electron') return 'full potential'; + else return value; + } + } - this.materialList.initSearch( - this._addFiltersInSearchQuery( this.filterPanel.getValues(), - searchExpressionQuery)); - //util.setBrowserHashPath('search','results'); +} - this.element.querySelector('.add-box').style.display = 'none'; - } +function getValueHTML(text,value){ + if (value === undefined || value === null) return ''; + else return '<b>'+text+'</b>: '+value+'<br>'; +} - }); +function getSmearingHTML(value){ + let values = value.substring(1,value.length-1).split(','); + return (values[0] === 'none' ? '' : '<b>smearing kind</b>: '+values[0]+' , ')+ + (values[1] === '0' ? '' : '<b>smearing width</b>: '+values[1]); +} - this.cleanButton.addEventListener( "click", (e) => { - this.searchQuery = []; - this.queryTypes = []; - this.updateSearchQuery(); - this.addFormulaButton.disabled = false; - this.addMatNameButton.disabled = false; - this.addElementButton.disabled = false; - this.formulaBox.disable(false); - this.materialNameBox.disable(false); - this.elementTable.deselectAllElements(); - }); - this.addButtonsBox.addEventListener( "click", (e) => { - if (e.target !== e.currentTarget) { // When the event source is a child - let className = e.target.className; - let index = className.indexOf('add-btn'); - - if (index > 0){ - let selectingElement; - let selectingTab = className.substring(0, index-1); - if (selectingTab === 'element') - selectingElement = this.elementTable.element; - else if (selectingTab === 'name') - selectingElement = this.materialNameBox.element; - else if (selectingTab === 'formula') - selectingElement = this.formulaBox.element; - - this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild); - - this.element.querySelector('.add-box').style.display = 'block'; - - // Change the styles of the buttons - let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn'); - this._setTabSelectedStyles(selEl, false); - this._setTabSelectedStyles(e.target, true); - - // Change the triangle - this.element.querySelector('.'+this.currentTab+'-tri').style.visibility = 'hidden'; - this.element.querySelector('.'+selectingTab+'-tri').style.visibility = 'visible'; - - this.currentTab = selectingTab; - - /* - if (this.userGuidance){ - if (selectingTab === 'element'){ - UserGuidance.showIndependentTip(7, false); - UserGuidance.showIndependentTip(3, true); - } - else if (selectingTab === 'props'){ - UserGuidance.showIndependentTip(3, false); - UserGuidance.showIndependentTip(7, true); - }else if (selectingTab === 'formula'){ - UserGuidance.showIndependentTip(3, false); - UserGuidance.showIndependentTip(7, false); - } - } - */ - - } - } - }); - this.searchQueryBox.addEventListener( "click", (e) => { - let className = e.target.className; - if (className === 'remove-label'){ - let elSymbol = e.target.parentElement.getAttribute('data-el'); - this.removeElementORFormulaInSearchQuery(elSymbol); - } - }); +// EXPORTS +module.exports = MethodologyDetails; - } + +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { - _getESQueryFromSearchQuery(searchQuery, queryTypes){ - let formulas = []; - let parFormulas = []; - let parOperator = null; - let openIndex = -1; - searchQuery.forEach( (item, i) => { - if (queryTypes[i] === 'F' || queryTypes[i] === 'E'){ - addItem( (openIndex >= 0 ? parFormulas : formulas), i, - item+(queryTypes[i] === 'E' ? '0' : '')); - - }else if ( searchQuery[i] === '(' ){ - if (i-1 >= 0 ) parOperator = searchQuery[i-1]; - openIndex = i; - } - else if ( searchQuery[i] === ')' ){ - if (parOperator === null){ // The starting ( was the first symbol of the expression - formulas = parFormulas; - }else if (parOperator === 'OR'){ // OR (... - formulas = formulas.concat(parFormulas); - }else{ // AND (... - let rFormulas = []; - formulas.forEach( formula => { - parFormulas.forEach( parFormula => rFormulas.push(formula+parFormula) ); - }); - formulas = rFormulas; - } - parFormulas = []; // reset the array formulas inside the parentheses - - openIndex = -1; - } - }); - console.log('_getESQueryFromSearchQuery: ',formulas, parFormulas); - - //*********** Get the elastic search expression from the search expression - // the elements inserted must be sorted for this to work - // queryObj.bool.must.push(this._getESSimpleMatch('atom_labels_keyword', this._sortElements(elements) )); ///elements.join(''))); - // if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search - let rootQueryObj = { 'bool' : {} }; - rootQueryObj.bool.should = []; - let queryObj = rootQueryObj.bool.should; - let reduced = this.element.querySelector('#multiples-of-formula').checked; - - formulas.forEach( formula => { - let pFormula; - let searchByElement = (formula.indexOf('0') >= 0); - if (searchByElement){ // There are some element in the search expression - /** TDO**/ - let f = formula; - if (reduced) f = this._reduceFormula(formula, false); - pFormula = this._processFormula(f, 'element-string'); - let tempQueryObj = { 'bool' : { 'must' : [] } }; - - if (pFormula[0].length > 0) // length === 0 No formula, only elements - tempQueryObj.bool.must.push( - this._getESOperatorMatch( - (reduced ? 'formula_reduced_terms' : 'formula_cell_terms'), pFormula[0])); - tempQueryObj.bool.must.push( - this._getESSimpleMatch('atom_labels_keyword', pFormula[1])); - - queryObj.push(tempQueryObj); - - }else{ // Only formulas - if ( reduced ){ // reduced search - pFormula = this._reduceFormula(formula, false); - queryObj.push(this._getESSimpleMatch('formula_reduced_keyword', pFormula)); - }else{ - pFormula = this._processFormula(formula, 'canonical-formula'); - queryObj.push(this._getESSimpleMatch('formula_cell_keyword', pFormula)); - } - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - }); + /* + Sorting button component implementation + */ - return rootQueryObj; - - function addItem(formulas, i, item){ - if (i === 0 || searchQuery[i-1] === '('){ - formulas.push(item); - }else{ - if (searchQuery[i-1] === 'OR') formulas.push(item); - else if (searchQuery[i-1] === 'AND') - formulas.push(formulas.pop()+item); - } - } - } // _getESQueryFromSearchQuery() +let util = __webpack_require__(0); +class SortingButton { - _getESQueryFromSearchQuery_otherElements(searchQuery, queryTypes){ - // Query structure analysis - looking for parentheses (only one level supported) - if ( searchQuery.indexOf('(') >= 0){ // Recursion + constructor(id) { - let openIndex = -1; - let prodQuery = []; - let prodTypes = []; - //let prodQueryIndex = 0; - for (let i = 0; i < searchQuery.length; i++) { - if ( searchQuery[i] === '(' ) openIndex = i; - else if ( searchQuery[i] === ')' ){ - prodQuery.push(this._getESQueryFromSearchQuery_otherElements( - searchQuery.slice(openIndex+1, i), queryTypes.slice(openIndex+1, i))); - prodTypes.push('Q'); - openIndex = -1; - }else if (openIndex < 0){ // outside a parentheses - prodQuery.push(searchQuery[i]); - prodTypes.push(queryTypes[i]); - } - } - //console.log('prodQuery', prodQuery, prodTypes); - return this._getESQueryFromSearchQuery_otherElements(prodQuery, prodTypes); + this.id = id; + this.ascending = true; - }else{ // BASE CASE: there is no parentheses + this.element = document.createElement('span'); + this.element.innerHTML+=` + <img src="img/sorting_init.png" width="12px" + style="margin-bottom: -1px; cursor: pointer"/> + `; + this.image = this.element.querySelector('img'); - let boolOperator; - searchQuery.forEach( (item, i) => { - if (searchQuery[i] === 'AND' || searchQuery[i] === 'OR') - boolOperator = item; - }); + if (id === 'id') + this.image.setAttribute('src','img/sorting_ascending.png'); - let rootQueryObj = { 'bool' : {} }; - let queryObj; - if (boolOperator === 'AND'){ - rootQueryObj.bool.must = []; - queryObj = rootQueryObj.bool.must; - }else{ // OR - rootQueryObj.bool.should = []; - queryObj = rootQueryObj.bool.should; - } + this.element.addEventListener('click', e => { + this.ascending = !this.ascending; + this.image.setAttribute('src', + 'img/sorting_'+(this.ascending ? 'ascending' : 'descending')+'.png'); + this.listener(this.ascending, this.id); + }); + } - searchQuery.forEach( (item, i) => { - if (queryTypes[i] === 'F'){ // Formula case - let esMatchQuery; - if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search - esMatchQuery = this._getESOperatorMatch('formula_reduced_terms', this._reduceFormula(item)); - }else - esMatchQuery = this._getESOperatorMatch('formula_cell_terms', this._processFormula(item, 'tokens')); - queryObj.push(esMatchQuery); + init(){ + this.image.setAttribute('src','img/sorting_init.png'); + } - }else if (queryTypes[i] === 'E'){ // Element case - queryObj.push(this._getESSimpleMatch('atom_labels_terms', item)); - }else if (queryTypes[i] === 'Q'){ - queryObj.push(item); - } - }); + setListener(listener){ + this.listener = listener; + } - return rootQueryObj; - } // else - } // _getESQueryFromSearchQuery_otherElements() +} +// EXPORTS +module.exports = SortingButton; +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* + 'Details' view container that shows the material calculations info related to + their vibrational and thermal properties. + */ + + + +let DetailsViewBase = __webpack_require__(6); +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let DataStore = __webpack_require__(1); +let LoadingPopup = __webpack_require__(4); + +let HeatCapPlotter = __webpack_require__(12); +let HelmholtzPlotter = __webpack_require__(30); +let PhononDispDOSPlotter = __webpack_require__(31); + + +class ThermalPropsDetails extends DetailsViewBase{ + + constructor() { + super('Thermal Properties'); + this.firstId; + this.lastId; + this.element.innerHTML+=` + <div style="float: left; width: 30%;"> + <div class="view-box"> + <div class="title">Calculations </div> + <div class="navTreeWrapper"></div> + </div> + </div> + + <div style="float: right; width: 70%;"> + <div class="view-box thermal-properties-box"> + + <div class="title">Vibrational and thermal properties</div> + + <div style="padding-top: 10px;"> + <div class="tree-leaf-title"></div> + </div> + + <div class="calc-disp-dos-plotter" style="padding: 30px 100px; "> + <div class="info-fields-label" style="float: left; width: 52%; "> + <span info-sys-data="phonon-dispersion">Phonon dispersion </span> + </div> + <div class="info-fields-label" style="float: left;"> + <span info-sys-data="phonon-DOS">Phonon DOS </span> + </div> + <div style="clear: both;"></div> + </div> + + + <div class="band" > + <div style="padding: 30px 50px; display: flex; justify-content: space-around; "> + + <div > + <div class="info-fields-label" > + <span info-sys-data="specific-heat-cv">Specific heat</span> + </div> + <div class="heat-plotter" > </div> + </div> + + <div> + <div class="info-fields-label" > + <span info-sys-data="helmholtz-free-energy">Helmholtz free energy</span> + </div> + <div class="helmholtz-plotter" > </div> + </div> + + </div> + </div> + + </div> + </div> + `; + + this.navTreeWrapper = + this.element.getElementsByClassName("navTreeWrapper")[0]; + + this.rightBox = this.element.querySelector('.thermal-properties-box'); + this.leafTitle = this.element.querySelector('.tree-leaf-title'); + + this.dispDosPlotter = new PhononDispDOSPlotter(); + this.heatPlotter= new HeatCapPlotter(); + this.helmholtzPlotter= new HelmholtzPlotter(); + + InfoSys.addToInfoSystem(this.element); + } + + + _events() { + super._events(); + } + - _addFiltersInSearchQuery(filterMap, searchExpressionQuery){ - let rootQueryObj = { 'bool' : {} }; - rootQueryObj.bool.must = []; - rootQueryObj.bool.must.push( searchExpressionQuery ); + updateSelection(leafIds/*Set*/){ + //console.log('E-StructureDetails UPDATING calcs: ',calcs.values().next().value); + if (leafIds.size > 0){ + this.rightBox.style.visibility = 'visible'; + let counter= 0; + leafIds.forEach( calcId => { + counter++; + if (counter === 1) this.firstId = calcId; + else if (counter === leafIds.size) this.lastId = calcId; + }); + }else + this.rightBox.style.visibility = 'hidden'; + //console.log('METHODOLOGY '+this.tbody.innerHTML); + } - filterMap.forEach((values/*Array*/, filterName) => { - let filterNameDef = replaceDashes(filterName); + updateMarkedLeaf(leafId){ + //console.log('updateMarkedCalc ',calc); + if (leafId === null){ + this.leafTitle.innerHTML = 'NO SELECTION'; + return; + //this.bandGapField.textContent= ''; + }else{ + if (DataStore.getGroups().has(leafId)){ + this.leafTitle.innerHTML = leafId+ + ' ('+DataStore.getGroups().get(leafId).calcs.size+')'; + }else + this.leafTitle.innerHTML = leafId; + } - if (filterName === 'mass-density' || filterName === 'band-gap'){ - //***** util.eV2J() apply? - rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) ); + let calc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId)); - }else if (filterName === 'band-gap-type'){ // special case - if ( values !== 'both') - rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct', - ( values === 'direct' ? true : false ) ) ); + if (!this.dispDosPlotter.isAttached()){ + this.dispDosPlotter.attach(this.element.querySelector('.calc-disp-dos-plotter'),undefined,360); + this.heatPlotter.attach(this.element.querySelector('.heat-plotter'),317,317); + this.helmholtzPlotter.attach(this.element.querySelector('.helmholtz-plotter'),317,317); + } + if (calc === null || (!calc.has_phonon_dos && !calc.has_phonon_dispersion + && !calc.has_thermal_properties)){ + this.dispDosPlotter.setNoData(); + this.heatPlotter.setNoData(); + this.helmholtzPlotter.setNoData(); + }else{ + LoadingPopup.show(); + let matId = DataStore.getMaterialData().id; - }else if (filterName.startsWith('has')){ // has- filters - rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) ); - - }else{ // normal case - //rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) ); - rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) ); - - //console.log(this._getESOperatorMatch(filterNameDef, values, false) ); - } - }); + util.serverReq(util.getMaterialCalcURL(matId, calc.id,'phonon_dos'), e=> { + let dosData= JSON.parse(e.target.response).phonon_dos; - return rootQueryObj; - } + util.serverReq(util.getMaterialCalcURL(matId, calc.id, + 'phonon_dispersion'), e2 => { + let dispersionData= JSON.parse(e2.target.response).phonon_dispersion; + this.dispDosPlotter.setUpAndData(dispersionData, dosData); + if (calc.has_thermal_properties){ - _getESSimpleMatch(field, value){ - return { - "match": { [field] : value } - }; - } + util.serverReq(util.getMaterialCalcURL(matId, calc.id, + 'specific_heat_cv'), e3 => { + let sHeatData= JSON.parse(e3.target.response).specific_heat_cv; + this.heatPlotter.setData(sHeatData); + }); - _getESTermsArray(field, value){ - return { - "terms": { [field] : value } - }; - } + util.serverReq(util.getMaterialCalcURL(matId, calc.id, + 'helmholtz_free_energy'), e4 => { + let helmholtzData = JSON.parse(e4.target.response).helmholtz_free_energy; + this.helmholtzPlotter.setData(helmholtzData); + }); + }else{ + this.heatPlotter.setNoData(); + this.helmholtzPlotter.setNoData(); + } - _getESOperatorMatch(field, elements, and = true){ - let elementsString = ''; - if (elements.length > 0) elementsString = elements.join(' '); + LoadingPopup.hide(); + }); + }); - return { - "match": { - [field]: { - "operator": (and ? "and" : 'or'), - "query": elementsString - } - } - }; - } + } + } - _getFieldESRange(field, valuesString){ - let data = valuesString.split(':'); - console.log('_getFieldESRange data', data); - return { - "range": { - [field]: { "gte" : parseInt(data[0]), "lte" : parseInt(data[1]) } - } - }; - } + setPrevCalcListener(listener){ + this.prevCalcListener= listener; + } + setNextCalcListener(listener){ + this.nextCalcListener= listener; + } +} - _setTabSelectedStyles(element, value){ - /* - element.style.fontWeight = (value ? 'bold' : 'normal'); - element.style.color = (value ? '#E56400' : '#777'); - element.style.borderColor = (value ? '#E56400' : '#777'); - */ - element.id = (value ? 'add-tab-selected' : ''); - } +// EXPORTS +module.exports = ThermalPropsDetails; - /* - _showSearchBox(){ - if (!this.showingSearchBox){ - this.showingSearchBox = true; - this.searchLine.style.visibility = 'visible'; +/***/ }), +/* 30 */ +/***/ (function(module, exports, __webpack_require__) { - if (this.userGuidance) UserGuidance.setFinal(); - } - }*/ +"use strict"; - _addItemInSearchQuery(item, type){ - this.searchQuery.push(item); - this.queryTypes.push(type); - } +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - addTagInSearchQuery(tag, type){ - // If the it's an element and is already in the query it's not inserted - if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return; + /* + Helmholtz free energy plotter implementation + */ - if ( this.searchQuery.length > 0 - && this.searchQuery[this.searchQuery.length-1] !== '(') - this._addItemInSearchQuery(this.currentOperator, 'S'); - this._addItemInSearchQuery(tag, type); - this.updateSearchQuery(); - //this._showSearchBox(); - } - addElementsInSearchQuery(elementArray){ - let index = elementArray.length; - while (index--) { - this.addTagInSearchQuery(elementArray[index], 'E'); - } - return true; - } +let svg = __webpack_require__(2); +let PlotterBase = __webpack_require__(5); - _addParenthesesInSearchQuery(isOpen){ +class HelmholtzPlotter extends PlotterBase{ - if ( this.searchQuery.length > 0 && isOpen) - this._addItemInSearchQuery(this.currentOperator, 'S'); - this._addItemInSearchQuery( (isOpen ? '(' : ')'), 'P'); - this.updateSearchQuery(); - //this._showSearchBox(); - } + constructor() { + super({left: 60, right: 16, top: 10, bottom: 32}); + this.tooltip; + } - removeElementORFormulaInSearchQuery(item){ - //console.log(" removeElementORFormulaInSearchQuery item: ",item, this.searchQuery.indexOf(item)); - - // Travese the array removing the item and the bool operator related - let itemIndex = this.searchQuery.indexOf(item); - if (itemIndex >= 0){ - let i, elementsToRemove; - if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the left - i = itemIndex; elementsToRemove = 2; - }else if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the right - i = itemIndex-1; elementsToRemove = 2; - }else{ // case: (item) - i = itemIndex; elementsToRemove = 1; - } - this.searchQuery.splice(i, elementsToRemove); - this.queryTypes.splice(i, elementsToRemove); - } - - // Travese the array removing the unnecessary parethesis (only tested for one level nested) - if ( this.searchQuery.indexOf('(') >= 0){ // Recursion - - for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed - if ( this.searchQuery[i] === '(' ){ - if ( this.searchQuery[i+1] === ')'){ // '()' case - this.searchQuery.splice(i, 2); - this.queryTypes.splice(i, 2); - }else if (searchQuery[i+2] === ')'){ // '(item)' case - this.searchQuery.splice(i, 3, this.searchQuery[i+1]); - this.queryTypes.splice(i, 3, this.queryTypes[i+1]); - } - } - } - } - - this.updateSearchQuery(); - - if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed) - this.elementTable.deselectElement(item); - if (this.queryTypes.indexOf('E') < 0){ // It's the last element - //this.addFormulaButton.disabled = false; - this.addMatNameButton.disabled = false; - } - - }else{ // It's other than element (formula, material name ¿or prop???) - this.addElementButton.disabled = false; - this.formulaBox.disable(false); - this.materialNameBox.disable(false); - // Added ¿? - this.addFormulaButton.disabled = false; - this.addMatNameButton.disabled = false; - } - - //console.log(" final searchQuery: ",this.searchQuery); - //} - - return true; - } + setData(data){ + this.clear(); + // up to 600K data is taken + let indexOf600K = data.temperature.indexOf(600)+1; + let values = data.value.slice(0, indexOf600K); + let temperatures = data.temperature.slice(0, indexOf600K); + let yMaxValue = Math.max.apply(null, values); + let yMinValue = Math.min.apply(null, values); + let yAxisMaxValue = Math.ceil(yMaxValue/1000)*1000; + let yAxisMinValue = Math.floor(yMinValue/1000)*1000; - updateSearchQuery(){ - let html= ''; - for (let i = 0; i < this.searchQuery.length; i++) { - let type = this.queryTypes[i]; + this.setRangeAndLabels('T (K)', 0, 600, + 'F (J/kg)', yAxisMinValue, yAxisMaxValue); + this.drawAxis(4, null, 0); - if (type === 'S' || type === 'P') - html+= `<span class="search-query-symbol" > ${this.searchQuery[i]} </span>`; - else - html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false)); - } - console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes); - this.searchQueryBox.innerHTML = html; - } + let polylinePoints = ''; + temperatures.forEach( (t, i) => { + let y = values[i];///1e-25; + polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(y - this.yMin); + }); + svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1'); + } - ///********* DELETE? - showSearchResults(){ - /* - this.searchPage.style.display= 'none'; - this.resultsPage.style.display= 'block'; - */ - //if (this.userGuidance) UserGuidance.show(false); - } +} - showSearchPage(){ - /* - this.searchPage.style.display= 'block'; - this.resultsPage.style.display= 'none'; - - if (this.userGuidance){ - setTimeout(() => { - UserGuidance.init(this.addButtonsBox, this.elementTable.element, - this.searchBox, this.propertiesBox.tabsElement); - UserGuidance.show(true, this.currentTab === 'element', - this.currentTab === 'props'); - }, 400); - } - */ - } +// EXPORTS +module.exports = HelmholtzPlotter; - _sortElements(elements){ - let numbers = []; - let sortedElements = []; - elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) ); - numbers.sort( (a, b) => a - b ); // atomic number-1 - numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) ); - //console.log('_sortElements ',numbers, elString); - return sortedElements; - } +/***/ }), +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /* + Implements a graphical UI component composed of + a phonon DOS plotter (PhononDOSPlotter class, implemented in thsi file) and a + BSPlotter (showing Phonon Dispersion) + */ + + +let BSPlotter = __webpack_require__(7); +let InteractivePlotterBase = __webpack_require__(8); +let svg = __webpack_require__(2); +let util = __webpack_require__(0); + + +class PhononDispDOSPlotter{ + + constructor() { + this.element = document.createElement('div'); + //this.element.setAttribute('id','elementable'); + this.parentElement= null; + this.dispPlotter= new BSPlotter(); + this.dispPlotter.setPhononMode(); + + this.dosPlotter= new PhononDOSPlotter(); + } + + + attach(element, width, height){ + element.appendChild(this.element); + this.dispPlotter.attach(this.element, height, height); + this.dosPlotter.attach(this.element, undefined, height); + this.parentElement= element; + } + + + isAttached(){ + return this.parentElement !== null; + } + + + setUpAndData(dispData, dosData){ + + this.hasDispData = (dispData !== undefined && dispData !== null); + this.hasDosData = (dosData !== undefined && dosData !== null); + + if (this.hasDispData){ + this.dispPlotter.setBandStructureData(dispData); + if (this.hasDosData) + this.dispPlotter.setRepaintListener( (yZoom, yOffset) => { + this.dosPlotter.setYZoomAndOffset(yZoom, yOffset); + this.dosPlotter.repaint(); + }); + }else + this.dispPlotter.setNoData(); + + if (this.hasDosData){ + this.dosPlotter.setPoints(dosData); + if (this.hasDispData) + this.dosPlotter.setRepaintListener( (yZoom, yOffset) => { + this.dispPlotter.setYZoomAndOffset(yZoom, yOffset); + this.dispPlotter.repaint(); + }); + }else + this.dosPlotter.setNoData(); + } + + setNoData(){ + this.dispPlotter.setNoData(); + this.dosPlotter.setNoData(); + } + +} + + + +class PhononDOSPlotter extends InteractivePlotterBase{ + + constructor() { + super({left: 4, right: 16, top: 0, bottom: 30}); + this.outOfRangeColorActivated = false; + } + + // detach if necessary and attach + attach(element, width, height){ + super.attach(element, height/2+this.margins.left, height); + } + + + setPoints(points){ + + this.pointsSpin1 = []; + this.pointsSpin2 = []; + this._reset(); + + let pSpin1= points.dos_values[0]; + let pSpin2 = null; + if (points.dos_values.length === 2) pSpin2 = points.dos_values[1]; + let pointsY= points.dos_energies; + let pointsXInPlotRange = []; + let pointsYInPlotRange = []; - _reduceFormula(formula, getTokens = true){ - let index = 0; - let map = new Map(); - let key; - while ( index < formula.length ){ - let el2 = formula.substring(index, index+2); - let el1 = formula.substring(index, index+1); - - if (util.ELEMENTS.indexOf(el2) >= 0){ - map.set(el2, 1); // 1 default value - index += 2; - key = el2; - //console.log('eleemnt 2chars', key); - }else if (util.ELEMENTS.indexOf(el1) >= 0){ - map.set(el1, 1); // 1 default value - index++; - key = el1; - //console.log('eleemnt 1chars', key); - }else{ // It's a number - let num = parseInt(el2); - if (num >= 10) index += 2; // 2 figures number - else index++;// 1 figure number - //console.log('number ', num, key); - map.set(key, num); - } - // console.log('FINAL LOOP', map, index); - } - - let counter = 0; - while ( !checkIfReduced(map) ){ // console.log('Reducing', map); - let div = 1; - if (isDivisibleBy(map, 2)) div = 2; - else if (isDivisibleBy(map, 3)) div = 3; - else if (isDivisibleBy(map, 5)) div = 5; - else if (isDivisibleBy(map, 7)) div = 7; - else if (isDivisibleBy(map, 11)) div = 11; - - map.forEach( (value, key) => { - map.set(key, (value/div)); - }); - //console.log('Reducing DIV', map); - counter++; - if (counter > 5) break; - } - - function checkIfReduced(formulaMap){ - let min = 100; - formulaMap.forEach( (value, key) => { - if (value < min) min = value; - }); - return min === 1; - } - - function isDivisibleBy(formulaMap, n){ - let div = true; - formulaMap.forEach( (value, key) => { - if (value % n !== 0) div = false; - }); - return div; - } - - let tokens = []; - let canonicalFormula = ''; - if (getTokens){ - map.forEach( (value, key) => tokens.push(key+value) ); - }else{ - let sortedElements = this._sortElements( Array.from( map.keys() ) ); - sortedElements.forEach( element => { - canonicalFormula += element+map.get(element); - //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element)); - }); - } - - - console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula); - return (getTokens ? tokens : canonicalFormula); - } + for (var i = 0; i < pointsY.length; i++) { + let frecuency = pointsY[i]*5.034117012222e22; + let dosSpin1 = pSpin1[i]*0.029979; + let dosSpin2; + if (pSpin2 !== null) dosSpin2 = pSpin2[i]*0.029979246; + //console.log('POINTS : ',frecuency); + pointsXInPlotRange.push(dosSpin1); + if (pSpin2 !== null) pointsXInPlotRange.push(dosSpin2); + pointsYInPlotRange.push(frecuency); + //console.log('POINTS : ',pointsX[i], energy); + this.pointsSpin1.push({x: dosSpin1, y: frecuency}); + if (pSpin2 !== null) this.pointsSpin2.push({x: dosSpin2, y: frecuency}); + } + + let maxDosVal = Math.max.apply(null, pointsXInPlotRange); + let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange); + let minEnergyVal = Math.min.apply(null, pointsYInPlotRange); + + // x axis steps generation + let t = util.generateDiagramSteps(maxDosVal); + let xSteps = t[0], exp = t[1]; + + //console.log('formattedPoints paintPointsLine : ', this.formattedPoints.length); + this.setAxisRangeAndLabels(null,0,xSteps[xSteps.length-1]/*maxDosVal*1.2*/, + null,-50,320,minEnergyVal, maxEnergyVal, 100); + + svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom, + 'DOS (states/cm⁻¹)', 'middle', 'axis-steps-big'); + + 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); + //console.log('step ',xSteps[i], stepX); + svg.addText(this.axisGroup, stepX, 13, + (i === 0 ? '0' : xSteps[i].toFixed(exp)), 'middle', 'axis-steps-smaller'); + } + + this.repaint(); + } + + + repaintData(){ + let polylinePoints = ''; + for (var i = 0; i < this.pointsSpin1.length; i++) { + polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+ + ' '+this.transformY(this.pointsSpin1[i].y); + //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1'); + + polylinePoints = ''; + for (var i = 0; i < this.pointsSpin2.length; i++) { + polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+ + ' '+this.transformY(this.pointsSpin2[i].y); + //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y); + } + svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2'); + + } +} + +// EXPORTS +module.exports = PhononDispDOSPlotter; - _processFormula(formula, type){ - let index = 0; - let map = new Map(); - let key; - while ( index < formula.length ){ - let el2 = formula.substring(index, index+2); - let el1 = formula.substring(index, index+1); - - if (util.ELEMENTS.indexOf(el2) >= 0){ - map.set(el2, 1); // 1 default value - index += 2; - key = el2; - //console.log('eleemnt 2chars', key); - }else if (util.ELEMENTS.indexOf(el1) >= 0){ - map.set(el1, 1); // 1 default value - index++; - key = el1; - //console.log('eleemnt 1chars', key); - }else{ // It's a number - let num = parseInt(el2); - if (num >= 10) index += 2; // 2 figures number - else index++;// 1 figure number - //console.log('number ', num, key); - map.set(key, num); - } - // console.log('FINAL LOOP', map, index); - } - - - - if (type === 'tokens'){ - let tokens = []; - map.forEach( (value, key) => tokens.push(key+value) ); - console.log('_processFormula RETURN: ', map, tokens); - return tokens; - }else{ - let sortedElements = this._sortElements( Array.from( map.keys() ) ); - if (type === 'canonical-formula'){ - let formulaString = ''; - sortedElements.forEach( element => { - formulaString += element+map.get(element); - }); - console.log('_processFormula RETURN: ', map, formulaString); - return formulaString; - }else{ // elements-string - let elementsString = ''; - let elementsInFormulas = []; - sortedElements.forEach( element => { - elementsString += element; - let val = map.get(element); - if (val !== 0) elementsInFormulas.push(element+val); - }); - console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]); - return [elementsInFormulas ,elementsString]; - } - } +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { - } +"use strict"; - } +/* +Pending to implement +*/ - class FormulaBox{ - constructor() { - this.element = document.createElement('div'); - this.element.setAttribute("id",'formula-box'); - this.element.innerHTML= - ` - <div style="padding-bottom: 20px;"> - <!-- - <input type="checkbox" class="allow-other-elements" value=""> - Allow other elements - <br> +let DetailsViewBase = __webpack_require__(6); +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +//let DataStore = require('./DataStore.js'); - <input type="checkbox" class="multiples-of-formula" value=""> - Include multiples of formula - <br> --> - <input type="text" class="formula-text-field" - placeholder="Add formula to the search query above" > - <button class="adding-formula-btn" disabled>Add to query</button> - </div> - `; - this.formulaTextField = this.element.querySelector('.formula-text-field'); - this.formulaButton = this.element.querySelector('.adding-formula-btn'); +class ElasticConstDetails extends DetailsViewBase { - this.formulaButton.addEventListener( "click", (e) => { - this.addFormulaListener(this.formulaTextField.value); - this.formulaTextField.value = ''; - }); + constructor() { + super('Elastic constants'); - this.formulaTextField.addEventListener( 'input', e => { - //console.log('formulaTextField input: ',this.formulaTextField.value); - this.formulaButton.disabled = (this.formulaTextField.value === ''); - }); + this.sortedLeafs = []; + this.markedCalc = null; - } + this.element.innerHTML+=` + <div style="float: left; width: 27%;"> + <div class="view-box"> + <div class="title">Calculation </div> + <div class="navTreeWrapper"></div> + </div> + </div> - setAddFormulaListener(listener) { - this.addFormulaListener= listener; - } + <div style="float: right; width: 73%;"> + <div class="view-box"> + <div class="title">Elastic constants</div> + <div>Parameters</div> + <div ></div> - disable(bool) { - this.formulaTextField.disabled = bool; - this.formulaButton.disabled = true;//bool; - } - /* - getAllowOtherElements(){ - return this.element.querySelector('.allow-other-elements').checked; - } + </div> + </div> + <div style="clear: both;"></div> + `; - getMultiplesOfFormula(){ - return this.element.querySelector('.multiples-of-formula').checked; - }*/ + this.navTreeWrapper = + this.element.querySelector('.navTreeWrapper'); - } + // For static ones + InfoSys.addToInfoSystem(this.element); - class MaterialNameBox{ - - constructor() { - this.element = document.createElement('div'); - this.element.setAttribute("id",'material-name-box'); - this.element.innerHTML= - ` <div style="padding-bottom: 20px;"> - <input type="text" class="name-text-field" - placeholder="Add a material name to the search query above" > - <button class="adding-name-btn" disabled>Add to query</button> - </div> - `; - this.textField = this.element.querySelector('.name-text-field'); - this.button = this.element.querySelector('.adding-name-btn'); - - this.button.addEventListener( "click", (e) => { - this.listener(this.textField.value); - this.textField.value = ''; - }); + this._events(); + } - this.textField.addEventListener( 'input', e => { - //console.log('formulaTextField input: ',this.formulaTextField.value); - this.button.disabled = (this.textField.value === ''); - }); - } + _events() { + //super._events(); +/* + this.dataTableWrapper.addEventListener('click', (e) => { + let rowElement = e.target.parentElement; + if (rowElement.className.indexOf('data-row') < 0) + rowElement = rowElement.parentElement; + //console.log("TABLE EVENT ",rowElement); - setAddMaterialNameListener(listener) { - this.listener = listener; - } + if (rowElement.className.indexOf('data-row') >= 0){ + let id= rowElement.getAttribute('data-calc-id'); - disable(bool) { - this.textField.disabled = bool; - this.button.disabled = true;//bool; - } - } + } - // EXPORTS - module.exports = NewSearchMod; + }); +*/ + } -/***/ }, -/* 33 */ -/***/ function(module, exports) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This file implements the user guidance system. This complex system for - showing tips to the user is specified in the project documentation - */ - - - "use strict"; - - //let util = require('util.js'); - - let body = document.querySelector('body'); - let tips = []; - let tipData = []; - let currentTip;// = 1; - let independentTips = []; // if null => showing else if 'off' not showing - let addBox, table, searchBox, propertiesTabs; - let searchBarShowing = false; - - - function createUserMsg(i, width){ - let element= document.createElement('div'); - element.setAttribute('class','user-guidance'); - element.innerHTML = '<img src="img/tip'+i+'.png" width="'+width+'px" />'; - element.style.position = 'absolute'; - element.style.display = 'none'; - body.appendChild(element); - return element; - } + updateSelection( leafIds /*Set*/){ + console.log('ElasticDetails updateSelection ',leafIds); - function showTip(tipNumber){ - if (tipNumber === 8 && !searchBarShowing) return; // Do nothing - if (tipNumber === 4 && !searchBarShowing) return; // Do nothing + } - let r = tipData[tipNumber].domTarget.getBoundingClientRect(); - //console.log("GGGGGGGGG: ",r); - tips[tipNumber].style.top = (r.top + tipData[tipNumber].top + window.pageYOffset)+'px'; - tips[tipNumber].style.left = (r.left + tipData[tipNumber].left)+'px'; - tips[tipNumber].style.display = 'block'; - } - // Init per user session - function init(addBoxP, tableP, searchBoxP, propertiesTabsP){ - addBox = addBoxP; - table = tableP; - searchBox = searchBoxP; - propertiesTabs = propertiesTabsP; - - independentTips[3] = localStorage.getItem('tip3'); - independentTips[4] = localStorage.getItem('tip4'); - independentTips[7] = localStorage.getItem('tip7'); - - createUserTips(); - let currentTip = localStorage.getItem('currentTip'); - if (currentTip === null){ // First time - updateCurrentTip(1); - }else{ - currentTip = parseInt(currentTip); - if (currentTip < 10){ // regular case - updateCurrentTip(currentTip); - }//else { currentTip === 10 the guidance has finished - } - } + updateMarkedLeaf(leafId){ +/* Do nothing for now... + this.markedCalc = leafId; + let rowElement= this.element.querySelector('.data-row-marked'); + if (rowElement !== null) rowElement.className= 'data-row'; - function createUserTips(){ - - if (tips.length === 0){ - tips[1] = createUserMsg(1, 220); - tipData[1] = {domTarget: addBox, top: -70, left: -240 }; - tips[2] = createUserMsg(2, 280); - tipData[2] = {domTarget: addBox, top: -110, left: 80 }; - tips[3] = createUserMsg(3, 180); - tipData[3] = {domTarget: table, top: 180, left: 720 }; - tips[4] = createUserMsg(4, 240); - tipData[4] = {domTarget: searchBox, top: 45, left: -250 }; - tips[5] = createUserMsg(5, 210); - tipData[5] = {domTarget: addBox, top: -130, left: 70}; - tips[6] = createUserMsg(6, 240); - tipData[6] = {domTarget: addBox, top: -100, left: 370}; - tips[7] = createUserMsg(7, 220); - tipData[7] = {domTarget: propertiesTabs, top: 160, left: -40}; - tips[8] = createUserMsg(8, 240); - tipData[8] = {domTarget: searchBox, top: 30, left: 760}; - - // Event - tips[1].addEventListener( "click", closeAndShowNext); - tips[2].addEventListener( "click", closeAndShowNext); - tips[3].addEventListener( "click", e => { closeIndependentTip(3) }); - tips[4].addEventListener( "click", e => { closeIndependentTip(4) }); - tips[5].addEventListener( "click", closeAndShowNext); - tips[6].addEventListener( "click", closeAndShowNext); - tips[7].addEventListener( "click", e => { closeIndependentTip(7) }); - tips[8].addEventListener( "click", e => { - e.target.style.display = 'none'; - updateCurrentTip(10); - }); - } - } + if (this.markedCalc !== null){ + let rowElement1= document.querySelector('tr[data-calc-id="'+this.markedCalc+'"]'); + if (rowElement1 !== null) rowElement1.className= 'data-row-marked'; + } + */ + } +} - function setFinal(){ - searchBarShowing = true; - if (currentTip < 10){ - tips[currentTip].style.display = 'none'; - updateCurrentTip(8);//currentTip = 4; - showTip(currentTip); - } - showIndependentTip(4, true); - } +// EXPORTS +module.exports = ElasticConstDetails; - function closeIndependentTip( tipNumber){ - //e.preventDefault(); - tips[tipNumber].style.display = 'none'; - localStorage.setItem('tip'+tipNumber, 'off'); - independentTips[tipNumber] = 'off'; - } +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { - function closeAndShowNext(e){ - e.preventDefault(); - //console.log("closeAndShowNext",currentTip); - e.target.style.display = 'none'; +"use strict"; - switch (currentTip) { - case 2: currentTip = 5; break; +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ - case 6: currentTip = 8; break; - default: // 1 , 5 - currentTip++; - } - updateCurrentTip(currentTip); - showTip(currentTip); - } + /* + This file implements the Search Module of the application. + It's a container UI component that shows the seach part of the application: + front search interface and search results page. + */ - function showIndependentTip(tipNumber, value){ - //console.log("showIndependentTip",tipNumber); - if (independentTips[tipNumber] === null){ // Tip has not been removed (clicked) - if (value) showTip(tipNumber); - else tips[tipNumber].style.display = 'none'; - } - } +let util = __webpack_require__(0); +let UserGuidance = __webpack_require__(15); +let ElementTable = __webpack_require__(34); +let MaterialList = __webpack_require__(35); +//let PropertiesBox = require('./PropertiesBox.view.js'); +let FilterPanel = __webpack_require__(36); +let SwitchComponent = __webpack_require__(10); - function show(value, tip3, tip7){ // Global show - the UserGuidance is shown or hidden at all - if (currentTip < 10){ // sequential tips - if (value) showTip(currentTip); - else tips[currentTip].style.display = 'none'; - } - // Independent tips - showIndependentTip(3, value && tip3); - showIndependentTip(4, value); - showIndependentTip(7, value && tip7); - } +// local utility functions +function getTagHtml(tag, isFormula){ + return `<span class="search-label" data-el="${tag}" > + <img src="img/tag.svg" height="16px" class="remove-label" + style="vertical-align: bottom"/> + ${isFormula ? util.getSubscriptedFormula(tag) : tag} + <img src="img/cross.svg" height="6px" class="remove-label" + style="vertical-align: middle; padding: 4px 3px 6px 5px;" /> + </span>`; +} - function updateCurrentTip(value){ - currentTip = value; - localStorage.setItem('currentTip', value); - //console.log('localStorage.currentTip:',localStorage.getItem('currentTip')); - } +function replaceDashes(s){ + return s.split('-').join('_'); +} +class NewSearchMod { - // EXPORTS - module.exports = {init, setFinal, show, showIndependentTip} + constructor() { + this.userGuidance = true; // can enabled/disabled -/***/ }, -/* 34 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This is the UI component implementing the interactive Element Table - in the search front. It need to communicate to other UI components - so it exposes several listeners - */ - - "use strict"; - - let util = __webpack_require__(1); - - - // constans - - const GROUPS = new Map([ - [1,['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']], - [2,['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']], - [3,['Sc', 'Y']], - [4,['Ti', 'Zr', 'Hf', 'Rf']], - [5,['V', 'Nb', 'Ta', 'Ha']], - [6,['Cr', 'Mo', 'W', 'Sg']], - [7,['Mn', 'Tc', 'Re', 'Ns']], - [8,['Fe', 'Ru', 'Os', 'Hs']], - [9,['Co', 'Rh', 'Ir', 'Mt']], - [10,['Ni', 'Pd', 'Pt', 'Ds']], - [11,['Cu', 'Ag', 'Au', 'Rg']], - [12,['Zn', 'Cd', 'Hg', 'Cn']], - [13,['B', 'Al', 'Ga', 'In', 'Tl', 'Nh']], - [14,['C', 'Si', 'Ge', 'Sn', 'Pb', 'Fl']], - [15,['N', 'P', 'As', 'Sb', 'Bi', 'Mc']], - [16,['O', 'S', 'Se', 'Te', 'Po', 'Lv']], - [17,['F', 'Cl', 'Br', 'I', 'At', 'Ts']], - [18,['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']], - [19,['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu']], - [20,['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr']] - ]); - - const BLOCKS = new Map([ - ['metalloids',['B', 'Si', 'Ge', 'As', 'Sb', 'Te', 'Po']], - ['other-non-metals',['H', 'C', 'N', 'O', 'P', 'S', 'Se']], - ['halogens',['F', 'Cl', 'Br', 'I', 'At', 'Ts']], - ['noble-gases',['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']], - ['alkali-metals',['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']], - ['alkaline-earth-metals',['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']], - ['lanthanoids',['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er' - ,'Tm','Yb','Lu']], - ['actinoids',['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm' - ,'Md','No','Lr']], - ['transition-metals', ['Sc', 'Y','Ti', 'Zr', 'Hf', 'Rf','V','Nb','Ta','Ha' - ,'Cr','Mo','W','Sg','Mn','Tc','Re','Ns','Fe','Ru','Os','Hs','Co','Rh','Ir' - , 'Mt','Ni','Pd','Pt', 'Ds', 'Cu','Ag','Au', 'Rg', 'Zn','Cd','Hg', 'Cn']], - ['post-transition-metals', ['Al','Ga', 'In', 'Tl', 'Nh', 'Sn', 'Pb', 'Fl', 'Bi', 'Mc', 'Lv']] - ]); - - const BLOCKS_COLORS = new Map([ - ['metalloids','#F9E298'], - ['other-non-metals','#F2B01D'], - ['halogens','#85ADC1'], - ['noble-gases','#F7D660'], - ['alkali-metals','#D04629'], - ['alkaline-earth-metals','#F7B57D'], - ['transition-metals', '#F58737'], - ['post-transition-metals', '#AE4747'], - ['lanthanoids','#3B91AE'], - ['actinoids','#E97147'] - ]); - - let elementNames = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium', - 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine', - 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon', - 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium', - 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium', - 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper', - 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium', - 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium', - 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium', - 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium', - 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon', - 'Cesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium', - 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium', - 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium', - 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten', - 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold', - 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium', - 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium', - 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium', - 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium', - 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium', - 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium', - 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium', - 'Livermorium', 'Tennessine', 'Oganesson' - ]; - - - // utility functions - - function getElementBlock(elSymbol){ - let block; - BLOCKS.forEach(function(value, key) { - //console.log(key + " " + value); - if (value.indexOf(elSymbol) >= 0) - block= key; - }); - return block; - } + //this.showingSearchBox = false; + this.searchQuery = []; + this.queryTypes = []; //**** Types associated to query elements + // Types: element (E), formula (F), symbol (S) and prop names + this.searchFilters = []; + this.currentOperator = 'AND'; + + this.element = document.createElement('div'); + this.element.setAttribute("id",'search-module'); + this.element.innerHTML= + ` + <div class="search-filter-side"> - let CELL_WIDTH= 36; + <div>Filters</div> + + <!-- <div id="filter-panel-placeholder"> </div> --> + + </div> + + <div class="search-main-side"> - function getCellHtml(elNum){ - let elSymbol= util.ELEMENTS[elNum-1]; - return '<td class="cell '+getElementBlock(elSymbol)+'" data-el="el-'+elSymbol+'">'+ - '<b>'+elSymbol+'</b> <div>'+elNum+'</div> </td>'; - } + <div class="search-box" > + <div class="search-query-wrapper" > + <div class="search-query-box" style="float: left;"> + </div> + <button class="clean-btn" style="float: right;">Clear all</button> + </div> + + <button class="search-btn" >Search</button> + + </div> + + <input type="checkbox" id="multiples-of-formula" value=""> + Include multiples of formula + <br> + <input type="checkbox" id="allow-other-elements" value=""> + Allow other elements + <br> + + + + <div class="add-buttons" > + <div class="tab-buttons" style="width: 70%; display: inline-block"> + <button class="element-add-btn" id="add-tab-selected">Element</button> + <button class="formula-add-btn" style="padding: 10px 20px;" >Formula</button> + <button class="name-add-btn" >Name</button> + </div> + <div class="bool-buttons" style="width: 28%; display: inline-block" > + OR <span id="and-or-switch" ></span> AND + <button >NOT</button> + <button class="open-parentheses" >(</button> + <button class="close-parentheses">)</button> + <!--<input type="checkbox" name="and-or" class="not-symbol-btn" />NOT--> + </div> + </div> - function getOddCellHtml(elNum){ - let elSymbol= util.ELEMENTS[elNum-1]; - return '<td class="cellpad '+getElementBlock(elSymbol)+'" data-el="el-X">'+ - '<b> </b> <div> </div> </td>'; - } + <div class="add-box"> + <div> + <div class="triangle element-tri" style="margin: -10px 64px; visibility: visible"></div> + <div class="triangle formula-tri" style="margin: -10px 30px; visibility: hidden"></div> + <div class="triangle name-tri" style="margin: -10px 70px; visibility: hidden"></div> + </div> - /** Group selection not implemented + <div class="add-panel"> + </div> + </div> - function getGroupSelectorHtml(num){ - return '<th class="group-sel" data-group="'+num+'"><div></div></th>'; - } + <div class="results-panel" > <!-- style="display: none"--> + </div> - function getGroupSelectorHtmlLAcAc(num){ - return '<th class="group-sel-la-ac" data-group="'+num+'"><div></div></th>'; - }*/ - - /************************/ - - - class ElemenTable{ - - constructor() { - - this.element = document.createElement('div'); - this.element.setAttribute('id','elementable'); - - // header with dropdown - let tempHtml = '<div class="element-info"></div>'; - - tempHtml+= '<div class="ptWrapper">'; - - tempHtml+= '<table id="pt-main">'; // table zone div - - /* Deactivated for the moment - // header with group selectors - tempHtml+= '<thead><tr>';//'<div id="group-selectors">'; - for (let i= 1; i<=18; i++){ - tempHtml+= getGroupSelectorHtml(i); - } - tempHtml+= '</tr></thead>';// selectors - */ - - tempHtml+= '<tbody>'; - // row 1 - tempHtml+= '<tr>'+getCellHtml(1); - tempHtml+= '<td class="cellpad" colspan="16"></td>'; - tempHtml+= getCellHtml(2)+'</tr>'; - - let get8ElementRowHtml= (initPos) => { - tempHtml+= '<tr>'+getCellHtml(initPos)+getCellHtml(initPos+1); - tempHtml+= '<td class="cellpad" colspan="10"></td>'; - for (let i= initPos+2; i< initPos+8; i++) tempHtml+= getCellHtml(i); - tempHtml+= '</tr>';//div.row - } - - // row 2 and 3 - get8ElementRowHtml(3); - get8ElementRowHtml(11); - - // row 4,5, 6 - let counter= 19; - for (let i= 0; i<4; i++){ - tempHtml+= '<tr>'; - for (let j= 0; j<18; j++){ - if (counter === 57 || counter === 89){ - tempHtml += getOddCellHtml(counter); - counter += 15; - }else{ - tempHtml+= getCellHtml(counter); - counter++; - } - } - tempHtml+= '</tr>';//div.row - } - - - tempHtml+= '</tbody></table>'; - - // Lanthanides and Actinides - tempHtml+= '<div id="specialRows"><table id="pt-laac">'; - for (let i= 0; i<2; i++){ - tempHtml+= '<tr>'; - counter = (i === 0 ? 57 : 89); - //tempHtml+= getGroupSelectorHtmlLAcAc((i === 0 ? 19 : 20)); - for (let j= 0; j<15; j++){ - tempHtml+= getCellHtml(counter); - counter++; - } - tempHtml+= '</tr>';//div.row - } - tempHtml+= '</table></div>'; //div#specialRows - - - // Block labels - tempHtml+= `<div class="legend"> - <div class="alkali-metals">Alkali metals</div> - <div class="alkaline-earth-metals">Alkaline earth metals</div> - <div class="transition-metals">Transition metals</div> - <div class="post-transition-metals">Post-transition metals</div> - <div class="metalloids">Metalloids</div> - <div class="other-non-metals">Other nonmetals</div> - <div class="halogens">Halogens</div> - <div class="noble-gases">Noble gases</div> - <div class="lanthanoids">Lanthanoids</div> - <div class="actinoids">Actinoids</div> - </div>`;//'<div id="group-selectors">'; - - - tempHtml+= '</div>'; // ptWrapper - - this.element.innerHTML= tempHtml; - this.elementInfo= this.element.getElementsByClassName('element-info')[0]; - this.tableZone= this.element.getElementsByClassName('ptWrapper')[0]; - //this.specialRows= this.element.querySelector('#specialRows'); - - this._events(); - } + </div> <!-- search-main-side --> + `; + this.filterSidePanel = this.element.querySelector('.search-filter-side'); + this.searchQueryBox= this.element.getElementsByClassName("search-query-box")[0]; + this.searchBox = this.element.querySelector('.search-box'); - _events() { - - // One listener for all diferent clicks (simple element, group) - var adhocListener= (e) => { - - if (e.target !== e.currentTarget) { // When the event source is a child - let className = e.target.className; - let element = e.target; - if (className === ''){ - element= e.target.parentElement; - className = e.target.parentElement.className; - } - - if (className.indexOf('cellpad') >= 0) return; // structural empty table cells - - if (className.indexOf('group-sel') >= 0){ // group selector cells - //*** Not working at the moment - group selection deactivated - let elements= GROUPS.get(parseInt(element.getAttribute('data-group'))); - //console.log("group-sel "+elements); - this.clickListener(elements);//let done = - //if (done) - for (var i = 0; i < elements.length; i++) - this.selectElement(elements[i]); - - }else if (className.indexOf('cell') >= 0){ // element cells - let html= element.innerHTML; - let elSymbol= html.substring(3,html.indexOf('<',3)); - if (elSymbol === ' ') return; // blank cells - //console.log("elSymbol-sel "+elSymbol); - if (className.indexOf('el-selected') >= 0){ // If selected - this.deselectListener(elSymbol);//this.deselectElement(elSymbol); - }else{ // If not selected - this.clickListener([elSymbol]);//let done = this.clickListener([elSymbol]); - this.selectElement(elSymbol);//if (done) this.selectElement(elSymbol); - } - } - } - //e.stopPropagation(); - }; - - // Event listener set in the root div element - this.tableZone.addEventListener('click',adhocListener,true); - - this.tableZone.addEventListener('mouseover',e => { - - let elSymbol= getElement(e); - //console.log("ENTERIG elSymbol-sel "+elSymbol); - if (elSymbol !== null){ - //console.log("elSymbol-sel "+elSymbol); - this.elementInfo.style.display = 'block'; - let borderColor= BLOCKS_COLORS.get(getElementBlock(elSymbol)); - this.elementInfo.style.borderColor = borderColor; - let number= util.ELEMENTS.indexOf(elSymbol)+1; - this.elementInfo.innerHTML= ` - <div> - <div style="float: right; padding: 3px 4px;border-left: 3px solid ${borderColor}; - border-bottom: 3px solid ${borderColor}" > ${number} </div> - <div style="clear: right;"></div> - </div> - <div class="symbol">${elSymbol} </div> - <div class="">${elementNames[number-1]} </div> - ` - } - }); + this.mainButton= this.element.querySelector('.search-btn'); + this.cleanButton= this.element.querySelector('.clean-btn'); + this.addButtonsBox= this.element.querySelector('.add-buttons'); + this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn'); + this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn'); + this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn'); - this.tableZone.addEventListener('mouseout',e => { - let element= getElement(e); - if (element !== null) this.elementInfo.style.display = 'none'; - }); + this.addPanel= this.element.querySelector('.add-panel'); - } + let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch'); + this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element); + andOrSwitch.setListener( e => { + this.currentOperator = ( e ? 'AND' : 'OR'); + }); + + this.openParenthButton = this.element.querySelector('.open-parentheses'); + this.closeParenthButton = this.element.querySelector('.close-parentheses'); + this.openParenthButton.addEventListener( 'click', e => { + this._addParenthesesInSearchQuery(true); + }); + this.closeParenthButton.addEventListener( 'click', e => { + this._addParenthesesInSearchQuery(false); + }); + + this.elementTable= new ElementTable(); + this.elementTable.setClickListener(elementArray => { + this.addElementsInSearchQuery(elementArray); + this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code + }); + this.elementTable.setDeselectListener(e => this.removeElementORFormulaInSearchQuery(e)); + + + this.formulaBox = new FormulaBox(); + this.formulaBox.setAddFormulaListener(formula => { + if (formula.trim() !== ''){ + this.addTagInSearchQuery(formula, 'F'); + //this.formulaBox.disable(true); + this.addMatNameButton.disabled = true; + this.materialNameBox.disable(true); + } + }); + + + this.materialNameBox = new MaterialNameBox(); + this.materialNameBox.setAddMaterialNameListener( name => { + if (name.trim() !== ''){ + this.addTagInSearchQuery(name, 'MN'); + this.addElementButton.disabled = true; + this.addFormulaButton.disabled = true; + this.formulaBox.disable(true); + this.materialNameBox.disable(true); + } + }); + + + this.filterPanel = new FilterPanel(); + this.filterSidePanel.appendChild(this.filterPanel.element); + + this.materialList= new MaterialList(); + this.resultsPage = this.element.querySelector('.results-panel'); + this.materialList.attachAndSetEvents(this.resultsPage); + + this.currentTab = 'element'; + this.addPanel.appendChild(this.elementTable.element); + + + + this._events(); + } + + + _events() { + // External event - Search button press + this.mainButton.addEventListener( "click", (e) => { + //console.log("this.labels: "+JSON.stringify(this.labels)); + + if (this.searchQuery.lenght === 0){ + util.showUserMsg('No query'); + }else{ + let searchExpressionQuery; + + // Search by Material name + if (this.queryTypes[0] === 'MN'){ + //queryObj.push(this._getESSimpleMatch('material_name', item)); + let rootQueryObj = { 'bool' : {} }; + rootQueryObj.bool.must = []; + rootQueryObj.bool.must.push( this._getESSimpleMatch('material_name', this.searchQuery[0]) ); + this.materialList.initSearch( rootQueryObj ); + + }else{ // Search by complex search expression + + if (this.element.querySelector('#allow-other-elements').checked) + searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements( + this.searchQuery, this.queryTypes); + // Regular case: search containing only the elements in the search expression + else searchExpressionQuery = + this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes); + + this.materialList.initSearch( + this._addFiltersInSearchQuery( this.filterPanel.getValues(), + searchExpressionQuery)); + //util.setBrowserHashPath('search','results'); + + this.element.querySelector('.add-box').style.display = 'none'; + } + } + + }); + + + this.cleanButton.addEventListener( "click", (e) => { + this.searchQuery = []; + this.queryTypes = []; + this.updateSearchQuery(); + this.addFormulaButton.disabled = false; + this.addMatNameButton.disabled = false; + this.addElementButton.disabled = false; + this.formulaBox.disable(false); + this.materialNameBox.disable(false); + this.elementTable.deselectAllElements(); + }); + + this.addButtonsBox.addEventListener( "click", (e) => { + if (e.target !== e.currentTarget) { // When the event source is a child + let className = e.target.className; + let index = className.indexOf('add-btn'); + + if (index > 0){ + let selectingElement; + let selectingTab = className.substring(0, index-1); + if (selectingTab === 'element') + selectingElement = this.elementTable.element; + else if (selectingTab === 'name') + selectingElement = this.materialNameBox.element; + else if (selectingTab === 'formula') + selectingElement = this.formulaBox.element; + + this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild); + + this.element.querySelector('.add-box').style.display = 'block'; + + // Change the styles of the buttons + let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn'); + this._setTabSelectedStyles(selEl, false); + this._setTabSelectedStyles(e.target, true); + + // Change the triangle + this.element.querySelector('.'+this.currentTab+'-tri').style.visibility = 'hidden'; + this.element.querySelector('.'+selectingTab+'-tri').style.visibility = 'visible'; + + this.currentTab = selectingTab; + +/* + if (this.userGuidance){ + if (selectingTab === 'element'){ + UserGuidance.showIndependentTip(7, false); + UserGuidance.showIndependentTip(3, true); + } + else if (selectingTab === 'props'){ + UserGuidance.showIndependentTip(3, false); + UserGuidance.showIndependentTip(7, true); + }else if (selectingTab === 'formula'){ + UserGuidance.showIndependentTip(3, false); + UserGuidance.showIndependentTip(7, false); + } + } + */ + + } + } + }); + + this.searchQueryBox.addEventListener( "click", (e) => { + let className = e.target.className; + if (className === 'remove-label'){ + let elSymbol = e.target.parentElement.getAttribute('data-el'); + this.removeElementORFormulaInSearchQuery(elSymbol); + } + }); + + } + + + _getESQueryFromSearchQuery(searchQuery, queryTypes){ + let formulas = []; + let parFormulas = []; + let parOperator = null; + let openIndex = -1; + searchQuery.forEach( (item, i) => { + if (queryTypes[i] === 'F' || queryTypes[i] === 'E'){ + addItem( (openIndex >= 0 ? parFormulas : formulas), i, + item+(queryTypes[i] === 'E' ? '0' : '')); + + }else if ( searchQuery[i] === '(' ){ + if (i-1 >= 0 ) parOperator = searchQuery[i-1]; + openIndex = i; + } + else if ( searchQuery[i] === ')' ){ + if (parOperator === null){ // The starting ( was the first symbol of the expression + formulas = parFormulas; + }else if (parOperator === 'OR'){ // OR (... + formulas = formulas.concat(parFormulas); + }else{ // AND (... + let rFormulas = []; + formulas.forEach( formula => { + parFormulas.forEach( parFormula => rFormulas.push(formula+parFormula) ); + }); + formulas = rFormulas; + } + parFormulas = []; // reset the array formulas inside the parentheses + + openIndex = -1; + } + }); + console.log('_getESQueryFromSearchQuery: ',formulas, parFormulas); + + //*********** Get the elastic search expression from the search expression + // the elements inserted must be sorted for this to work + // queryObj.bool.must.push(this._getESSimpleMatch('atom_labels_keyword', this._sortElements(elements) )); ///elements.join(''))); + // if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search + let rootQueryObj = { 'bool' : {} }; + rootQueryObj.bool.should = []; + let queryObj = rootQueryObj.bool.should; + let reduced = this.element.querySelector('#multiples-of-formula').checked; + + formulas.forEach( formula => { + let pFormula; + let searchByElement = (formula.indexOf('0') >= 0); + if (searchByElement){ // There are some element in the search expression + /** TDO**/ + let f = formula; + if (reduced) f = this._reduceFormula(formula, false); + pFormula = this._processFormula(f, 'element-string'); + let tempQueryObj = { 'bool' : { 'must' : [] } }; + + if (pFormula[0].length > 0) // length === 0 No formula, only elements + tempQueryObj.bool.must.push( + this._getESOperatorMatch( + (reduced ? 'formula_reduced_terms' : 'formula_cell_terms'), pFormula[0])); + tempQueryObj.bool.must.push( + this._getESSimpleMatch('atom_labels_keyword', pFormula[1])); + + queryObj.push(tempQueryObj); + + }else{ // Only formulas + if ( reduced ){ // reduced search + pFormula = this._reduceFormula(formula, false); + queryObj.push(this._getESSimpleMatch('formula_reduced_keyword', pFormula)); + }else{ + pFormula = this._processFormula(formula, 'canonical-formula'); + queryObj.push(this._getESSimpleMatch('formula_cell_keyword', pFormula)); + } + } + + }); + + return rootQueryObj; + + function addItem(formulas, i, item){ + if (i === 0 || searchQuery[i-1] === '('){ + formulas.push(item); + }else{ + if (searchQuery[i-1] === 'OR') formulas.push(item); + else if (searchQuery[i-1] === 'AND') + formulas.push(formulas.pop()+item); + } + } + + } // _getESQueryFromSearchQuery() + + + _getESQueryFromSearchQuery_otherElements(searchQuery, queryTypes){ + // Query structure analysis - looking for parentheses (only one level supported) + if ( searchQuery.indexOf('(') >= 0){ // Recursion + + let openIndex = -1; + let prodQuery = []; + let prodTypes = []; + //let prodQueryIndex = 0; + for (let i = 0; i < searchQuery.length; i++) { + if ( searchQuery[i] === '(' ) openIndex = i; + else if ( searchQuery[i] === ')' ){ + prodQuery.push(this._getESQueryFromSearchQuery_otherElements( + searchQuery.slice(openIndex+1, i), queryTypes.slice(openIndex+1, i))); + prodTypes.push('Q'); + openIndex = -1; + }else if (openIndex < 0){ // outside a parentheses + prodQuery.push(searchQuery[i]); + prodTypes.push(queryTypes[i]); + } + } + //console.log('prodQuery', prodQuery, prodTypes); + return this._getESQueryFromSearchQuery_otherElements(prodQuery, prodTypes); + + }else{ // BASE CASE: there is no parentheses + + let boolOperator; + searchQuery.forEach( (item, i) => { + if (searchQuery[i] === 'AND' || searchQuery[i] === 'OR') + boolOperator = item; + }); + + let rootQueryObj = { 'bool' : {} }; + let queryObj; + if (boolOperator === 'AND'){ + rootQueryObj.bool.must = []; + queryObj = rootQueryObj.bool.must; + }else{ // OR + rootQueryObj.bool.should = []; + queryObj = rootQueryObj.bool.should; + } + + searchQuery.forEach( (item, i) => { + + if (queryTypes[i] === 'F'){ // Formula case + let esMatchQuery; + if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search + esMatchQuery = this._getESOperatorMatch('formula_reduced_terms', this._reduceFormula(item)); + }else + esMatchQuery = this._getESOperatorMatch('formula_cell_terms', this._processFormula(item, 'tokens')); + queryObj.push(esMatchQuery); + + }else if (queryTypes[i] === 'E'){ // Element case + queryObj.push(this._getESSimpleMatch('atom_labels_terms', item)); + + }else if (queryTypes[i] === 'Q'){ + queryObj.push(item); + } + }); + + return rootQueryObj; + } // else + } // _getESQueryFromSearchQuery_otherElements() - // Observer pattern - setClickListener(listener) { - this.clickListener= listener; - } - setDeselectListener(listener) { - this.deselectListener= listener; - } + _addFiltersInSearchQuery(filterMap, searchExpressionQuery){ + let rootQueryObj = { 'bool' : {} }; + rootQueryObj.bool.must = []; + rootQueryObj.bool.must.push( searchExpressionQuery ); + + + filterMap.forEach((values/*Array*/, filterName) => { - selectElement(elSymbol) { - this.element.querySelector('td[data-el="el-'+elSymbol+'"]') - .className= 'cell el-selected'; - } + let filterNameDef = replaceDashes(filterName); + if (filterName === 'mass-density' || filterName === 'band-gap'){ + //***** util.eV2J() apply? + rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) ); - deselectElement(elSymbol) { - //document.getElementById('el-'+elSymbol).className= 'cell '+getElementBlock(elSymbol); - this.element.querySelector('td[data-el="el-'+elSymbol+'"]') - .className= 'cell '+getElementBlock(elSymbol); - } + }else if (filterName === 'band-gap-type'){ // special case + if ( values !== 'both') + rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct', + ( values === 'direct' ? true : false ) ) ); + }else if (filterName.startsWith('has')){ // has- filters + rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) ); + + }else{ // normal case + //rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) ); + rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) ); - deselectAllElements(){ - let selectedElements = this.element.querySelectorAll('td.el-selected'); + //console.log(this._getESOperatorMatch(filterNameDef, values, false) ); + } - //selectedElements.forEach( element => { - for (let i = 0; i < selectedElements.length; ++i){ - let elSymbol = selectedElements[i].getAttribute('data-el').substring(3); - selectedElements[i].className= 'cell '+getElementBlock(elSymbol); - } - } + }); - } // class ElemenTable + return rootQueryObj; + } - function getElement(e){ - let element = null; - let className = null; - //console.log("TARGET " +e.target.className+' '+e.target.innerHTML+' '); - if (e.target.className.indexOf('cell ') >= 0){ - element= e.target; - className = e.target.className; + _getESSimpleMatch(field, value){ + return { + "match": { [field] : value } + }; + } - }else if (e.target.parentElement.className.indexOf('cell ') >= 0){ - element= e.target.parentElement; - className = e.target.parentElement.className; - } + _getESTermsArray(field, value){ + return { + "terms": { [field] : value } + }; + } - if (element === null) return null; - else{ - let html= element.innerHTML; - let elSymbol= html.substring(3,html.indexOf('<',3)); - if (elSymbol === ' ') return null; // blank cells - else return elSymbol; - } - } + _getESOperatorMatch(field, elements, and = true){ + let elementsString = ''; + if (elements.length > 0) elementsString = elements.join(' '); + return { + "match": { + [field]: { + "operator": (and ? "and" : 'or'), + "query": elementsString + } + } + }; + } - // EXPORTS - module.exports = ElemenTable; + _getFieldESRange(field, valuesString){ + let data = valuesString.split(':'); + console.log('_getFieldESRange data', data); + return { + "range": { + [field]: { "gte" : parseInt(data[0]), "lte" : parseInt(data[1]) } + } + }; + } -/***/ }, -/* 35 */ -/***/ function(module, exports, __webpack_require__) { - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - This component implements the list of materials found in the search - */ - - "use strict"; - - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - let LoadingPopup = __webpack_require__(4); - - const RESULTS_PER_PAGE = 20; - - - class MaterialList { - - constructor(){ - this.element = document.createElement('div'); - this.element.setAttribute("id",'matlist'); - // map containing an entry per system type (if there are materials for it) - this.matMap = new Map(); - this.currentSystemType = 'bulk'; - this.esQueryObject = null; - - this.element.innerHTML= - ` - <div id="system-type-tabs" style=" display: inline-block"> - <button id="system-tab-bulk" class="selected">BULK</button> - <button id="system-tab-2D" >2D</button> - <button id="system-tab-1D" >1D</button> - <span class="results-total" >Results<span> - </div> - <div class="pag-header"> - <span class="prevButton"> - <img src="img/prev.svg" style="display: inline;" width="7px"/> prev - </span> - <span class="page"> X </span> - <span class="nextButton"> next - <img src="img/next.svg" width="7px" /> - </span> - </div> - <div class="data-container"> </div>`; // results data container - - this.titleBox = this.element.getElementsByClassName("results-total")[0]; - this.tabsBox = this.element.querySelector("#system-type-tabs"); - // Pagination components - this.resultsNrTag = this.element.getElementsByClassName("pag-header")[0]; - - this.prevButton = this.element.getElementsByClassName("prevButton")[0]; - this.pageElement = this.element.getElementsByClassName("page")[0]; - this.nextButton = this.element.getElementsByClassName("nextButton")[0]; - - this.resultsContainer = this.element.getElementsByClassName("data-container")[0]; - } - attachAndSetEvents(element){ - element.appendChild(this.element); - this._events(); - } + _setTabSelectedStyles(element, value){ + /* + element.style.fontWeight = (value ? 'bold' : 'normal'); + element.style.color = (value ? '#E56400' : '#777'); + element.style.borderColor = (value ? '#E56400' : '#777'); + */ + element.id = (value ? 'add-tab-selected' : ''); + } - _events() { - this.tabsBox.addEventListener( "click", (e) => { - if (e.target !== e.currentTarget) { // When the event source is a child - let tabId = e.target.id; - let index = tabId.indexOf('system-tab'); +/* + _showSearchBox(){ + if (!this.showingSearchBox){ + this.showingSearchBox = true; + this.searchLine.style.visibility = 'visible'; - if (index >= 0){ - let selectingTab = tabId.substring('system-tab'.length+1); + if (this.userGuidance) UserGuidance.setFinal(); + } + }*/ - this._updateUI(selectingTab); - // Change the styles of the tabs - this.element.querySelector('#system-tab-'+this.currentSystemType).className = ''; - this.element.querySelector('#system-tab-'+selectingTab).className = 'selected'; + _addItemInSearchQuery(item, type){ + this.searchQuery.push(item); + this.queryTypes.push(type); + } - this.currentSystemType = selectingTab; - } - } - }); + addTagInSearchQuery(tag, type){ + // If the it's an element and is already in the query it's not inserted + if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return; + + if ( this.searchQuery.length > 0 + && this.searchQuery[this.searchQuery.length-1] !== '(') + this._addItemInSearchQuery(this.currentOperator, 'S'); + this._addItemInSearchQuery(tag, type); + this.updateSearchQuery(); + //this._showSearchBox(); + } + + + addElementsInSearchQuery(elementArray){ + let index = elementArray.length; + while (index--) { + this.addTagInSearchQuery(elementArray[index], 'E'); + } + return true; + } + + + _addParenthesesInSearchQuery(isOpen){ + + if ( this.searchQuery.length > 0 && isOpen) + this._addItemInSearchQuery(this.currentOperator, 'S'); + this._addItemInSearchQuery( (isOpen ? '(' : ')'), 'P'); + this.updateSearchQuery(); + //this._showSearchBox(); + } + + + removeElementORFormulaInSearchQuery(item){ + //console.log(" removeElementORFormulaInSearchQuery item: ",item, this.searchQuery.indexOf(item)); + + let isMaterialName = (this.queryTypes[0] === 'MN'); + // Travese the array removing the item and the bool operator related + let itemIndex = this.searchQuery.indexOf(item); + if (itemIndex >= 0){ + let i, elementsToRemove; + if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the left + i = itemIndex; elementsToRemove = 2; + }else if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the right + i = itemIndex-1; elementsToRemove = 2; + }else{ // case: (item) + i = itemIndex; elementsToRemove = 1; + } + this.searchQuery.splice(i, elementsToRemove); + this.queryTypes.splice(i, elementsToRemove); + } + + // Travese the array removing the unnecessary parethesis (only tested for one level nested) + if ( this.searchQuery.indexOf('(') >= 0){ // Recursion + + for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed + if ( this.searchQuery[i] === '(' ){ + if ( this.searchQuery[i+1] === ')'){ // '()' case + this.searchQuery.splice(i, 2); + this.queryTypes.splice(i, 2); + }else if (searchQuery[i+2] === ')'){ // '(item)' case + this.searchQuery.splice(i, 3, this.searchQuery[i+1]); + this.queryTypes.splice(i, 3, this.queryTypes[i+1]); + } + } + } + } + + this.updateSearchQuery(); + + if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed) + this.elementTable.deselectElement(item); + } + + if (this.queryTypes.length === 0){ + + if (isMaterialName){ + this.addElementButton.disabled = false; + this.addFormulaButton.disabled = false; + this.formulaBox.disable(false); + }else{ // element or formula + this.addMatNameButton.disabled = false; + + } + this.materialNameBox.disable(false); + } + + //console.log(" final searchQuery: ",this.searchQuery); + //} + + return true; + } + + + updateSearchQuery(){ + let html= ''; + for (let i = 0; i < this.searchQuery.length; i++) { + let type = this.queryTypes[i]; + + if (type === 'S' || type === 'P') + html+= `<span class="search-query-symbol" > ${this.searchQuery[i]} </span>`; + else + html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false)); + } + console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes); + this.searchQueryBox.innerHTML = html; + } + + ///********* DELETE? + showSearchResults(){ + /* + this.searchPage.style.display= 'none'; + this.resultsPage.style.display= 'block'; +*/ + //if (this.userGuidance) UserGuidance.show(false); + } + + + showSearchPage(){ + /* + this.searchPage.style.display= 'block'; + this.resultsPage.style.display= 'none'; + + if (this.userGuidance){ + setTimeout(() => { + UserGuidance.init(this.addButtonsBox, this.elementTable.element, + this.searchBox, this.propertiesBox.tabsElement); + UserGuidance.show(true, this.currentTab === 'element', + this.currentTab === 'props'); + }, 400); + } + */ + } + + + _sortElements(elements){ + let numbers = []; + let sortedElements = []; + elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) ); + numbers.sort( (a, b) => a - b ); // atomic number-1 + numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) ); + //console.log('_sortElements ',numbers, elString); + return sortedElements; + } + + + + + _reduceFormula(formula, getTokens = true){ + let index = 0; + let map = new Map(); + let key; + while ( index < formula.length ){ + let el2 = formula.substring(index, index+2); + let el1 = formula.substring(index, index+1); + + if (util.ELEMENTS.indexOf(el2) >= 0){ + map.set(el2, 1); // 1 default value + index += 2; + key = el2; + //console.log('eleemnt 2chars', key); + }else if (util.ELEMENTS.indexOf(el1) >= 0){ + map.set(el1, 1); // 1 default value + index++; + key = el1; + //console.log('eleemnt 1chars', key); + }else{ // It's a number + let num = parseInt(el2); + if (num >= 10) index += 2; // 2 figures number + else index++;// 1 figure number + //console.log('number ', num, key); + map.set(key, num); + } + // console.log('FINAL LOOP', map, index); + } + + let counter = 0; + while ( !checkIfReduced(map) ){ // console.log('Reducing', map); + let div = 1; + if (isDivisibleBy(map, 2)) div = 2; + else if (isDivisibleBy(map, 3)) div = 3; + else if (isDivisibleBy(map, 5)) div = 5; + else if (isDivisibleBy(map, 7)) div = 7; + else if (isDivisibleBy(map, 11)) div = 11; + + map.forEach( (value, key) => { + map.set(key, (value/div)); + }); + //console.log('Reducing DIV', map); + counter++; + if (counter > 5) break; + } + + function checkIfReduced(formulaMap){ + let min = 100; + formulaMap.forEach( (value, key) => { + if (value < min) min = value; + }); + return min === 1; + } + + function isDivisibleBy(formulaMap, n){ + let div = true; + formulaMap.forEach( (value, key) => { + if (value % n !== 0) div = false; + }); + return div; + } + + let tokens = []; + let canonicalFormula = ''; + if (getTokens){ + map.forEach( (value, key) => tokens.push(key+value) ); + }else{ + let sortedElements = this._sortElements( Array.from( map.keys() ) ); + sortedElements.forEach( element => { + canonicalFormula += element+map.get(element); + //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element)); + }); + } + + + console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula); + return (getTokens ? tokens : canonicalFormula); + } + + + _processFormula(formula, type){ + let index = 0; + let map = new Map(); + let key; + while ( index < formula.length ){ + let el2 = formula.substring(index, index+2); + let el1 = formula.substring(index, index+1); + + if (util.ELEMENTS.indexOf(el2) >= 0){ + map.set(el2, 1); // 1 default value + index += 2; + key = el2; + //console.log('eleemnt 2chars', key); + }else if (util.ELEMENTS.indexOf(el1) >= 0){ + map.set(el1, 1); // 1 default value + index++; + key = el1; + //console.log('eleemnt 1chars', key); + }else{ // It's a number + let num = parseInt(el2); + if (num >= 10) index += 2; // 2 figures number + else index++;// 1 figure number + //console.log('number ', num, key); + map.set(key, num); + } + // console.log('FINAL LOOP', map, index); + } + + + + if (type === 'tokens'){ + let tokens = []; + map.forEach( (value, key) => tokens.push(key+value) ); + console.log('_processFormula RETURN: ', map, tokens); + return tokens; + }else{ + let sortedElements = this._sortElements( Array.from( map.keys() ) ); + if (type === 'canonical-formula'){ + let formulaString = ''; + sortedElements.forEach( element => { + formulaString += element+map.get(element); + }); + console.log('_processFormula RETURN: ', map, formulaString); + return formulaString; + }else{ // elements-string + let elementsString = ''; + let elementsInFormulas = []; + sortedElements.forEach( element => { + elementsString += element; + let val = map.get(element); + if (val !== 0) elementsInFormulas.push(element+val); + }); + console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]); + return [elementsInFormulas ,elementsString]; + } + } + + } + +} + + +class FormulaBox{ + + constructor() { + this.element = document.createElement('div'); + this.element.setAttribute("id",'formula-box'); + this.element.innerHTML= + ` + <div style="padding-bottom: 20px;"> +<!-- + <input type="checkbox" class="allow-other-elements" value=""> + Allow other elements + <br> + + + <input type="checkbox" class="multiples-of-formula" value=""> + Include multiples of formula + <br> --> + + <input type="text" class="formula-text-field" + placeholder="Add formula to the search query above" > + <button class="adding-formula-btn" disabled>Add to query</button> + </div> + `; + this.formulaTextField = this.element.querySelector('.formula-text-field'); + this.formulaButton = this.element.querySelector('.adding-formula-btn'); + + this.formulaButton.addEventListener( "click", (e) => { + this.addFormulaListener(this.formulaTextField.value); + this.formulaTextField.value = ''; + }); + + this.formulaTextField.addEventListener( 'input', e => { + //console.log('formulaTextField input: ',this.formulaTextField.value); + this.formulaButton.disabled = (this.formulaTextField.value === ''); + }); + + } + + + setAddFormulaListener(listener) { + this.addFormulaListener= listener; + } + + + disable(bool) { + this.formulaTextField.disabled = bool; + this.formulaButton.disabled = true;//bool; + } + +/* + getAllowOtherElements(){ + return this.element.querySelector('.allow-other-elements').checked; + } + + + getMultiplesOfFormula(){ + return this.element.querySelector('.multiples-of-formula').checked; + }*/ + +} + + +class MaterialNameBox{ - this.nextButton.addEventListener('click', e => { - let systemData = this.matMap.get(this.currentSystemType); - if (systemData.page === systemData.totalPages) return; - systemData.page++; - this._paginationSearch(); - }); + constructor() { + this.element = document.createElement('div'); + this.element.setAttribute("id",'material-name-box'); + this.element.innerHTML= + ` <div style="padding-bottom: 20px;"> + <input type="text" class="name-text-field" + placeholder="Add a material name to the search query above" > + <button class="adding-name-btn" disabled>Add to query</button> + </div> + `; + this.textField = this.element.querySelector('.name-text-field'); + this.button = this.element.querySelector('.adding-name-btn'); - this.prevButton.addEventListener('click', e => { - let systemData = this.matMap.get(this.currentSystemType); - if (systemData.page === 1) return; - systemData.page--; - this._paginationSearch(); - }); + this.button.addEventListener( "click", (e) => { + this.listener(this.textField.value); + this.textField.value = ''; + }); - this.resultsContainer.addEventListener('click', (e) => { - if (e.target !== e.currentTarget) { // When the event source is a child - - let element; - if (e.target.className === 'mat-row') element = e.target; - else if (e.target.parentElement.className === 'mat-row') - element = e.target.parentElement; - else if (e.target.parentElement.parentElement.className === 'mat-row') - element = e.target.parentElement.parentElement; - - if (element) { - util.setBrowserHashPath('material', element.getAttribute('data-mat-id')); - } - e.stopPropagation(); - } - }); + this.textField.addEventListener( 'input', e => { + //console.log('formulaTextField input: ',this.formulaTextField.value); + this.button.disabled = (this.textField.value === ''); + }); - } + } - _paginationSearch(){ + setAddMaterialNameListener(listener) { + this.listener = listener; + } - let systemTypePosition = this.esQueryObject.bool.must.length-1; - this.esQueryObject.bool.must[systemTypePosition].match.system_type = this.currentSystemType; - let page = this.matMap.get(this.currentSystemType).page; - let req = util.serverReqPOST(util.getSearchURL()+'?page='+page+ - '&per_page='+RESULTS_PER_PAGE, JSON.stringify(this.esQueryObject), e => { + disable(bool) { + this.textField.disabled = bool; + this.button.disabled = true;//bool; + } +} - let data = JSON.parse(e.target.response); +// EXPORTS +module.exports = NewSearchMod; - this.matMap.set(this.currentSystemType, this._createSystemTypeData(data, page, true)); - //console.log('this.matMap: ', this.matMap); - this._updateUI(this.currentSystemType); - }); - } +/***/ }), +/* 34 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This is the UI component implementing the interactive Element Table + in the search front. It need to communicate to other UI components + so it exposes several listeners + */ + + + +let util = __webpack_require__(0); + + +// constans + +const GROUPS = new Map([ + [1,['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']], + [2,['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']], + [3,['Sc', 'Y']], + [4,['Ti', 'Zr', 'Hf', 'Rf']], + [5,['V', 'Nb', 'Ta', 'Ha']], + [6,['Cr', 'Mo', 'W', 'Sg']], + [7,['Mn', 'Tc', 'Re', 'Ns']], + [8,['Fe', 'Ru', 'Os', 'Hs']], + [9,['Co', 'Rh', 'Ir', 'Mt']], + [10,['Ni', 'Pd', 'Pt', 'Ds']], + [11,['Cu', 'Ag', 'Au', 'Rg']], + [12,['Zn', 'Cd', 'Hg', 'Cn']], + [13,['B', 'Al', 'Ga', 'In', 'Tl', 'Nh']], + [14,['C', 'Si', 'Ge', 'Sn', 'Pb', 'Fl']], + [15,['N', 'P', 'As', 'Sb', 'Bi', 'Mc']], + [16,['O', 'S', 'Se', 'Te', 'Po', 'Lv']], + [17,['F', 'Cl', 'Br', 'I', 'At', 'Ts']], + [18,['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']], + [19,['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu']], + [20,['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr']] +]); + +const BLOCKS = new Map([ + ['metalloids',['B', 'Si', 'Ge', 'As', 'Sb', 'Te', 'Po']], + ['other-non-metals',['H', 'C', 'N', 'O', 'P', 'S', 'Se']], + ['halogens',['F', 'Cl', 'Br', 'I', 'At', 'Ts']], + ['noble-gases',['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']], + ['alkali-metals',['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']], + ['alkaline-earth-metals',['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']], + ['lanthanoids',['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er' + ,'Tm','Yb','Lu']], + ['actinoids',['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm' + ,'Md','No','Lr']], + ['transition-metals', ['Sc', 'Y','Ti', 'Zr', 'Hf', 'Rf','V','Nb','Ta','Ha' + ,'Cr','Mo','W','Sg','Mn','Tc','Re','Ns','Fe','Ru','Os','Hs','Co','Rh','Ir' + , 'Mt','Ni','Pd','Pt', 'Ds', 'Cu','Ag','Au', 'Rg', 'Zn','Cd','Hg', 'Cn']], + ['post-transition-metals', ['Al','Ga', 'In', 'Tl', 'Nh', 'Sn', 'Pb', 'Fl', 'Bi', 'Mc', 'Lv']] +]); + +const BLOCKS_COLORS = new Map([ + ['metalloids','#F9E298'], + ['other-non-metals','#F2B01D'], + ['halogens','#85ADC1'], + ['noble-gases','#F7D660'], + ['alkali-metals','#D04629'], + ['alkaline-earth-metals','#F7B57D'], + ['transition-metals', '#F58737'], + ['post-transition-metals', '#AE4747'], + ['lanthanoids','#3B91AE'], + ['actinoids','#E97147'] +]); + +let elementNames = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium', + 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine', + 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon', + 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium', + 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium', + 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper', + 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium', + 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium', + 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium', + 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium', + 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon', + 'Cesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium', + 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium', + 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium', + 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten', + 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold', + 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium', + 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium', + 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium', + 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium', + 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium', + 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium', + 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium', + 'Livermorium', 'Tennessine', 'Oganesson' +]; + + +// utility functions + +function getElementBlock(elSymbol){ + let block; + BLOCKS.forEach(function(value, key) { + //console.log(key + " " + value); + if (value.indexOf(elSymbol) >= 0) + block= key; + }); + return block; +} + + +let CELL_WIDTH= 36; + +function getCellHtml(elNum){ + let elSymbol= util.ELEMENTS[elNum-1]; + return '<td class="cell '+getElementBlock(elSymbol)+'" data-el="el-'+elSymbol+'">'+ + '<b>'+elSymbol+'</b> <div>'+elNum+'</div> </td>'; +} + +function getOddCellHtml(elNum){ + let elSymbol= util.ELEMENTS[elNum-1]; + return '<td class="cellpad '+getElementBlock(elSymbol)+'" data-el="el-X">'+ + '<b> </b> <div> </div> </td>'; +} + + +/** Group selection not implemented + +function getGroupSelectorHtml(num){ + return '<th class="group-sel" data-group="'+num+'"><div></div></th>'; +} + +function getGroupSelectorHtmlLAcAc(num){ + return '<th class="group-sel-la-ac" data-group="'+num+'"><div></div></th>'; +}*/ + +/************************/ + + +class ElemenTable{ + + constructor() { + + this.element = document.createElement('div'); + this.element.setAttribute('id','elementable'); + + // header with dropdown + let tempHtml = '<div class="element-info"></div>'; + + tempHtml+= '<div class="ptWrapper">'; + + tempHtml+= '<table id="pt-main">'; // table zone div + + /* Deactivated for the moment + // header with group selectors + tempHtml+= '<thead><tr>';//'<div id="group-selectors">'; + for (let i= 1; i<=18; i++){ + tempHtml+= getGroupSelectorHtml(i); + } + tempHtml+= '</tr></thead>';// selectors + */ + + tempHtml+= '<tbody>'; + // row 1 + tempHtml+= '<tr>'+getCellHtml(1); + tempHtml+= '<td class="cellpad" colspan="16"></td>'; + tempHtml+= getCellHtml(2)+'</tr>'; + + let get8ElementRowHtml= (initPos) => { + tempHtml+= '<tr>'+getCellHtml(initPos)+getCellHtml(initPos+1); + tempHtml+= '<td class="cellpad" colspan="10"></td>'; + for (let i= initPos+2; i< initPos+8; i++) tempHtml+= getCellHtml(i); + tempHtml+= '</tr>';//div.row + } + + // row 2 and 3 + get8ElementRowHtml(3); + get8ElementRowHtml(11); + + // row 4,5, 6 + let counter= 19; + for (let i= 0; i<4; i++){ + tempHtml+= '<tr>'; + for (let j= 0; j<18; j++){ + if (counter === 57 || counter === 89){ + tempHtml += getOddCellHtml(counter); + counter += 15; + }else{ + tempHtml+= getCellHtml(counter); + counter++; + } + } + tempHtml+= '</tr>';//div.row + } + + + tempHtml+= '</tbody></table>'; + + // Lanthanides and Actinides + tempHtml+= '<div id="specialRows"><table id="pt-laac">'; + for (let i= 0; i<2; i++){ + tempHtml+= '<tr>'; + counter = (i === 0 ? 57 : 89); + //tempHtml+= getGroupSelectorHtmlLAcAc((i === 0 ? 19 : 20)); + for (let j= 0; j<15; j++){ + tempHtml+= getCellHtml(counter); + counter++; + } + tempHtml+= '</tr>';//div.row + } + tempHtml+= '</table></div>'; //div#specialRows + + + // Block labels + tempHtml+= `<div class="legend"> + <div class="alkali-metals">Alkali metals</div> + <div class="alkaline-earth-metals">Alkaline earth metals</div> + <div class="transition-metals">Transition metals</div> + <div class="post-transition-metals">Post-transition metals</div> + <div class="metalloids">Metalloids</div> + <div class="other-non-metals">Other nonmetals</div> + <div class="halogens">Halogens</div> + <div class="noble-gases">Noble gases</div> + <div class="lanthanoids">Lanthanoids</div> + <div class="actinoids">Actinoids</div> + </div>`;//'<div id="group-selectors">'; + + + tempHtml+= '</div>'; // ptWrapper + + this.element.innerHTML= tempHtml; + this.elementInfo= this.element.getElementsByClassName('element-info')[0]; + this.tableZone= this.element.getElementsByClassName('ptWrapper')[0]; + //this.specialRows= this.element.querySelector('#specialRows'); + + this._events(); + } + + + _events() { + + // One listener for all diferent clicks (simple element, group) + var adhocListener= (e) => { + + if (e.target !== e.currentTarget) { // When the event source is a child + let className = e.target.className; + let element = e.target; + if (className === ''){ + element= e.target.parentElement; + className = e.target.parentElement.className; + } + + if (className.indexOf('cellpad') >= 0) return; // structural empty table cells + + if (className.indexOf('group-sel') >= 0){ // group selector cells + //*** Not working at the moment - group selection deactivated + let elements= GROUPS.get(parseInt(element.getAttribute('data-group'))); + //console.log("group-sel "+elements); + this.clickListener(elements);//let done = + //if (done) + for (var i = 0; i < elements.length; i++) + this.selectElement(elements[i]); + + }else if (className.indexOf('cell') >= 0){ // element cells + let html= element.innerHTML; + let elSymbol= html.substring(3,html.indexOf('<',3)); + if (elSymbol === ' ') return; // blank cells + //console.log("elSymbol-sel "+elSymbol); + if (className.indexOf('el-selected') >= 0){ // If selected + this.deselectListener(elSymbol);//this.deselectElement(elSymbol); + }else{ // If not selected + this.clickListener([elSymbol]);//let done = this.clickListener([elSymbol]); + this.selectElement(elSymbol);//if (done) this.selectElement(elSymbol); + } + } + } + //e.stopPropagation(); + }; + + // Event listener set in the root div element + this.tableZone.addEventListener('click',adhocListener,true); + + this.tableZone.addEventListener('mouseover',e => { + + let elSymbol= getElement(e); + //console.log("ENTERIG elSymbol-sel "+elSymbol); + if (elSymbol !== null){ + //console.log("elSymbol-sel "+elSymbol); + this.elementInfo.style.display = 'block'; + let borderColor= BLOCKS_COLORS.get(getElementBlock(elSymbol)); + this.elementInfo.style.borderColor = borderColor; + let number= util.ELEMENTS.indexOf(elSymbol)+1; + this.elementInfo.innerHTML= ` + <div> + <div style="float: right; padding: 3px 4px;border-left: 3px solid ${borderColor}; + border-bottom: 3px solid ${borderColor}" > ${number} </div> + <div style="clear: right;"></div> + </div> + <div class="symbol">${elSymbol} </div> + <div class="">${elementNames[number-1]} </div> + ` + } + }); + + this.tableZone.addEventListener('mouseout',e => { + let element= getElement(e); + if (element !== null) this.elementInfo.style.display = 'none'; + }); + + } + + // Observer pattern + setClickListener(listener) { + this.clickListener= listener; + } + + + setDeselectListener(listener) { + this.deselectListener= listener; + } + + + selectElement(elSymbol) { + this.element.querySelector('td[data-el="el-'+elSymbol+'"]') + .className= 'cell el-selected'; + } - initSearchTest(rootQueryObj){ - //this.resultsContainer.style.visibility = 'hidden'; - this.matMap.clear(); + deselectElement(elSymbol) { + //document.getElementById('el-'+elSymbol).className= 'cell '+getElementBlock(elSymbol); + this.element.querySelector('td[data-el="el-'+elSymbol+'"]') + .className= 'cell '+getElementBlock(elSymbol); + } + + + deselectAllElements(){ + let selectedElements = this.element.querySelectorAll('td.el-selected'); + + //selectedElements.forEach( element => { + for (let i = 0; i < selectedElements.length; ++i){ + let elSymbol = selectedElements[i].getAttribute('data-el').substring(3); + selectedElements[i].className= 'cell '+getElementBlock(elSymbol); + } + } + +} // class ElemenTable + + +function getElement(e){ + + let element = null; + let className = null; + //console.log("TARGET " +e.target.className+' '+e.target.innerHTML+' '); + if (e.target.className.indexOf('cell ') >= 0){ + element= e.target; + className = e.target.className; + + }else if (e.target.parentElement.className.indexOf('cell ') >= 0){ + element= e.target.parentElement; + className = e.target.parentElement.className; + } + + if (element === null) return null; + else{ + let html= element.innerHTML; + let elSymbol= html.substring(3,html.indexOf('<',3)); + if (elSymbol === ' ') return null; // blank cells + else return elSymbol; + } +} + - let qqq = { 'bool' : {} }; - //qqq.bool.must = {"terms":{"crystal_system":{"operator":"or","query":"cubic"}}}; - qqq.bool.must = {"terms":{"crystal_system": ["hexagonal","cubic"]}}; - console.log('SENDING: ', JSON.stringify(qqq)); console.log(': ', qqq.bool.must); - let bulkReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ - '&per_page='+RESULTS_PER_PAGE, JSON.stringify(qqq), bulke => { +// EXPORTS +module.exports = ElemenTable; - let bulkData = JSON.parse(bulke.target.response); console.log('GETTING: ', bulkData); - this.matMap.set('bulk', this._createSystemTypeData(bulkData, 1, bulke.target.status === 200)); - LoadingPopup.hide(); - }); +/***/ }), +/* 35 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + This component implements the list of materials found in the search + */ + + + +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); +let LoadingPopup = __webpack_require__(4); + +const RESULTS_PER_PAGE = 20; + + +class MaterialList { + + constructor(){ + this.element = document.createElement('div'); + this.element.setAttribute("id",'matlist'); + // map containing an entry per system type (if there are materials for it) + this.matMap = new Map(); + this.currentSystemType = 'bulk'; + this.esQueryObject = null; + + this.element.innerHTML= + ` + <div id="system-type-tabs" style=" display: inline-block"> + <button id="system-tab-bulk" class="selected">BULK</button> + <button id="system-tab-2D" >2D</button> + <button id="system-tab-1D" >1D</button> + <span class="results-total" >Results<span> + </div> + <div class="pag-header"> + <span class="prevButton"> + <img src="img/prev.svg" style="display: inline;" width="7px"/> prev + </span> + <span class="page"> X </span> + <span class="nextButton"> next + <img src="img/next.svg" width="7px" /> + </span> + </div> + <div class="data-container"> </div>`; // results data container + + this.titleBox = this.element.getElementsByClassName("results-total")[0]; + this.tabsBox = this.element.querySelector("#system-type-tabs"); + // Pagination components + this.resultsNrTag = this.element.getElementsByClassName("pag-header")[0]; - } + this.prevButton = this.element.getElementsByClassName("prevButton")[0]; + this.pageElement = this.element.getElementsByClassName("page")[0]; + this.nextButton = this.element.getElementsByClassName("nextButton")[0]; + this.resultsContainer = this.element.getElementsByClassName("data-container")[0]; + } - initSearch(rootQueryObj){ - //this.resultsContainer.style.visibility = 'hidden'; - this.matMap.clear(); - rootQueryObj.bool.must.push( { "match": {"system_type" : "bulk"} } ); - console.log('SENDING: ', JSON.stringify(rootQueryObj)); console.log(': ', rootQueryObj.bool.must); - let systemTypePosition = rootQueryObj.bool.must.length-1; - this.esQueryObject = rootQueryObj; + attachAndSetEvents(element){ + element.appendChild(this.element); + this._events(); + } - LoadingPopup.show(); - //let oReq = util.serverReqPOST('http://enc-staging-nomad.esc.rzg.mpg.de/current/v1.0/esmaterials', postQuery, e => { - // Bulk materials request - let bulkReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ - '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), bulke => { + _events() { - let bulkData = JSON.parse(bulke.target.response); console.log('GETTING: ', bulkData); + this.tabsBox.addEventListener( "click", (e) => { + if (e.target !== e.currentTarget) { // When the event source is a child + let tabId = e.target.id; + let index = tabId.indexOf('system-tab'); - this.matMap.set('bulk', this._createSystemTypeData(bulkData, 1, bulke.target.status === 200)); + if (index >= 0){ + let selectingTab = tabId.substring('system-tab'.length+1); + this._updateUI(selectingTab); + // Change the styles of the tabs + this.element.querySelector('#system-tab-'+this.currentSystemType).className = ''; + this.element.querySelector('#system-tab-'+selectingTab).className = 'selected'; - // 2D materials request - rootQueryObj.bool.must[systemTypePosition].match.system_type = '2D'; - let twoDReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ - '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), twoDe => { + this.currentSystemType = selectingTab; + } + } - let twoDData = JSON.parse(twoDe.target.response); + }); - this.matMap.set('2D', this._createSystemTypeData(twoDData, 1, twoDe.target.status === 200)); + this.nextButton.addEventListener('click', e => { + let systemData = this.matMap.get(this.currentSystemType); + if (systemData.page === systemData.totalPages) return; + systemData.page++; + this._paginationSearch(); + }); + this.prevButton.addEventListener('click', e => { + let systemData = this.matMap.get(this.currentSystemType); + if (systemData.page === 1) return; + systemData.page--; + this._paginationSearch(); + }); - // 1D materials request - rootQueryObj.bool.must[systemTypePosition].match.system_type = '1D'; - let oneDReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ - '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), oneDe => { + this.resultsContainer.addEventListener('click', (e) => { + if (e.target !== e.currentTarget) { // When the event source is a child - let oneDData = JSON.parse(oneDe.target.response); + let element; + if (e.target.className === 'mat-row') element = e.target; + else if (e.target.parentElement.className === 'mat-row') + element = e.target.parentElement; + else if (e.target.parentElement.parentElement.className === 'mat-row') + element = e.target.parentElement.parentElement; - this.matMap.set('1D', this._createSystemTypeData(oneDData, 1, oneDe.target.status === 200)); + if (element) { + util.setBrowserHashPath('material', element.getAttribute('data-mat-id')); + } + e.stopPropagation(); + } + }); - let selectedSystemType = null; - this.matMap.forEach( (materials, systemType) => { - this.element.querySelector('#system-tab-'+systemType).disabled = - (materials.total === 0); - if (selectedSystemType === null && materials.total > 0) { - selectedSystemType = systemType; - this.element.querySelector('#system-tab-'+this.currentSystemType).className = ''; - this.element.querySelector('#system-tab-'+selectedSystemType).className = 'selected'; + } - this.currentSystemType = selectedSystemType; - } - }); - rootQueryObj.bool.must[systemTypePosition].match.system_type = this.currentSystemType; - this._updateUI(this.currentSystemType); - //this.resultsContainer.style.visibility = 'visible'; - LoadingPopup.hide(); - this._launchMaterialViewerIfOnlyOne(); - }); + _paginationSearch(){ - }); + let systemTypePosition = this.esQueryObject.bool.must.length-1; + this.esQueryObject.bool.must[systemTypePosition].match.system_type = this.currentSystemType; + let page = this.matMap.get(this.currentSystemType).page; - }); + let req = util.serverReqPOST(util.getSearchURL()+'?page='+page+ + '&per_page='+RESULTS_PER_PAGE, JSON.stringify(this.esQueryObject), e => { - /* - oReq.addEventListener("error", e => { // Not valid query - console.log('Search ERROR - Not valid query '); - this.total_results= 0; - this.setData([]); - this._updateUI(); - this.resultsContainer.style.visibility = 'visible'; - LoadingPopup.hide(); - }); - */ + let data = JSON.parse(e.target.response); + this.matMap.set(this.currentSystemType, this._createSystemTypeData(data, page, true)); + //console.log('this.matMap: ', this.matMap); + this._updateUI(this.currentSystemType); + }); - } + } - _launchMaterialViewerIfOnlyOne(){ - let mat; - let sum = 0; - this.matMap.forEach( (materials, systemType) => { - sum += materials.total; - if (materials.total === 1) - mat = materials.materials.values().next().value[0]; - }); - if (sum === 1) util.setBrowserHashPath('material',+(mat.id)); - } + initSearchTest(rootQueryObj){ + //this.resultsContainer.style.visibility = 'hidden'; + this.matMap.clear(); + let qqq = { 'bool' : {} }; + //qqq.bool.must = {"terms":{"crystal_system":{"operator":"or","query":"cubic"}}}; + qqq.bool.must = {"terms":{"crystal_system": ["hexagonal","cubic"]}}; - _createSystemTypeData(data, page, status){ - - if (status){ - let systemTypeMatMap = new Map(); - - data.results.forEach( mat => { - if (systemTypeMatMap.has(mat.formula_reduced)){ - let matArray= systemTypeMatMap.get(mat.formula_reduced); - matArray.push(mat); - }else{ - let newArray= []; newArray.push(mat); - systemTypeMatMap.set(mat.formula_reduced, newArray); - } - }); - - return { 'materials': systemTypeMatMap, - 'page': page, - 'total': data.total_results, - 'totalPages': Math.ceil(data.total_results/RESULTS_PER_PAGE) - }; - }else return null; // Right query - results not found - //this.total_results = 0; - } + console.log('SENDING: ', JSON.stringify(qqq)); console.log(': ', qqq.bool.must); + let bulkReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ + '&per_page='+RESULTS_PER_PAGE, JSON.stringify(qqq), bulke => { - _updateUI(systemType){ - - let systemData = this.matMap.get(systemType); - //console.log('_updateUI',systemType, systemData); - this.titleBox.innerHTML= 'Results (total: '+systemData.total+')'; - this.pageElement.innerHTML= 'page '+systemData.page+' / '+systemData.totalPages; - - let html = ''; - - if (systemData === null) { - this.resultsNrTag.style.display = 'none'; - this.titleBox.style.display = 'none'; - - //if (this.page === 1) - //html+= `<div class="not-found"> No results found </div>`; - // If there is search without results IF PAGE == 0 THERE ISN'T SEARCH - - }else{ - this.resultsNrTag.style.display = 'block'; - this.titleBox.style.display = 'inline'; - - html +=` - <table> - <thead> <tr> - <th style="width: 24%;"></th> - <th style="width: 16%;"> - <span info-sys-data="space-group">Space group</span> - </th> - <th style="width: 20%;"> - <span >Space gr. int. symbol</span> - </th> - - <th style="width: 22%;"> - <span info-sys-data="structure-type">Structure type</span> - </th> - <th style="width: 18%;">Nº calculations</th> - </tr> </thead> - <tbody> - `; - - systemData.materials.forEach((mats, formula) => { - - let rFormula = util.getSubscriptedFormula(formula); - html+= '<tr> <td class="formula" colspan="5"><b>'+rFormula+'</b>'; - if ( mats.length > 1) - html += '<span style="font-size: 0.86em;"> ('+mats.length+' structures)</span>'; - html += '</td></tr>'; - - mats.forEach( mat => { - let label= (mat.material_name !== null ? mat.material_name : rFormula); - //console.log("MATERIAL ",mat, mat.system_type); - html+= - `<tr class="mat-row" data-mat-id="${mat.id}"> - <td > ${label} [${mat.formula}] </td> - <td style="text-align:center" > - ${mat.space_group_number === null ? '' : mat.space_group_number} - </td> - <td> - ${mat.space_group_international_short_symbol === null ? '' : - mat.space_group_international_short_symbol} - </td> - - <td> ${mat.structure_type === null ? '' : mat.structure_type } </td> - <td style="text-align:center" > ${mat.nr_of_calculations} </td> - </tr>`; - }); - }); - - html +=` </tbody> </table>`; - } - - this.resultsContainer.innerHTML = html; - - InfoSys.addToInfoSystem(this.resultsContainer); - } + let bulkData = JSON.parse(bulke.target.response); console.log('GETTING: ', bulkData); - } + this.matMap.set('bulk', this._createSystemTypeData(bulkData, 1, bulke.target.status === 200)); + LoadingPopup.hide(); + }); - module.exports = MaterialList; + } -/***/ }, -/* 36 */ -/***/ function(module, exports, __webpack_require__) { - - - - /** - * Copyright 2016-2018 Iker Hurtado - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - - /* - - */ - - - "use strict"; - - let util = __webpack_require__(1); - let InfoSys = __webpack_require__(12); - - - - class FilterPanel { - - constructor() { - //this.element = document.getElementById('filter-panel-placeholder'); - this.element = document.createElement('div'); - this.element.setAttribute("id",'filter-panel-placeholder'); - this.element.innerHTML= - ` <br> - <div>--- Structure ---</div> - <br> - <div class="field"> - <div class="field-title">System type</div> - <input type="checkbox" class="system-type-field" value="bulk"> Bulk<br> - <input type="checkbox" class="system-type-field" value="2D"> 2D<br> - <input type="checkbox" class="system-type-field" value="1D"> 1D<br> - </div> - - <div class="field"> - <div class="field-title"><span info-sys-data="crystal-system">Crystal system</span></div> - <input type="checkbox" class="crystal-system-field" value="cubic"> - <span info-sys-data="crystal-system.value:cubic">Cubic</span><br> - <input type="checkbox" class="crystal-system-field" value="hexagonal"> - <span info-sys-data="crystal-system.value:hexagonal">Hexagonal</span><br> - <input type="checkbox" class="crystal-system-field" value="trigonal"> - <span info-sys-data="crystal-system.value:trigonal">Trigonal</span><br> - <input type="checkbox" class="crystal-system-field" value="tetragonal"> - <span info-sys-data="crystal-system.value:tetragonal">Tetragonal</span><br> - <input type="checkbox" class="crystal-system-field" value="orthorhombic"> - <span info-sys-data="crystal-system.value:orthorhombic">Orthorhombic</span><br> - <input type="checkbox" class="crystal-system-field" value="monoclinic"> - <span info-sys-data="crystal-system.value:monoclinic">Monoclinic</span><br> - <input type="checkbox" class="crystal-system-field" value="triclinic"> - <span info-sys-data="crystal-system.value:triclinic">Triclinic</span><br> - </div> - - <div class="field"> - <div class="field-title"><span info-sys-data="space-group">Space group</span></div> - <select id="space-group-dropdown-list"> - <option value="by-number">by number</option> - <option value="by-symbol">by short symbol</option> - </select> - <input type="text" class="space-group-textfield" style="width: 60px"> - - </div> - - <div class="field"> - <div class="field-title">Strukturbericht designation</div> - <input type="text" class="strukturbericht-designation-field"> - </div> - - <div class="field"> - <div class="field-title"><span info-sys-data="structure-type">Structure type</span></div> - <select class="structure-type-field" style="max-width: 174px"> - <option ></option> - </select> - </div> - - - - <br> - <div>--- Properties ---</div> - <br> - - <div class="field"> - <div class="field-title"><span info-sys-data="mass-density">Mass density</span> <span style="font-weight: normal;">(kg/m<sup>3</sup>)</span></div> - <!-- <div class="test-slider"> </div> --> - - Min: <input type="text" class="mass-density-min-field"> - Max: <input type="text" class="mass-density-max-field"> - - </div> - - <div class="field"> - <div class="field-title"><span info-sys-data="band-gap">Band gap</span> <span style="font-weight: normal;">(eV)</span></div> - Min: <input type="text" class="band-gap-min-field"> - Max: <input type="text" class="band-gap-max-field"> - <!-- - <input type="range" class="band-gap-slider" min="5" max="200" step="5" value="100"/> - <input type="range" class="band-gap-slider" min="5" max="200" step="5" value="100"/> - --> - </div> - - - <div class="field" > - <input type="radio" name="band-gap-type" value="direct"> Direct<br> - <input type="radio" name="band-gap-type" value="indirect"> Indirect<br> - <input type="radio" name="band-gap-type" value="both" checked> Both<br> - </div> - - - <div style="font-weight: bold; padding-bottom: 6px" >Results containing...</div> - - <input type="checkbox" class="has-band-structure-field" value="Band structure"> - <span info-sys-data="has-band-structure">Band structure</span><br> - <input type="checkbox" class="has-dos-field" value="DOS"> - <span info-sys-data="has-dos">DOS</span><br> - <input type="checkbox" class="has-fermi-surface-field" value="Fermi surface"> - <span info-sys-data="has-fermi-surface">Fermi surface</span><br> - - <input type="checkbox" class="has-thermal-properties-field" value="Thermal properties"> - <span info-sys-data="has-thermal-properties">Thermal properties</span> - <br> - <!-- <input type="checkbox" class="results-containing-field" value="EOS" disabled>Equation of state<br>--> - - `; - - - let structureTypeSelect = this.element.querySelector('.structure-type-field'); - let r1 = util.serverReq(util.getSuggestionURL('structure_type'), () => { - JSON.parse(r1.response).structure_type.forEach( structureType => { - structureTypeSelect.innerHTML += '<option>'+structureType+'</option>'; - }); - }); + initSearch(rootQueryObj){ + //this.resultsContainer.style.visibility = 'hidden'; + this.matMap.clear(); - InfoSys.addToInfoSystem(this.element); + rootQueryObj.bool.must.push( { "match": {"system_type" : "bulk"} } ); + console.log('SENDING: ', JSON.stringify(rootQueryObj)); console.log(': ', rootQueryObj.bool.must); + let systemTypePosition = rootQueryObj.bool.must.length-1; + this.esQueryObject = rootQueryObj; - /* code for the MaxMinSlider component testing - this.testSlider = this.element.querySelector('.test-slider'); - console.log("TAB: ",this.testSlider); - this.slider = new MaxMinSlider(); - this.slider.setRange(0,10000); - this.testSlider.appendChild(this.slider.element); - */ + LoadingPopup.show(); - } + //let oReq = util.serverReqPOST('http://enc-staging-nomad.esc.rzg.mpg.de/current/v1.0/esmaterials', postQuery, e => { + // Bulk materials request + let bulkReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ + '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), bulke => { + let bulkData = JSON.parse(bulke.target.response); console.log('GETTING: ', bulkData); - getValues(){ - //let map = this.getPropsWithValueFromCurrentTab(false); - let filterMap = new Map(); - this.addFilterFromCheckboxes(filterMap,'system-type'); - this.addFilterFromCheckboxes(filterMap, 'crystal-system'); - this.addSpaceGroupFilter(filterMap);//this.addFilterFromTextField(filterMap, 'space-group-number'); - this.addFilterFromTextField(filterMap, 'strukturbericht-designation'); - this.addFilterFromDropdownList(filterMap, 'structure-type'); - this.addRangeFilter(filterMap, 'mass-density'); - - this.addRangeFilter(filterMap, 'band-gap'); - this.addBandgapTypeFilter(filterMap); - this.addFiltersFromBoolCheckboxes(filterMap, - ['has-band-structure', 'has-dos', 'has-fermi-surface', 'has-thermal-properties']); - console.log('FilterPanel selected:', filterMap); - return filterMap; - } + this.matMap.set('bulk', this._createSystemTypeData(bulkData, 1, bulke.target.status === 200)); + // 2D materials request + rootQueryObj.bool.must[systemTypePosition].match.system_type = '2D'; + let twoDReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ + '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), twoDe => { - addFilterFromTextField(filterMap, filterName){ - let field = this.element.querySelector('.'+filterName+'-field'); - if (field.value !== '') filterMap.set(filterName, [field.value]); - } + let twoDData = JSON.parse(twoDe.target.response); - addFilterFromCheckboxes(filterMap, filterName){ - let checkboxes = this.element.querySelectorAll('.'+filterName+'-field'); - let value = []; - for (var i = 0; i < checkboxes.length; i++) { - if (checkboxes[i].checked) value.push(checkboxes[i].value); - //if (reset) checkboxes[i].checked = false; - } - if (value.length > 0) filterMap.set(filterName, value); - } + this.matMap.set('2D', this._createSystemTypeData(twoDData, 1, twoDe.target.status === 200)); - addFilterFromDropdownList(filterMap, filterName){ - let field = this.element.querySelector('.'+filterName+'-field'); - let value = field.options[field.selectedIndex].value; - if (value.length > 2) filterMap.set(filterName, [value]); - //if (reset) field.selectedIndex = 0; - } - addSpaceGroupFilter(filterMap){ - let filterName; - let field = this.element.querySelector('#space-group-dropdown-list'); - let type = field.options[field.selectedIndex].value; - if (type === 'by-number') filterName = 'space-group-number'; - else filterName = 'space-group-international-short-symbol'; - let value = this.element.querySelector('.space-group-textfield').value; - if (value.trim() !== '') - filterMap.set(filterName, [value]); - //if (reset) field.selectedIndex = 0; - } + // 1D materials request + rootQueryObj.bool.must[systemTypePosition].match.system_type = '1D'; + let oneDReq = util.serverReqPOST(util.getSearchURL()+'?page=1'+ + '&per_page='+RESULTS_PER_PAGE, JSON.stringify(rootQueryObj), oneDe => { - addRangeFilter(filterMap, filterName){ - let minField = document.querySelector('.'+filterName+'-min-field'); - let maxField = document.querySelector('.'+filterName+'-max-field'); - let value = ':'; - //let label = 'Mass Density'; - if (minField.value !== ''){ - //label = minField.value+' < '+label; - value = minField.value+value; - //if (reset) minField.value = ''; - } - if (maxField.value !== ''){ - //label += ' < '+maxField.value; - value = value+maxField.value; - //if (reset) maxField.value = ''; - } - if (value !== ':') filterMap.set(filterName, value); - } + let oneDData = JSON.parse(oneDe.target.response); + this.matMap.set('1D', this._createSystemTypeData(oneDData, 1, oneDe.target.status === 200)); - addBandgapTypeFilter(filterMap){ - let val = document.querySelector('input[name="band-gap-type"]:checked').value; - filterMap.set("band-gap-type", val); - } + let selectedSystemType = null; + this.matMap.forEach( (materials, systemType) => { + this.element.querySelector('#system-tab-'+systemType).disabled = + (materials.total === 0); + if (selectedSystemType === null && materials.total > 0) { + selectedSystemType = systemType; + this.element.querySelector('#system-tab-'+this.currentSystemType).className = ''; + this.element.querySelector('#system-tab-'+selectedSystemType).className = 'selected'; - addFiltersFromBoolCheckboxes(filterMap, boolFilters){ - boolFilters.forEach( filterName => { - let checkboxes = this.element.querySelectorAll('.'+filterName+'-field'); - //let value = []; - for (var i = 0; i < checkboxes.length; i++) { - if (checkboxes[i].checked){ - //value.push(checkboxes[i].value); - filterMap.set(filterName, true); - } - } - //if (value.length > 0) propsMap.set(propName, value); - }); - } + this.currentSystemType = selectedSystemType; + } + }); + rootQueryObj.bool.must[systemTypePosition].match.system_type = this.currentSystemType; + this._updateUI(this.currentSystemType); + //this.resultsContainer.style.visibility = 'visible'; + LoadingPopup.hide(); + this._launchMaterialViewerIfOnlyOne(); + }); - setAddPropertiesListener(listener) { - this.addPropertiesListener= listener; - } + }); + }); - } +/* + oReq.addEventListener("error", e => { // Not valid query + console.log('Search ERROR - Not valid query '); + this.total_results= 0; + this.setData([]); + this._updateUI(); + this.resultsContainer.style.visibility = 'visible'; + LoadingPopup.hide(); + }); +*/ - // EXPORTS - module.exports = FilterPanel; + } + _launchMaterialViewerIfOnlyOne(){ + let mat; + let sum = 0; + this.matMap.forEach( (materials, systemType) => { + sum += materials.total; + if (materials.total === 1) + mat = materials.materials.values().next().value[0]; + }); + + if (sum === 1) util.setBrowserHashPath('material',+(mat.id)); + } + + + _createSystemTypeData(data, page, status){ + + if (status){ + let systemTypeMatMap = new Map(); + + data.results.forEach( mat => { + if (systemTypeMatMap.has(mat.formula_reduced)){ + let matArray= systemTypeMatMap.get(mat.formula_reduced); + matArray.push(mat); + }else{ + let newArray= []; newArray.push(mat); + systemTypeMatMap.set(mat.formula_reduced, newArray); + } + }); + + return { 'materials': systemTypeMatMap, + 'page': page, + 'total': data.total_results, + 'totalPages': Math.ceil(data.total_results/RESULTS_PER_PAGE) + }; + }else return null; // Right query - results not found + //this.total_results = 0; + } - /* To be implemented in the future - class MaxMinSlider{ + _updateUI(systemType){ + + let systemData = this.matMap.get(systemType); + //console.log('_updateUI',systemType, systemData); + this.titleBox.innerHTML= 'Results (total: '+systemData.total+')'; + this.pageElement.innerHTML= 'page '+systemData.page+' / '+systemData.totalPages; + + let html = ''; + + if (systemData === null) { + this.resultsNrTag.style.display = 'none'; + this.titleBox.style.display = 'none'; + + //if (this.page === 1) + //html+= `<div class="not-found"> No results found </div>`; + // If there is search without results IF PAGE == 0 THERE ISN'T SEARCH + + }else{ + this.resultsNrTag.style.display = 'block'; + this.titleBox.style.display = 'inline'; + + html +=` + <table> + <thead> <tr> + <th style="width: 24%;"></th> + <th style="width: 16%;"> + <span info-sys-data="space-group">Space group</span> + </th> + <th style="width: 20%;"> + <span >Space gr. int. symbol</span> + </th> + + <th style="width: 22%;"> + <span info-sys-data="structure-type">Structure type</span> + </th> + <th style="width: 18%;">Nº calculations</th> + </tr> </thead> + <tbody> + `; + + systemData.materials.forEach((mats, formula) => { + + let rFormula = util.getSubscriptedFormula(formula); + html+= '<tr> <td class="formula" colspan="5"><b>'+rFormula+'</b>'; + if ( mats.length > 1) + html += '<span style="font-size: 0.86em;"> ('+mats.length+' structures)</span>'; + html += '</td></tr>'; + + mats.forEach( mat => { + let label= (mat.material_name !== null ? mat.material_name : rFormula); + //console.log("MATERIAL ",mat, mat.system_type); + html+= + `<tr class="mat-row" data-mat-id="${mat.id}"> + <td > ${label} [${mat.formula}] </td> + <td style="text-align:center" > + ${mat.space_group_number === null ? '' : mat.space_group_number} + </td> + <td> + ${mat.space_group_international_short_symbol === null ? '' : + mat.space_group_international_short_symbol} + </td> + + <td> ${mat.structure_type === null ? '' : mat.structure_type } </td> + <td style="text-align:center" > ${mat.nr_of_calculations} </td> + </tr>`; + }); + }); + + html +=` </tbody> </table>`; + } + + this.resultsContainer.innerHTML = html; + + InfoSys.addToInfoSystem(this.resultsContainer); + } + +} + +module.exports = MaterialList; + + +/***/ }), +/* 36 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Copyright 2016-2018 Iker Hurtado + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + + /* + + */ - constructor(){ - this.element = document.createElement('div'); - this.element.innerHTML = ` - <svg class="maxminslider" xmlns="http://www.w3.org/2000/svg" width="100px" height="40px" - viewBox="0 0 100 40" > - <line class="slider-bar" x1="10" x2="90" y1="30" y2="30" stroke="blue" - stroke-width="4"/> - <circle class="min-btn" cx="10" cy="30" r="6" fill="black"/> - <text class="min-text maxminslider-text" x="10" y="10" text-anchor="start"></text> - <circle class="max-btn" cx="90" cy="30" r="6" fill="black"/> - <text class="max-text maxminslider-text" x="90" y="10" text-anchor="end"></text> - </svg> - `; - //this.bar = this.element.querySelector('.slider-bar'); - this.svg = this.element.querySelector('svg'); - this.minButton = this.element.querySelector('.min-btn'); - this.minText = this.element.querySelector('.min-text'); - this.maxButton = this.element.querySelector('.max-btn'); - this.maxText = this.element.querySelector('.max-text'); - this.BUTTON_R = 6; +let util = __webpack_require__(0); +let InfoSys = __webpack_require__(3); + + + +class FilterPanel { + + constructor() { + //this.element = document.getElementById('filter-panel-placeholder'); + this.element = document.createElement('div'); + this.element.setAttribute("id",'filter-panel-placeholder'); + this.element.innerHTML= + ` <br> + <div>--- Structure ---</div> + <br> + <div class="field"> + <div class="field-title">System type</div> + <input type="checkbox" class="system-type-field" value="bulk"> Bulk<br> + <input type="checkbox" class="system-type-field" value="2D"> 2D<br> + <input type="checkbox" class="system-type-field" value="1D"> 1D<br> + </div> + + <div class="field"> + <div class="field-title"><span info-sys-data="crystal-system">Crystal system</span></div> + <input type="checkbox" class="crystal-system-field" value="cubic"> + <span info-sys-data="crystal-system.value:cubic">Cubic</span><br> + <input type="checkbox" class="crystal-system-field" value="hexagonal"> + <span info-sys-data="crystal-system.value:hexagonal">Hexagonal</span><br> + <input type="checkbox" class="crystal-system-field" value="trigonal"> + <span info-sys-data="crystal-system.value:trigonal">Trigonal</span><br> + <input type="checkbox" class="crystal-system-field" value="tetragonal"> + <span info-sys-data="crystal-system.value:tetragonal">Tetragonal</span><br> + <input type="checkbox" class="crystal-system-field" value="orthorhombic"> + <span info-sys-data="crystal-system.value:orthorhombic">Orthorhombic</span><br> + <input type="checkbox" class="crystal-system-field" value="monoclinic"> + <span info-sys-data="crystal-system.value:monoclinic">Monoclinic</span><br> + <input type="checkbox" class="crystal-system-field" value="triclinic"> + <span info-sys-data="crystal-system.value:triclinic">Triclinic</span><br> + </div> + + <div class="field"> + <div class="field-title"><span info-sys-data="space-group">Space group</span></div> + <select id="space-group-dropdown-list"> + <option value="by-number">by number</option> + <option value="by-symbol">by short symbol</option> + </select> + <input type="text" class="space-group-textfield" style="width: 60px"> + + </div> + + <div class="field"> + <div class="field-title">Strukturbericht designation</div> + <input type="text" class="strukturbericht-designation-field"> + </div> + + <div class="field"> + <div class="field-title"><span info-sys-data="structure-type">Structure type</span></div> + <select class="structure-type-field" style="max-width: 174px"> + <option ></option> + </select> + </div> + + + + <br> + <div>--- Properties ---</div> + <br> + + <div class="field"> + <div class="field-title"><span info-sys-data="mass-density">Mass density</span> <span style="font-weight: normal;">(kg/m<sup>3</sup>)</span></div> + <!-- <div class="test-slider"> </div> --> + + Min: <input type="text" class="mass-density-min-field"> + Max: <input type="text" class="mass-density-max-field"> + + </div> + + <div class="field"> + <div class="field-title"><span info-sys-data="band-gap">Band gap</span> <span style="font-weight: normal;">(eV)</span></div> + Min: <input type="text" class="band-gap-min-field"> + Max: <input type="text" class="band-gap-max-field"> + <!-- + <input type="range" class="band-gap-slider" min="5" max="200" step="5" value="100"/> + <input type="range" class="band-gap-slider" min="5" max="200" step="5" value="100"/> + --> + </div> + + + <div class="field" > + <input type="radio" name="band-gap-type" value="direct"> Direct<br> + <input type="radio" name="band-gap-type" value="indirect"> Indirect<br> + <input type="radio" name="band-gap-type" value="both" checked> Both<br> + </div> + + + <div style="font-weight: bold; padding-bottom: 6px" >Results containing...</div> + + <input type="checkbox" class="has-band-structure-field" value="Band structure"> + <span info-sys-data="has-band-structure">Band structure</span><br> + <input type="checkbox" class="has-dos-field" value="DOS"> + <span info-sys-data="has-dos">DOS</span><br> + <input type="checkbox" class="has-fermi-surface-field" value="Fermi surface"> + <span info-sys-data="has-fermi-surface">Fermi surface</span><br> + + <input type="checkbox" class="has-thermal-properties-field" value="Thermal properties"> + <span info-sys-data="has-thermal-properties">Thermal properties</span> + <br> + <!-- <input type="checkbox" class="results-containing-field" value="EOS" disabled>Equation of state<br>--> + + `; + + + let structureTypeSelect = this.element.querySelector('.structure-type-field'); + let r1 = util.serverReq(util.getSuggestionURL('structure_type'), () => { + JSON.parse(r1.response).structure_type.forEach( structureType => { + structureTypeSelect.innerHTML += '<option>'+structureType+'</option>'; + }); + }); + + InfoSys.addToInfoSystem(this.element); + +/* code for the MaxMinSlider component testing + this.testSlider = this.element.querySelector('.test-slider'); + console.log("TAB: ",this.testSlider); + this.slider = new MaxMinSlider(); + this.slider.setRange(0,10000); + this.testSlider.appendChild(this.slider.element); +*/ + + } + + + getValues(){ + //let map = this.getPropsWithValueFromCurrentTab(false); + let filterMap = new Map(); + this.addFilterFromCheckboxes(filterMap,'system-type'); + this.addFilterFromCheckboxes(filterMap, 'crystal-system'); + this.addSpaceGroupFilter(filterMap);//this.addFilterFromTextField(filterMap, 'space-group-number'); + this.addFilterFromTextField(filterMap, 'strukturbericht-designation'); + this.addFilterFromDropdownList(filterMap, 'structure-type'); + this.addRangeFilter(filterMap, 'mass-density'); + + this.addRangeFilter(filterMap, 'band-gap'); + this.addBandgapTypeFilter(filterMap); + this.addFiltersFromBoolCheckboxes(filterMap, + ['has-band-structure', 'has-dos', 'has-fermi-surface', 'has-thermal-properties']); + console.log('FilterPanel selected:', filterMap); + return filterMap; + } + + + + addFilterFromTextField(filterMap, filterName){ + let field = this.element.querySelector('.'+filterName+'-field'); + if (field.value !== '') filterMap.set(filterName, [field.value]); + } + + addFilterFromCheckboxes(filterMap, filterName){ + let checkboxes = this.element.querySelectorAll('.'+filterName+'-field'); + let value = []; + for (var i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked) value.push(checkboxes[i].value); + //if (reset) checkboxes[i].checked = false; + } + if (value.length > 0) filterMap.set(filterName, value); + } + + addFilterFromDropdownList(filterMap, filterName){ + let field = this.element.querySelector('.'+filterName+'-field'); + let value = field.options[field.selectedIndex].value; + if (value.length > 2) filterMap.set(filterName, [value]); + //if (reset) field.selectedIndex = 0; + } + + addSpaceGroupFilter(filterMap){ + let filterName; + let field = this.element.querySelector('#space-group-dropdown-list'); + let type = field.options[field.selectedIndex].value; + if (type === 'by-number') filterName = 'space-group-number'; + else filterName = 'space-group-international-short-symbol'; + let value = this.element.querySelector('.space-group-textfield').value; + if (value.trim() !== '') + filterMap.set(filterName, [value]); + //if (reset) field.selectedIndex = 0; + } + + addRangeFilter(filterMap, filterName){ + let minField = document.querySelector('.'+filterName+'-min-field'); + let maxField = document.querySelector('.'+filterName+'-max-field'); + let value = ':'; + //let label = 'Mass Density'; + if (minField.value !== ''){ + //label = minField.value+' < '+label; + value = minField.value+value; + //if (reset) minField.value = ''; + } + if (maxField.value !== ''){ + //label += ' < '+maxField.value; + value = value+maxField.value; + //if (reset) maxField.value = ''; + } + if (value !== ':') filterMap.set(filterName, value); + } + + + addBandgapTypeFilter(filterMap){ + let val = document.querySelector('input[name="band-gap-type"]:checked').value; + filterMap.set("band-gap-type", val); + } + + addFiltersFromBoolCheckboxes(filterMap, boolFilters){ + boolFilters.forEach( filterName => { + let checkboxes = this.element.querySelectorAll('.'+filterName+'-field'); + //let value = []; + for (var i = 0; i < checkboxes.length; i++) { + if (checkboxes[i].checked){ + //value.push(checkboxes[i].value); + filterMap.set(filterName, true); + } + } + //if (value.length > 0) propsMap.set(propName, value); + }); + } + + + setAddPropertiesListener(listener) { + this.addPropertiesListener= listener; + } + + +} + +// EXPORTS +module.exports = FilterPanel; + + + +/* To be implemented in the future + +class MaxMinSlider{ + + constructor(){ + + this.element = document.createElement('div'); + this.element.innerHTML = ` + <svg class="maxminslider" xmlns="http://www.w3.org/2000/svg" width="100px" height="40px" + viewBox="0 0 100 40" > + <line class="slider-bar" x1="10" x2="90" y1="30" y2="30" stroke="blue" + stroke-width="4"/> + <circle class="min-btn" cx="10" cy="30" r="6" fill="black"/> + <text class="min-text maxminslider-text" x="10" y="10" text-anchor="start"></text> + <circle class="max-btn" cx="90" cy="30" r="6" fill="black"/> + <text class="max-text maxminslider-text" x="90" y="10" text-anchor="end"></text> + </svg> + `; + + //this.bar = this.element.querySelector('.slider-bar'); + this.svg = this.element.querySelector('svg'); + this.minButton = this.element.querySelector('.min-btn'); + this.minText = this.element.querySelector('.min-text'); + this.maxButton = this.element.querySelector('.max-btn'); + this.maxText = this.element.querySelector('.max-text'); + + this.BUTTON_R = 6; - this.minButtonDown = false; - this.minButtonInitX = null; + this.minButtonDown = false; + this.minButtonInitX = null; - this.MIN_BUTTON_INIT_X = 10; - this.minX = 0; + this.MIN_BUTTON_INIT_X = 10; + this.minX = 0; - this.maxButtonDown = false; - this.maxButtonInitX = null; - this.MAX_VALUE = 80;//this.MAX_BUTTON_INIT_X = 90; - this.maxX = this.MAX_VALUE; + this.maxButtonDown = false; + this.maxButtonInitX = null; + this.MAX_VALUE = 80;//this.MAX_BUTTON_INIT_X = 90; + this.maxX = this.MAX_VALUE; - console.log('minButton', this.minButton.getBoundingClientRect()); + console.log('minButton', this.minButton.getBoundingClientRect()); - this._events(); - } + this._events(); + } - _events() { + _events() { - this.minButton.addEventListener( "mousedown", e => this.minButtonDown = true ); - this.minButton.addEventListener( "mouseup", e => this.minButtonDown = false ); - this.minButton.addEventListener( "mouseleave", e => this.minButtonDown = false ); + this.minButton.addEventListener( "mousedown", e => this.minButtonDown = true ); + this.minButton.addEventListener( "mouseup", e => this.minButtonDown = false ); + this.minButton.addEventListener( "mouseleave", e => this.minButtonDown = false ); - this.minButton.addEventListener( "mousemove", e => { - //e.preventDefault(); - if (this.minButtonInitX === null){ - //this.minButtonInitX = this.svg.getBoundingClientRect().left; - this.minButtonInitX = this.minButton.getBoundingClientRect().left+this.BUTTON_R;// - //console.log('left', this.minButtonInitX); - } + this.minButton.addEventListener( "mousemove", e => { + //e.preventDefault(); + if (this.minButtonInitX === null){ + //this.minButtonInitX = this.svg.getBoundingClientRect().left; + this.minButtonInitX = this.minButton.getBoundingClientRect().left+this.BUTTON_R;// + //console.log('left', this.minButtonInitX); + } - if (this.minButtonDown){ - this.minX = e.clientX-this.minButtonInitX ; - if (this.minX > 0 && this.minX < this.maxX-this.BUTTON_R){ - this.minButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.minX); - this.minText.textContent = this.minX*this.factor-250; - } + if (this.minButtonDown){ + this.minX = e.clientX-this.minButtonInitX ; + if (this.minX > 0 && this.minX < this.maxX-this.BUTTON_R){ + this.minButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.minX); + this.minText.textContent = this.minX*this.factor-250; + } - } - }); + } + }); - this.maxButton.addEventListener( "mousedown", e => this.maxButtonDown = true ); - this.maxButton.addEventListener( "mouseup", e => this.maxButtonDown = false ); - this.maxButton.addEventListener( "mouseleave", e => this.maxButtonDown = false ); + this.maxButton.addEventListener( "mousedown", e => this.maxButtonDown = true ); + this.maxButton.addEventListener( "mouseup", e => this.maxButtonDown = false ); + this.maxButton.addEventListener( "mouseleave", e => this.maxButtonDown = false ); - this.maxButton.addEventListener( "mousemove", e => { - //e.preventDefault(); + this.maxButton.addEventListener( "mousemove", e => { + //e.preventDefault(); - if (this.maxButtonInitX === null) - this.maxButtonInitX = this.maxButton.getBoundingClientRect().left+this.BUTTON_R;// + if (this.maxButtonInitX === null) + this.maxButtonInitX = this.maxButton.getBoundingClientRect().left+this.BUTTON_R;// - if (this.maxButtonDown){ + if (this.maxButtonDown){ - this.maxX = e.clientX - this.minButtonInitX; - //console.log('maxButton', e.clientX, this.maxButtonInitX, this.maxX); - if (this.maxX < this.MAX_VALUE && this.minX+this.BUTTON_R < this.maxX){ - this.maxButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.maxX); - this.maxText.textContent = this.maxX*this.factor; - } + this.maxX = e.clientX - this.minButtonInitX; + //console.log('maxButton', e.clientX, this.maxButtonInitX, this.maxX); + if (this.maxX < this.MAX_VALUE && this.minX+this.BUTTON_R < this.maxX){ + this.maxButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.maxX); + this.maxText.textContent = this.maxX*this.factor; + } - } + } - }); + }); - } + } - setRange(min, max){ - this.factor = (max-min)/80; - } + setRange(min, max){ + this.factor = (max-min)/80; + } - } +} - */ +*/ -/***/ } +/***/ }) /******/ ]); \ No newline at end of file diff --git a/client/src/search-mod/NewSearchMod.js b/client/src/search-mod/NewSearchMod.js index a3f6644d..00a798df 100644 --- a/client/src/search-mod/NewSearchMod.js +++ b/client/src/search-mod/NewSearchMod.js @@ -38,7 +38,7 @@ let SwitchComponent = require('../common/SwitchComponent.js'); // local utility functions function getTagHtml(tag, isFormula){ return `<span class="search-label" data-el="${tag}" > - <img src="img/${isFormula ? 'info' : 'tag'}.svg" height="16px" class="remove-label" + <img src="img/tag.svg" height="16px" class="remove-label" style="vertical-align: bottom"/> ${isFormula ? util.getSubscriptedFormula(tag) : tag} <img src="img/cross.svg" height="6px" class="remove-label" @@ -219,24 +219,30 @@ class NewSearchMod { }else{ let searchExpressionQuery; - //************** Material name or complex search expression - //if (this.queryTypes[i] === 'MN'){ + // Search by Material name + if (this.queryTypes[0] === 'MN'){ //queryObj.push(this._getESSimpleMatch('material_name', item)); - - - if (this.element.querySelector('#allow-other-elements').checked) - searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements( - this.searchQuery, this.queryTypes); - // Regular case: search containing only the elements in the search expression - else searchExpressionQuery = - this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes); - - this.materialList.initSearch( - this._addFiltersInSearchQuery( this.filterPanel.getValues(), - searchExpressionQuery)); - //util.setBrowserHashPath('search','results'); - - this.element.querySelector('.add-box').style.display = 'none'; + let rootQueryObj = { 'bool' : {} }; + rootQueryObj.bool.must = []; + rootQueryObj.bool.must.push( this._getESSimpleMatch('material_name', this.searchQuery[0]) ); + this.materialList.initSearch( rootQueryObj ); + + }else{ // Search by complex search expression + + if (this.element.querySelector('#allow-other-elements').checked) + searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements( + this.searchQuery, this.queryTypes); + // Regular case: search containing only the elements in the search expression + else searchExpressionQuery = + this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes); + + this.materialList.initSearch( + this._addFiltersInSearchQuery( this.filterPanel.getValues(), + searchExpressionQuery)); + //util.setBrowserHashPath('search','results'); + + this.element.querySelector('.add-box').style.display = 'none'; + } } }); @@ -608,6 +614,7 @@ class NewSearchMod { removeElementORFormulaInSearchQuery(item){ //console.log(" removeElementORFormulaInSearchQuery item: ",item, this.searchQuery.indexOf(item)); + let isMaterialName = (this.queryTypes[0] === 'MN'); // Travese the array removing the item and the bool operator related let itemIndex = this.searchQuery.indexOf(item); if (itemIndex >= 0){ @@ -643,18 +650,19 @@ class NewSearchMod { if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed) this.elementTable.deselectElement(item); - if (this.queryTypes.indexOf('E') < 0){ // It's the last element - //this.addFormulaButton.disabled = false; + } + + if (this.queryTypes.length === 0){ + + if (isMaterialName){ + this.addElementButton.disabled = false; + this.addFormulaButton.disabled = false; + this.formulaBox.disable(false); + }else{ // element or formula this.addMatNameButton.disabled = false; + } - - }else{ // It's other than element (formula, material name ¿or prop???) - this.addElementButton.disabled = false; - this.formulaBox.disable(false); this.materialNameBox.disable(false); - // Added ¿? - this.addFormulaButton.disabled = false; - this.addMatNameButton.disabled = false; } //console.log(" final searchQuery: ",this.searchQuery); -- GitLab