diff --git a/client/bundle.js b/client/bundle.js index 1170a617f3bf3f5239cb320ca3a7a371d85502cd..5e64aaddb7749fa65e683e884d3bac6d749a9f20 100644 --- a/client/bundle.js +++ b/client/bundle.js @@ -1 +1,445 @@ -!function(e){var t={};function s(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=e,s.c=t,s.d=function(e,t,i){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(s.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(i,n,function(t){return e[t]}.bind(null,n));return i},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=16)}([function(e,t,s){"use strict";const i=window.nomadEnv.apiRoot;function n(e){let t="";for(let s=0;s<e.length;s++)e.charCodeAt(s)>=47&&e.charCodeAt(s)<58?t+="<sub>"+e[s]+"</sub>":t+=e[s];return t}function a(e){let t=e.substring(1,e.length-1).split(","),s=[];for(var i=0;i<t.length;i++)s.push(parseFloat(t[i]));return s}function l(e){return(e*(180/Math.PI)).toFixed(0)+"°"}e.exports={searchResults:!1,materialId:null,MAT_VIEW:{structure:"structure",electronicstruct:"electronicstruct",methodology:"methodology",thermalprops:"thermalprops",elasticconst:"elasticconst"},IMAGE_DIR:"img/",ELEMENTS:["H","He","Li","Be","B","C","N","O","F","Ne","Na","Mg","Al","Si","P","S","Cl","Ar","K","Ca","Sc","Ti","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y","Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I","Xe","Cs","Ba","La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb","Lu","Hf","Ta","W","Re","Os","Ir","Pt","Au","Hg","Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th","Pa","U","Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No","Lr","Rf","Ha","Sg","Ns","Hs","Mt","Ds","Rg","Cn","Nh","Fl","Mc","Lv","Ts","Og"],getUserData:function(){return null},getServerLocation:function(){return document.location.hostname},authServerReq:function(e,t){var s=new XMLHttpRequest;return s.addEventListener("load",t),console.log("util.authServerReq: ",i+e),s.open("GET",i+e),s.send(),s},serverReq:function(e,t){var s=new XMLHttpRequest;s.addEventListener("load",t),s.open("GET",e);var i=window.keycloak;return i.authenticated&&s.setRequestHeader("Authorization","Bearer "+i.token),s.send(),s},convert2d:function(e,t){let s=[];for(var i=0;i<e.length;i++){let a=e[i],l=[];for(var n=0;n<a.length;n++)l.push(a[n]*t);s.push(l)}return s},serverReqPOST:function(e,t,s){var i=new XMLHttpRequest;i.addEventListener("load",s),i.open("POST",e),i.setRequestHeader("Content-Type","application/json");var n=window.keycloak;return n.authenticated&&i.setRequestHeader("Authorization","Bearer "+n.token),i.send(t),i},getShortCode:function(e){if((e.startsWith("eos/")||e.startsWith("par/"))&&32==e.length)return e.substring(0,12);if(28==e.length)return e.substring(0,8);throw"The given identifier could not be shortened as it does not have the right initial length."},getSearchURL:function(){return i+"materials/"},getSuggestionURL:function(e){return i+"suggestions?property="+e},getMaterialURL:function(e){return i+"materials/"+e},getMaterialCalcURL:function(e,t,s=""){return i+"materials/"+e+"/calculations/"+t+(""===s?"":"?property="+s)},getMaterialGroupURL:function(e,t,s){return i+"materials/"+e+"/groups/"+t+"/"+s},getMaterialStatsURL:function(e){return i+"materials/"+e+"/statistics"},getMaterialXsURL:function(e,t){return i+"materials/"+t+"/"+e},getCalcEnergiesURL:function(e,t){return i+"materials/"+e+"/calculations/"+t+"/energies"},getReportURL:function(e){return i+"materials/"+e+"/reports"},setBrowserHashPath:function(e,t){document.location=void 0===t?"#/"+e:"#/"+e+"/"+t},loadLib:function(e){let t=document.createElement("script");t.setAttribute("type","text/javascript"),t.setAttribute("src",e),document.getElementsByTagName("head")[0].appendChild(t)},getNumberArray:a,J2eV:function(e,t){let s=e/1602176565e-28;return void 0===t?s<.01?s.toFixed(6):s.toFixed(3):s.toFixed(t)},eV2J:function(e){return 1602176565e-28*e},m2Angstrom:function(e){return(e/1e-10).toFixed(3)+" Å"},getLatticeAnglesValues:function(e,t,s){let i=[0,0,0];return e.forEach(e=>{if(void 0!==e.lattice_parameters&&null!==e.lattice_parameters){let t=a(e.lattice_parameters);i[0]+=t[3],i[1]+=t[4],i[2]+=t[5]}}),s?`<div>α = ${l(i[0]/e.size)}</div>\n <div>β = ${l(i[1]/e.size)}</div>\n <div>γ = ${l(i[2]/e.size)}</div>`:t?`<div>α = ${l(i[0]/e.size)}</div>`:""},rad2degree:l,m3ToAngstrom3:function(e){return(e/1e-30).toFixed(3)+" Å<sup>3</sup>"},toAngstromMinus3:function(e){return(1e-30*e).toFixed(3)+" Å<sup>-3</sup>"},getMaterialTitle:function(e,t){let s;return s=n(e.formula_reduced),void 0!==t&&!1===t&&(s=e.formula_reduced),null!==e.space_group_number&&void 0!==e.space_group_number&&(s+=" - space group "+e.space_group_number),s},getMinMaxHTML:function(e,t){let s=[];return e.forEach(e=>{s.push(e[t])}),"("+Math.min.apply(null,s)+" ... "+Math.max.apply(null,s)+")"},getSubscriptedFormula:n,getAverage:function(e){let t=0;for(var s=0;s<e.length;s++)t+=e[s];return t/e.length},numberWithCommas:function(e){return e.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")},generateDiagramSteps:function(e,t=4){let s=-Math.floor(Math.log(e/t)*Math.LOG10E),i=Math.pow(10,s),n=Math.ceil(e*i/t),a=[];for(var l=0;l<=t;l++)a[l]=n*l/i;return s=s<0?0:s,[a,s]},getCalcMapByFunctional:function(e){let t=new Map;return e.forEach(e=>{if(t.has(e.functional_type))t.get(e.functional_type).add(e.calc_id);else{let s=new Set;s.add(e.calc_id),t.set(e.functional_type,s)}}),t},getDefault:function(e,t="unavailable"){return null==e?t:e}}},function(e,t,s){"use strict";const i="http://www.w3.org/2000/svg";e.exports={addPoint:function(e,t,s,n,a){let l=document.createElementNS(i,"circle");return l.setAttribute("r",n),l.setAttribute("cx",t),l.setAttribute("cy",s),void 0!==a&&l.setAttribute("class",a),e.appendChild(l),l},addCircle:function(e,t,s,n,a,l,r){let o=document.createElementNS(i,"circle");return o.setAttribute("r",n),o.setAttribute("cx",t),o.setAttribute("cy",s),o.setAttribute("fill",a),o.setAttribute("stroke",l),o.setAttribute("stroke-width",r),e.appendChild(o),o},addLine:function(e,t,s,n,a,l){let r=document.createElementNS(i,"line");return r.setAttribute("x1",t),r.setAttribute("y1",s),r.setAttribute("x2",n),r.setAttribute("y2",a),void 0!==l&&r.setAttribute("class",l),e.appendChild(r),r},addRect:function(e,t,s,n,a,l){let r=document.createElementNS(i,"rect");return r.setAttribute("x",t),r.setAttribute("y",s),r.setAttribute("width",n),r.setAttribute("height",a),void 0!==l&&r.setAttribute("class",l),e.appendChild(r),r},addText:function(e,t,s,n,a="start",l){let r=document.createElementNS(i,"text");return r.setAttribute("x",t),r.setAttribute("y",s),r.textContent=n,r.setAttribute("text-anchor",a),void 0!==l&&r.setAttribute("class",l),e.appendChild(r),r},addPolyline:function(e,t,s){let n=document.createElementNS(i,"polyline");n.setAttribute("points",t),void 0!==s&&n.setAttribute("class",s),e.appendChild(n)},removeElement:function(e){e.parentElement.removeChild(e)}}},function(e,t,s){let i,n,a,l,r,o,d,h,c=s(0),p=new Map;function u(e){return e.substring(0,3)}function m(e){return"eos/"===e.substring(0,4)||"par/"===e.substring(0,4)}function y(e){return e.substring(4)}e.exports={getRepresentatives:function(){return r},setMaterialData:function(e){i=e},getMaterialData:function(){return i},getCalculations:function(){return l},setCalculations:function(e){l=e.results,l.forEach((function(e,t){let s=c.getDefault(e.functional_type),i=c.getDefault(e.core_electron_treatment);this[t].functional_type=s,this[t].core_electron_treatment=i}),l),r=e.representatives;for(let e=0;e<l.length;e++)p.set(l[e].calc_id,l[e]);let t=r.electronic_band_structure,s=r.electronic_dos,i=r.thermodynamical_properties;h=void 0!==t||void 0!==s,d=void 0!==i},getCalc:function(e){return p.get(e)},getGroups:function(){return n},setGroups:function(e){let t=new Map(Object.entries(e.groups_eos)),s=new Map(Object.entries(e.groups_par));n=new Map,n.set("eos",t),n.set("par",s),a=new Set,t.forEach((e,t)=>{e.forEach(a.add,a)})},getGroupId:y,isGroup:m,getGroupType:u,getReprCalc:function(e){if(m(e)){let t=u(e),s=y(e);return n.get(t).get(s)[0]}return e},isInAnyGroup:function(e){return a.has(e)},isInAnyNotDisabledGroup:function(e){return a.has(e)},getGroupLeafId:function(e){let t=null;return n.forEach((s,i)=>{s.calculation_set.has(e)&&(t=i)}),t},isReady:function(e){return void 0!==i&&void 0!==o&&void 0!==l&&void 0!==n&&e===i.material_id},clear:function(){i=void 0,l=void 0,n=void 0,o=void 0},setIdealizedStructure:function(e){o=e},getIdealizedStructure:function(){return o},hasThermalData:function(e){return d},hasElecStructureData:function(e){return h}}},function(e,t,s){"use strict";let i=s(0),n=s(21);let a=document.querySelector("#info-tooltip"),l=document.querySelector("#tooltip-content"),r=[],o=null,d=!1,h=null;function c(){null!==o&&window.clearTimeout(o),o=window.setTimeout(e=>a.style.display="none",1e3)}let p=new n(i.IMAGE_DIR+"switch");function u(e){e.addEventListener("mouseover",m),e.addEventListener("mouseout",t=>{c(),e.style.cursor="inherit"}),e.className="info-sys-label"}function m(e){let t=e.target.getBoundingClientRect(),s=e.target.getAttribute("info-sys-data"),i=s.indexOf(".value");if(i>0){let e=h[s.split("-").join(" ").substring(0,i)];if(void 0===e.value_template){let t=e.values[s.split(":")[1]];l.innerHTML=v(t)}else{let t=e.value_template;t.text=y(t.text,s.split(":")[1]),t.link=y(t.link,s.split(":")[1]),l.innerHTML=v(t)}}else l.innerHTML=v(h[s.split("-").join(" ")]);a.style.visibility="hidden",a.style.display="block";let n=a.getBoundingClientRect(),r=n.width-t.width,d=t.left-r/2;d+n.width>window.innerWidth&&(d=window.innerWidth-n.width);let c=-window.pageYOffset;a.style.left=(d<0?5:d)+"px",a.style.top=t.top+t.height-c+"px",a.style.visibility="visible",window.clearTimeout(o),e.target.style.cursor="help"}function y(e,t){let s=e.indexOf("${"),i=e.indexOf("}");return s>=0&&i>=0&&i>s?e.substring(0,s)+t+e.substring(i+1):e}function v(e){let t="";return void 0===e.text||(t+=e.text),void 0!==e.link&&(void 0!==e.text&&(t+="<br>"),t+='<a href="'+e.link+'" target="_blank">More information</a>'),t}document.querySelector("#info-sys-switch-box").appendChild(p.element),p.setListener(e=>{d=!e,e?r.forEach(e=>{e.removeEventListener("mouseover",m),e.className=""}):(null===h&&i.serverReq("infosys.json",e=>h=JSON.parse(e.target.response)),r.forEach(u)),a.addEventListener("mouseover",e=>{window.clearTimeout(o)}),a.addEventListener("mouseout",e=>{c()})}),e.exports={addToInfoSystem:function(e){let t=e.querySelectorAll("span[info-sys-data]");for(let e=0;e<t.length;++e)r.push(t[e]);if(d)for(let e=0;e<t.length;++e)u(t[e])},addElementToInfoSystem:function(e,t){r.push(e),e.setAttribute("info-sys-data",t),d&&u(e)}}},function(e,t,s){"use strict";s(0);let i=document.querySelector("#loading-popup"),n=new Set;e.exports={show:function(e){if(!window.allowNewLoadPopup)return;n.add(e,!0);let t=i.getBoundingClientRect(),s=(window.innerWidth-t.width)/2,a=(window.innerHeight-t.height)/2;i.style.left=s+"px",i.style.top=a-100+"px",i.style.visibility="visible"},hide:function(e){n.delete(e),0===n.size&&(i.style.visibility="hidden")},reset:function(){n=new Set,i.style.visibility="hidden"}}},function(e,t,s){"use strict";let i=s(1);const n="http://www.w3.org/2000/svg";e.exports=class{constructor(e={left:20,right:0,top:10,bottom:20}){this.svg=document.createElementNS(n,"svg"),this.plotArea=document.createElementNS(n,"g"),this.svg.appendChild(this.plotArea),this.margins=e,this.parentElement=null,this.yLabelText=null,this.noDataGroup=null}attach(e,t,s){this.parentElement=e,this.parentElement.appendChild(this.svg),this.width=void 0!==t?t:this.parentElement.clientWidth,this.height=void 0!==s?s: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 null!==this.parentElement}setRangeAndLabels(e,t,s,i,n,a){this.xLabel=e,this.xMin=t,this.xMax=s,this.yLabel=i,this.yMin=n,this.yMax=a,this.xRel=this.plotRangeX/(this.xMax-this.xMin),this.yRel=this.plotRangeY/(this.yMax-this.yMin)}drawAxis(e=0,t=0,s=2){if(this.plotArea.setAttribute("transform","matrix(1 0 0 1 "+this.margins.left+" "+(this.height-this.margins.bottom)+")"),this.yLabelText=i.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)"),i.addText(this.plotArea,this.plotRangeX/2,this.margins.bottom-1,this.xLabel,"middle","axis-steps-big"),null!==e){let t=this.plotRangeX/e;for(let n=0;n<=e;n++)i.addLine(this.plotArea,t*n,0,t*n,4,1),i.addText(this.plotArea,t*n,14,+(t*n/this.xRel+this.xMin).toFixed(s),"middle","statisticsviewersteps")}if(null===t&&this.yMax>0&&this.yMin<0){let e=1;for(;this.yMax*e>this.yMin;){i.addLine(this.plotArea,0,this.transformY(this.yMax*e),-3,this.transformY(this.yMax*e),1);let t=this.yMax*e,n=Math.abs(t)>=1e4?t.toExponential(s):t.toFixed(s);i.addText(this.plotArea,-5,this.transformY(this.yMax*e)+3,n,"end","statisticsviewersteps"),e--}}if(null!==t){let e=this.plotRangeY/t;for(let n=0;n<=t;n++){i.addLine(this.plotArea,0,-e*n,-3,-e*n,1);let t=e*n/this.yRel+this.yMin;t=Math.abs(t)>=1e4?t.toExponential(s):t.toFixed(s),Math.abs(t)<.01&&(t=0),i.addText(this.plotArea,-5,-(e*n-3),t,"end","statisticsviewersteps")}}i.addLine(this.plotArea,0,0,this.plotRangeX+1,0,"main-axis"),i.addLine(this.plotArea,0,0,0,-(this.plotRangeY+1),"main-axis"),i.addLine(this.plotArea,this.plotRangeX,0,this.plotRangeX,-this.plotRangeY,"main-axis"),i.addLine(this.plotArea,0,-this.plotRangeY,this.plotRangeX,-this.plotRangeY,"main-axis")}clear(){this.svg.removeChild(this.plotArea),this.plotArea=document.createElementNS(n,"g"),this.svg.appendChild(this.plotArea),this.plotArea.setAttribute("transform","matrix(1 0 0 1 "+this.margins.left+" "+(this.height-this.margins.bottom)+")"),null!==this.yLabelText&&(this.svg.removeChild(this.yLabelText),this.yLabelText=null),null!==this.noDataGroup&&(this.svg.removeChild(this.noDataGroup),this.noDataGroup=null)}setNoData(){this.clear(),null===this.noDataGroup&&(this.noDataGroup=document.createElementNS(n,"g"),this.svg.appendChild(this.noDataGroup),i.addRect(this.noDataGroup,0,0,this.width,this.height),this.noDataGroup.setAttribute("fill","#EEE"),i.addText(this.noDataGroup,this.width/2,this.height/2+10,"NO DATA","middle","nodata"))}transformY(e){return-this.yRel*(e-this.yMin)}}},function(e,t,s){"use strict";let i=s(0),n=s(2);e.exports=class{constructor(e){this.element=document.createElement("div"),this.element.setAttribute("id",e),this.loadedMatId=null,this.gotoResultsListener=null,this.gotoOverviewListener=null,this.element.innerHTML='<div class="material-title"></div>',this.element.style.display="none"}attachAndSetEvents(e){e.appendChild(this.element),this.materialTitle=this.element.querySelector(".material-title")}attachNavTree(e){e.attach(this.navTreeWrapper)}setVisible(){this.element.style.display="block"}load(){let e=n.getMaterialData().material_id;this.loadedMatId!==e&&(this.loadedMatId=e,this.setMaterialData())}setMaterialData(){this.materialTitle.innerHTML=i.getMaterialTitle(n.getMaterialData())}updateCalcs(e){}updateMarkedCalc(e){}}},function(e,t,s){"use strict";let i=s(0),n=s(2);function a(e,t){if(e.className.indexOf("material-l")>=0)return;let s=e.parentElement;for(let t=0;t<s.children.length;t++){let i=s.children[t++];if(i!==e&&i.className.indexOf("selected")<0)return}let i=s.previousElementSibling;if(t)i.className+=" node-selected";else{let e=i.className.indexOf(" node-selected");i.className=i.className.substring(0,e)}a(i,t)}e.exports=class{constructor(){this.selectedCalcs=new Set,this.element=document.createElement("div"),this.element.setAttribute("id","navigation-tree"),this.parentElement=null,this.markedNode=null,this._events()}attach(e){null!==this.parentElement&&this.parentElement.removeChild(this.element),this.parentElement=e,this.parentElement.appendChild(this.element)}build(e,t){function s(e,t,s,i=0){let n="node-"+(s?"unfolded":"folded"),a=document.createElement("div");a.className=e;let l=document.createElement("span");l.className=n,a.appendChild(l);let r=document.createElement("span");r.className="node-checkbox",a.appendChild(r);let o=document.createElement("span");if(o.className="node-label",o.textContent=t,a.appendChild(o),0!==i){let e=document.createElement("span");e.className="node-counter",e.textContent="("+i+")",a.appendChild(e)}return a}function a(e,t,s,n,a){let l=document.createElement("div");l.className="calc-l",l.setAttribute("data-calc-id",e);let r=document.createElement("span");l.appendChild(r);let d=document.createElement("span");d.className="node-checkbox",l.appendChild(d);let h=document.createElement("span");h.className="node-label",n&&h.appendChild(o),h.textContent=t+s+" ",h.appendChild(a),l.appendChild(h);let c=document.createElement("div");c.id="icon-container",c.style.float="right",c.style.padding="1px 10px 0 0",c.style.display="none";let p=document.createElement("img");return p.setAttribute("src",i.IMAGE_DIR+"next.png"),c.appendChild(p),l.appendChild(c),l}function l(e){let t=document.createElement("div");t.className="calc-graph-aval";let s=document.createElement("span");if(e.has_band_structure){let e=document.createElement("span");e.className="tooltip",e.textContent="B";let t=document.createElement("span");t.className="tooltiptext",t.textContent="Band structure",e.appendChild(t),s.appendChild(e)}if(e.has_dos){let e=document.createElement("span");e.className="tooltip",e.textContent="D";let t=document.createElement("span");t.className="tooltiptext",t.textContent="Density of states",e.appendChild(t),s.appendChild(e)}if(e.has_fermi_surface){let e=document.createElement("span");e.className="tooltip",e.textContent="F";let t=document.createElement("span");t.className="tooltiptext",t.textContent="Fermi surface",e.appendChild(t),s.appendChild(e)}if(e.has_thermal_properties){let e=document.createElement("span");e.className="tooltip",e.textContent="T";let t=document.createElement("span");t.className="tooltiptext",t.textContent="Phonons",e.appendChild(t),s.appendChild(e)}return t.appendChild(s),t}this.selectedCalcs.clear(),this.markedNode=null,this.element.innerHTML="",this.calcsInGroups=n.getGroups();let r=n.getCalculations(),o=document.createElement("img");o.className="folder-icon",o.src=i.IMAGE_DIR+"folder.png";let d=function(e,t,s){let i=new Map;function a(e,t){e.forEach((e,s)=>{let a=n.getCalc(e[0]),l=a.code_name.trim(),r=a.functional_type;if("exciting"===l)if(i.has(r)){let e=i.get(r);e.has(l)?e.get(l).push(t+s):e.set(l,[t+s])}else if("unavailable"!==r){let e=new Map;e.set(l,[t+s]),i.set(r,e)}})}function l(e,t){let s=e.code_name.trim(),n=e.functional_type;if(i.has(n)){let e=i.get(n);e.has(s)?e.get(s).push(t):e.set(s,[t])}else{let e=new Map;e.set(s,[t]),i.set(n,e)}}return"structure"===s&&(a(t.get("eos"),"eos/"),a(t.get("par"),"par/")),e.forEach(e=>{n.isInAnyNotDisabledGroup(e.calc_id)||("electronic"===s?(e.has_dos||e.has_band_structure)&&l(e,e.calc_id):"thermal"===s?e.has_thermal_properties&&l(e,e.calc_id):l(e,e.calc_id))}),i}(r,this.calcsInGroups,t),h=document.createElement("div");h.appendChild(s("material-l",e,!0));let c=document.createElement("div");h.appendChild(c),d.forEach((e,t)=>{c.appendChild(s("functional-l",t,!0));let r=document.createElement("div");c.appendChild(r),e.forEach((e,t)=>{r.appendChild(s("code-l",t,!1,e.length));let o=document.createElement("div");r.appendChild(o),o.style.display="none";for(var d=0;d<e.length;d++){let t=e[d],s="",r=i.getShortCode(t),h=n.isGroup(t);if(h){let e,i=n.getGroupType(t),a=t.substring(4);e=this.calcsInGroups.get(i).get(a),s="("+e.length+")"}let c=n.getReprCalc(t),p=a(t,r,s,h,l(n.getCalc(c)));o.appendChild(p),o.appendChild(document.createElement("div"))}})}),this.element.appendChild(h),this.showCalcsGraphDataAvailability("structure"!==t)}selectAll(e){let t=this.element.children[0].children[0];this._recursiveNodeSelection(t,!0),a(t,!0)}getMarkedLeaf(){return null===this.markedNode?null:this.markedNode.getAttribute("data-calc-id")}setMarkedLeafIfNoneMarked(e){if(null===this.getMarkedLeaf())if(null===e)this._markFirstSelectedNode();else{let t=this.element.querySelector('div[data-calc-id="'+e+'"]');this._setMarkedCalc(t)}}_events(){this.element.addEventListener("click",e=>{let t=e.target.className;if(t.indexOf("folded")>=0)this._foldTreeNode(e.target);else if(t.indexOf("node-checkbox")>=0){let t=e.target.parentElement.className.indexOf("selected")<0;this._recursiveNodeSelection(e.target.parentElement,t),a(e.target.parentElement,t),this.treeSelectionListener(this.selectedCalcs),this._keepCalcMarked(t)}else t.indexOf("node-label")>=0&&"calc-l node-selected"===e.target.parentElement.className&&this._setMarkedCalc(e.target.parentElement)})}_foldTreeNode(e){let t=e.parentElement.nextElementSibling;e.className.indexOf("-folded")>=0?(e.className=e.className.replace("folded","unfolded"),t.style.display="block"):(e.className=e.className.replace("unfolded","folded"),t.style.display="none")}_recursiveNodeSelection(e,t){e.children[1];if(t)e.className+=" node-selected";else{let t=e.className.indexOf(" node-selected");e.className=e.className.substring(0,t)}if(e.className.indexOf("calc-l")>=0){let s=e.getAttribute("data-calc-id");t?this.selectedCalcs.add(s):this.selectedCalcs.delete(s)}else{let s=e.nextElementSibling;for(let e=0;e<s.children.length;e++)this._recursiveNodeSelection(s.children[e++],t)}}setTreeSelectionListener(e){this.treeSelectionListener=e}setLeafMarkedListener(e){this.leafMarkedListener=e}getTreeSelectedCalcs(){return this.selectedCalcs}_keepCalcMarked(e){if(e&&null===this.markedNode)this._markFirstSelectedNode();else if(!e){let e=parseInt(this.markedNode.getAttribute("data-calc-id"));0===this.selectedCalcs.size?(this.markedNode.className=this.markedNode.className.replace("-marked",""),this.markedNode=null,this.leafMarkedListener(null)):this.selectedCalcs.has(e)||this._markFirstSelectedNode()}}_markFirstSelectedNode(){let e=this.element.getElementsByClassName("calc-l");for(var t=0;t<e.length;t++)if(e[t].className.indexOf("node-selected")>=0)return void this._setMarkedCalc(e[t])}_setMarkedCalc(e){if(null!==this.markedNode){this.markedNode.className=this.markedNode.className.replace("-marked",""),this.markedNode.querySelector("#icon-container").style.display="none";let e=this.markedNode.querySelector(".folder-icon");null!==e&&(e.src=i.IMAGE_DIR+"folder.png")}e.className+="-marked";let t=e.querySelector(".folder-icon");null!==t&&(t.src=i.IMAGE_DIR+"folder-sel.png"),e.querySelector("#icon-container").style.display="block",this.markedNode=e;let s=e.parentElement.previousElementSibling.firstElementChild;"node-folded"===s.className&&(s.className="node-unfolded",s.parentElement.nextElementSibling.style.display="block"),void 0!==this.leafMarkedListener&&this.leafMarkedListener(e.getAttribute("data-calc-id"))}showCalcsGraphDataAvailability(e){let t=this.element.getElementsByClassName("calc-graph-aval");for(var s=0;s<t.length;s++)t[s].style.display=e?"inline":"none"}setHeight(e){this.element.style.height=e+"px"}}},function(e,t,s){"use strict";let i=s(9),n=s(1);s(0);function a(e){return"Gamma"===e||"G"===e?"Γ":e}e.exports=class extends i{constructor(){super({left:55,right:5,top:0,bottom:30}),this.phononMode=!1,this.factor=1602176565e-28}setPhononMode(){this.phononMode=!0,this.factor=1986445681679957e-38,this.outOfRangeColorActivated=!1}attach(e,t,s){super.attach(e,t,s)}getTopAndLowestPoints(e){let t=e[0].band_energies[0][0][0],s=e[0].band_energies[0][0][0];for(let i=0;i<e.length;i++){for(let n=0;n<e[i].band_energies[0].length;n++){let a=Math.max.apply(null,e[i].band_energies[0][n]);a>t&&(t=a),a=Math.min.apply(null,e[i].band_energies[0][n]),a<s&&(s=a)}if(2===e[i].band_energies.length)for(let n=0;n<e[i].band_energies[1].length;n++){let a=Math.max.apply(null,e[i].band_energies[1][n]);a>t&&(t=a),a=Math.min.apply(null,e[i].band_energies[1][n]),a<s&&(s=a)}}return[(s-this.energyOffset)/this.factor,(t-this.energyOffset)/this.factor]}drawKPointLabel(e,t){n.addText(this.axisGroup,e*this.xRel,16,t,"middle","steps")}setBandStructureData(e){let t=e.section_k_band_segment;if(this.bandGapData=void 0,this.energyOffset=0,void 0!==e.section_band_gap){let t=1/0,s=0;for(let i=0;i<e.section_band_gap.length;++i){let n=e.section_band_gap[i].value;n<t&&(t=n,s=i)}0!=t&&(this.bandGapData=e.section_band_gap[s],this.bandGapData.cbmDistances=[],this.bandGapData.vbmDistances=[],this.energyOffset=this.bandGapData.valence_band_max_energy)}this.bandsDataSpin1=[],this.bandsDataSpin2=[],this.segmentLimitsX=[],this._reset();let s=this.getTopAndLowestPoints(t),i=s[0],n=s[1];this.phononMode?this.setAxisRangeAndLabels("",0,1,"Frequency (cm⁻¹)",-50,320,i,n,100):this.setAxisRangeAndLabels("",0,1,"Energy (eV)",-6,11,i,n,5);let l=0,r=null,o=!1,d=t[t.length-1].k_path_distances,h=d[d.length-1];for(let e=0;e<t.length;e++){let s=t[e],i=s.band_segm_labels,n=s.band_energies[0],d=s.band_energies[1],c=s.k_path_distances;this.bandsDataSpin1.push([]),this.bandsDataSpin2.push([]);let p=c[c.length-1]-c[0];this.segmentLimitsX.push(l/h),null!==i&&(null!==r&&r!==i[0]?this.drawKPointLabel(l/h,a(r)+"|"+a(i[0])):this.drawKPointLabel(l/h,a(i[0])),e===t.length-1&&this.drawKPointLabel(1,a(i[1])),r=i[1]);for(let t=0;t<c.length;t++){let s=c[t]/h;for(let i=0;i<n[t].length;i++){0===t&&(this.bandsDataSpin1[e][i]=[]);let a=(n[t][i]-this.energyOffset)/this.factor;this.bandsDataSpin1[e][i].push({x:s,y:a}),!o&&a>1e4&&(o=!0)}if(void 0!==d)for(let i=0;i<d[t].length;i++){0===t&&(this.bandsDataSpin2[e][i]=[]);let n=(d[t][i]-this.energyOffset)/this.factor;this.bandsDataSpin2[e][i].push({x:s,y:n}),!o&&n>1e4&&(o=!0)}}l+=p}if(o)throw"Plotter Data Overflow: Probably the energy data is not in correct units";this.repaint()}repaintData(e,t){let s;this.segmentLimitsX.forEach(s=>{let i=this.transformY(e),a=this.transformY(t);this.phononMode&&(i+=200,a-=200),n.addLine(this.plotContent,s*this.xRel,i,s*this.xRel,a,"segment")});for(var i=0;i<this.bandsDataSpin1.length;i++)for(var a=0;a<this.bandsDataSpin1[i].length;a++){s="";for(var l=0;l<this.bandsDataSpin1[i][a].length;l++)s+=" "+this.xRel*this.bandsDataSpin1[i][a][l].x+" "+this.transformY(this.bandsDataSpin1[i][a][l].y);n.addPolyline(this.plotContent,s,"plotSpin1")}if(this.bandsDataSpin2.length>0)for(i=0;i<this.bandsDataSpin2.length;i++)for(a=0;a<this.bandsDataSpin2[i].length;a++){s="";for(l=0;l<this.bandsDataSpin2[i][a].length;l++)s+=" "+this.xRel*this.bandsDataSpin2[i][a][l].x+" "+this.transformY(this.bandsDataSpin2[i][a][l].y);n.addPolyline(this.plotContent,s,"plotSpin2")}void 0!==this.bandGapData&&(this.bandGapData.cbmDistances.forEach(e=>{let t=this.xRel*e,s=this.transformY((this.bandGapData.conduction_band_min_energy-this.energyOffset)/this.factor);n.addPoint(this.plotContent,t,s,3,"cbm-vbm-points"),n.addText(this.plotContent,t+4,s-6,"CBM")}),this.bandGapData.vbmDistances.forEach(e=>{let t=this.xRel*e,s=this.transformY((this.bandGapData.valence_band_max_energy-this.energyOffset)/this.factor);n.addPoint(this.plotContent,t,s,3,"cbm-vbm-points"),n.addText(this.plotContent,t+4,s+14,"VBM")}))}}},function(e,t,s){"use strict";let i=s(1);const n="http://www.w3.org/2000/svg";e.exports=class{constructor(e={left:20,right:0,top:0,bottom:20}){this.margins=e,this.svg=document.createElementNS(n,"svg"),this.parentElement=null,this.plotContent=null,this.axisGroup=null,this.yAxisLabelsGroup=null,this.yLabelText=null,this.noDataGroup=null,this.yZoom=1,this.yOffset=0,this.repaintListener=null,this.nodataLabel=null,this.outOfRangeColorActivated=!0}attach(e,t,s){this.parentElement=e,this.parentElement.appendChild(this.svg),this.width=void 0!==t?t:this.parentElement.clientWidth,this.height=void 0!==s?s: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,this.yAxisArea=document.createElementNS(n,"svg"),this.svg.appendChild(this.yAxisArea);let a=this.margins.left;this.plotAreaHeight=this.height-this.margins.bottom-this.margins.top,this.yAxisArea.setAttribute("width",a),this.yAxisArea.setAttribute("height",this.plotAreaHeight),this.yAxisArea.setAttribute("x",0),this.yAxisArea.setAttribute("y",0),this.plotArea=document.createElementNS(n,"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),this.plotAreaBg=i.addRect(this.plotArea,0,0,this.plotAreaWidth,this.plotAreaHeight),this.plotAreaBg.setAttribute("class","moveable-plot"),this.plotAreaBg.setAttribute("opacity",0),this._events()}isAttached(){return null!==this.parentElement}setAxisRangeAndLabels(e,t,s,n,a,l,r,o,d,h=2){this.xLabel=e,this.xMin=t,this.xMax=s,this.yMinInit=a,this.yMaxInit=l,this.yMin=r,this.yMax=o,this.yLabelGapInit=d,this.xRel=this.plotRangeX/(this.xMax-this.xMin),this.yRel=this.plotRangeY/(this.yMaxInit-this.yMinInit),this._resetAxisGroup(),i.addLine(this.axisGroup,0,0,this.plotRangeX,0,"main-axis"),i.addLine(this.axisGroup,0,0,0,-this.plotRangeY,"main-axis"),i.addLine(this.axisGroup,this.plotRangeX,0,this.plotRangeX,-this.plotRangeY,"main-axis"),i.addLine(this.axisGroup,0,-this.plotRangeY,this.plotRangeX,-this.plotRangeY,"main-axis"),null!==n&&(this.yLabelText=i.addText(this.svg,0,0,n,"middle","axis-steps-big"),this.yLabelText.setAttribute("transform","translate(15,"+(this.plotRangeY/2+this.margins.top)+") rotate(-90)")),null!==e&&i.addText(this.axisGroup,this.plotRangeX/2,this.margins.bottom-12,this.xLabel,"middle","axis-steps-big"),this._resetYAxisLabelGroup(),this.precalculation_1=this.plotAreaHeight+this.yMinInit*this.yRel,this.precalculation_2=this.precalculation_1}_events(){let e;this.plotArea.addEventListener("wheel",e=>{e.preventDefault(),e.deltaY>0&&this.yZoom>.5?this.yZoom-=.2:e.deltaY<0&&this.yZoom<2&&(this.yZoom+=.2),this.repaint(),null!==this.repaintListener&&this.repaintListener(this.yZoom,this.yOffset)}),this.plotArea.addEventListener("mousedown",t=>{t.preventDefault(),e=t.clientY+this.yOffset,this.plotArea.addEventListener("mousemove",s),this.plotArea.addEventListener("mouseup",e=>{this.plotArea.removeEventListener("mousemove",s)}),this.plotArea.addEventListener("mouseout",e=>{this.plotArea.removeEventListener("mousemove",s)})});let t=this;function s(s){t.yOffset=e-s.clientY,t.precalculation_2=t.precalculation_1-t.yOffset,t.repaint(),null!==t.repaintListener&&t.repaintListener(t.yZoom,t.yOffset)}}setYZoomAndOffset(e,t){this.yZoom=e,this.yOffset=t,this.precalculation_2=this.precalculation_1-this.yOffset}setExternalYAxisMax(e){this.externalYAxisMax=e}getYAxisMax(){return this.yAxisMax}repaint(){let e;this._resetYAxisLabelGroup(),e=this.yZoom>1?this.yLabelGapInit/5:this.yLabelGapInit;let t=Math.floor(this.yMin/e)*e,s=Math.ceil(this.yMax/e)*e;if(this.yAxisMax=s,void 0!==this.externalYAxisMax&&(s=this.externalYAxisMax),null!==this.yLabelText)for(let n=t;n<s+1;n+=e)i.addLine(this.yAxisLabelsGroup,this.margins.left,this.transformY(n),this.margins.left-3,this.transformY(n)),i.addText(this.yAxisLabelsGroup,this.margins.left-5,this.transformY(n)+5,n,"end","axis-steps");if(this._resetPlotContent(),this.outOfRangeColorActivated){i.addRect(this.plotContent,0,this.transformY(this.yMax)-2*this.plotAreaHeight,this.plotAreaWidth,2*this.plotAreaHeight).setAttribute("class","out-of-range"),i.addRect(this.plotContent,0,this.transformY(this.yMin),this.plotAreaWidth,2*this.plotAreaHeight).setAttribute("class","out-of-range")}i.addLine(this.plotContent,0,this.transformY(0),this.plotRangeX,this.transformY(0),"zeroline"),this.repaintData(t,s),this.plotArea.removeChild(this.plotAreaBg),this.plotArea.appendChild(this.plotAreaBg)}setRepaintListener(e){this.repaintListener=e}transformY(e){return-e*this.yZoom*this.yRel+this.precalculation_2}_resetPlotContent(){null!==this.plotContent&&this.plotArea.removeChild(this.plotContent),this.plotContent=document.createElementNS(n,"g"),this.plotArea.appendChild(this.plotContent)}_resetAxisGroup(){null!==this.axisGroup&&this.svg.removeChild(this.axisGroup),this.axisGroup=document.createElementNS(n,"g"),this.svg.appendChild(this.axisGroup),this.axisGroup.setAttribute("transform","matrix(1 0 0 1 "+this.margins.left+" "+(this.height-this.margins.bottom)+")")}_resetYAxisLabelGroup(){null!==this.yLabelText&&(null!==this.yAxisLabelsGroup&&this.yAxisArea.removeChild(this.yAxisLabelsGroup),this.yAxisLabelsGroup=document.createElementNS(n,"g"),this.yAxisArea.appendChild(this.yAxisLabelsGroup))}_reset(){this.yZoom=1,this.yOffset=0,this._resetPlotContent(),null!==this.yLabelText&&(this.svg.removeChild(this.yLabelText),this.yLabelText=null),null!==this.noDataGroup&&(this.svg.removeChild(this.noDataGroup),this.noDataGroup=null)}setNoData(){this._resetYAxisLabelGroup(),this._resetPlotContent(),this._resetAxisGroup(),null===this.noDataGroup&&(this.noDataGroup=document.createElementNS(n,"g"),this.svg.appendChild(this.noDataGroup),i.addRect(this.noDataGroup,0,0,this.width,this.height),this.noDataGroup.setAttribute("fill","#EEE"),i.addText(this.noDataGroup,this.width/2,this.height/2+10,"NO DATA","middle","nodata"))}}},function(e,t,s){let i=s(1),n=s(0),a=(s(7),s(20)),l=s(23),r=s(25),o=s(27),d=s(29),h=s(32),c=s(2),p=s(4),u={eStruct:null,thermalProps:null};class m{constructor(e){this.hostElement=e;this.viewer=new matviewer.StructureViewer(e,{view:{autoResize:!1,autoFit:!0,fitMargin:.5},structure:{createLegend:!1,showLegend:!1,radiusScale:.7,bondScale:1.5,showCopies:!0,viewCenter:"COP"}}),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=`\n\n <div style="float: left; padding-right: 12px" >\n <input type="checkbox" class="show-axis" checked> Show axis\n </div>\n\n <div style="float: left; padding-right: 18px" >\n <input type="checkbox" class="show-bonds" checked> Show bonds\n </div>\n\n <div style="float: left; position:relative;" >\n <img class="view-reset" style="cursor: pointer;" height="18px"\n src="${n.IMAGE_DIR}reset.svg" />\n <div class="view-reset-tooltip" > Reset view </div>\n </div>\n\n <div class="vr-download" style="float: right"> </div>\n\n <div style="clear: both;"></div>\n `,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");let t=this.hostElement.querySelector(".view-reset");t.addEventListener("click",e=>this.viewer.controls.reset()),t.addEventListener("mouseover",e=>{this.hostElement.querySelector(".view-reset-tooltip").style.display="block"}),t.addEventListener("mouseout",e=>{this.hostElement.querySelector(".view-reset-tooltip").style.display="none"})}load(e){e=this.getCellDataForViewer(e),this.viewer.load(e),this.createElementLegend()}resizeCanvasToHostElement(){this.viewer.resizeCanvasToHostElement()}setMaterialId(e){}changeHostElement(e){this.hostElement!==e&&(this.hostElement.removeChild(this.legendElement),this.hostElement.removeChild(this.footerElement),this.hostElement=e,this.viewer.changeHostElement(e),this.hostElement.appendChild(this.legendElement),this.hostElement.appendChild(this.footerElement)),this.viewer.resizeCanvasToHostElement(),this.viewer.fitToCanvas(),this.viewer.render()}getCellDataForViewer(e){let t={};return t.pbc=e.periodicity,t.chemicalSymbols=e.atom_labels,t.cell=n.convert2d(e.lattice_vectors,1e10),t.scaledPositions=e.atom_positions,t}createElementLegend(){this.labelsContainer.innerHTML="";let e=this.viewer.elements,t=[];for(let s in e)e.hasOwnProperty(s)&&t.push([s,e[s][0],e[s][1]]);t.sort((function(e,t){return e[0]<t[0]?-1:e[0]>t[0]?1:0}));let s=document.createElementNS("http://www.w3.org/2000/svg","svg");s.setAttribute("width",50),s.setAttribute("height",25*t.length),this.labelsContainer.appendChild(s);for(let e=0;e<t.length;++e){let n=t[e][0],a=t[e][1].toString(16),l=6-a.length;a="#"+Array(l+1).join("0")+a,i.addCircle(s,10,25*e+12,8,a,"#777",1),i.addText(s,24,25*e+18,n,"start","structure-viewer-legend-labels")}}}e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","material-module"),this.overview=new a,this.overview.attachAndSetEvents(this.element),this.structureViewer=new m(this.overview.vizBox),this.structureDetails=new l,this.structureDetails.attachAndSetEvents(this.element),this.electronicStructDetails=new r,this.electronicStructDetails.attachAndSetEvents(this.element),this.methodologyDetails=new o,this.methodologyDetails.attachAndSetEvents(this.element),this.thermalDetails=new d,this.thermalDetails.attachAndSetEvents(this.element),this.elasticDetails=new h,this.elasticDetails.attachAndSetEvents(this.element),this.currentDetailView=null}setMaterialView(e){this._loadMaterial(e.material_id,e.view)}getCurrentPageStatus(){let e=null;return null===this.currentDetailView&&(e=this.overview.getEStructChosenCalcs()),{pageId:this.currentDetailViewId,eStructCalcs:e}}_setView(e){null===this.currentDetailView?this.overview.element.style.display="none":this.currentDetailView.element.style.display="none",void 0===e?(this.currentDetailView=null,this.currentDetailViewId=null,this.overview.setVisible()):(this.currentDetailViewId=e,this._setDetailView(n.MAT_VIEW[e]))}_setCellViewer(e){this.structureViewer.changeHostElement(e)}_setDetailView(e){e===n.MAT_VIEW.structure?this.currentDetailView=this.structureDetails:e===n.MAT_VIEW.electronicstruct?this.currentDetailView=this.electronicStructDetails:e===n.MAT_VIEW.methodology?this.currentDetailView=this.methodologyDetails:e===n.MAT_VIEW.thermalprops&&(this.currentDetailView=this.thermalDetails),this.currentDetailView.setVisible()}_loadMaterial(e,t){this._setView(t);let s=()=>{let e=c.getMaterialData();c.getIdealizedStructure();if(null!==this.currentDetailView)this.currentDetailView.load(),t===n.MAT_VIEW.structure&&this._setCellViewer(this.structureDetails.vizBox),t===n.MAT_VIEW.methodology&&this.methodologyDetails.updateSelection();else{document.querySelector("title").innerHTML="NOMAD Encyclopedia - "+n.getMaterialTitle(e,!1),this.overview.setMaterialData();null===e.material_name?e.formula:e.material_name;this.overview.setCalcsData(u),this._setCellViewer(this.overview.vizBox)}},i=()=>{let t=c.isReady(e);return t&&s(),t};i()||(c.clear(),p.reset(),this.structureViewer.axisCheckbox.checked=!0,this.structureViewer.bondsCheckbox.checked=!0,document.getElementById("methodology-ov").style.visibility="hidden",document.getElementById("structure-ov").style.visibility="hidden",document.getElementById("e-structure-ov").style.display="none",document.getElementById("e-structure-ov").style.visibility="hidden",document.getElementById("thermal-props-ov").style.visibility="hidden",p.show("load_basic"),n.serverReq(n.getMaterialURL(e),e=>{let t=JSON.parse(e.target.response);c.setMaterialData(t),n.materialId=t.material_id,i(),p.hide("load_basic")}),p.show("load_calculations"),n.serverReq(n.getMaterialXsURL("calculations",e),t=>{let s=JSON.parse(t.target.response),a=s.representatives.idealized_structure;c.setCalculations(s);let l=JSON.stringify({properties:["idealized_structure"]});p.show("load_idealized"),n.serverReqPOST(n.getMaterialCalcURL(e,a),l,e=>{let t=JSON.parse(e.target.response).idealized_structure;c.setIdealizedStructure(t),this.structureViewer.load(t),document.getElementById("structure-ov").style.visibility="visible",document.getElementById("methodology-ov").style.visibility="visible",i(),p.hide("load_idealized")}),p.hide("load_calculations")}),p.show("load_groups"),n.serverReq(n.getMaterialXsURL("groups",e),e=>{let t=JSON.parse(e.target.response);c.setGroups(t),i(),p.hide("load_groups")}))}}},function(e,t,s){"use strict";let i=s(9),n=s(1),a=s(0);e.exports=class extends i{constructor(e){super(e)}attach(e,t,s){super.attach(e,t,s)}setPoints(e,t){this.pointsSpin1=[],this.pointsSpin2=[],this._reset();let s=e.dos_values[0],i=null;2===e.dos_values.length&&(i=e.dos_values[1]);let l=e.dos_energies,r=[],o=[];for(var d=0;d<l.length;d++){let e=l[d]/1602176565e-28,t=1602176565e-28*s[d];if(r.push(t),o.push(e),this.pointsSpin1.push({x:t,y:e}),null!==i){let t=1602176565e-28*i[d];this.pointsSpin2.push({x:t,y:e}),r.push(t)}}let h=Math.max.apply(null,r),c=Math.max.apply(null,o),p=Math.min.apply(null,o),u=a.generateDiagramSteps(h,3),m=u[0];u[1];this.setAxisRangeAndLabels(null,0,m[m.length-1],"Energy (eV)",-6,11,p,c,5),n.addText(this.axisGroup,this.plotRangeX/2,this.margins.bottom,"DOS (states/eV/cell)","middle","axis-steps-big");for(let e=0;e<m.length;e++){let t=this.plotRangeX*m[e]/m[m.length-1];n.addLine(this.axisGroup,t,0,t,3,1);let s=m[e];s=Math.abs(s)>=1e4||Math.abs(s)<=1e-4?s.toExponential(1):parseFloat(s.toFixed(3)),n.addText(this.axisGroup,t,13,0===e?"0":s,"middle","axis-steps-smaller")}this.repaint()}repaintData(){let e="";for(var t=0;t<this.pointsSpin1.length;t++)e+=" "+this.xRel*this.pointsSpin1[t].x+" "+this.transformY(this.pointsSpin1[t].y);n.addPolyline(this.plotContent,e,"plotSpin1"),e="";for(t=0;t<this.pointsSpin2.length;t++)e+=" "+this.xRel*this.pointsSpin2[t].x+" "+this.transformY(this.pointsSpin2[t].y);n.addPolyline(this.plotContent,e,"plotSpin2")}setYAxisLabelsVisibility(e){null!==this.yAxisLabelsGroup&&(this.yAxisLabelsGroup.style.visibility=e?"visible":"hidden")}}},function(e,t,s){"use strict";let i=s(1),n=s(5);e.exports=class extends n{constructor(){super({left:50,right:16,top:10,bottom:32}),this.tooltip}setData(e,t){this.clear();let s,n=[],a=[];for(let s=0;s<e.length;++s){let i=e[s],l=t[s];null!==i&null!==l&&(n.push(i),a.push(l))}for(let t=0;t<e.length;++t){if(e[t]>600){s=t-1;break}}void 0===s&&(s=e.length-1),e=e.slice(0,s+1),t=t.slice(0,s+1);let l=Math.max.apply(null,t),r=200*Math.ceil(l/200),o=e[s],d=50*Math.ceil(o/50);this.setRangeAndLabels("T (K)",0,d,"Cv (J/K/kg)",0,r),this.drawAxis(4,4,0);let h="";e.forEach((e,s)=>{let i=t[s];h+=" "+this.xRel*e+" -"+this.yRel*(i-this.yMin)}),i.addPolyline(this.plotArea,h,"plotSpin1")}}},function(e,t){e.exports=class{constructor(e,t){this.first=!0,this.last=!1,this.element=document.createElement("div"),this.element.className=e,void 0!==t&&(this.element.style.width=t),this.element.innerHTML='\n <div class="prev-sel-btn" style="float: left; width: 20%;">\n <div style="padding-left: 10%;">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.069 11.872" width="20px">\n <path transform="scale(0.7) translate(-346.291 -664.481)"\n 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" />\n </svg>\n </div>\n </div>\n <div class="calc-sel-text" style="float: left; width: 60%;">\n NOT Calculation\n </div>\n <div class="next-sel-btn" style="float: right; width: 20%;">\n <div style="padding-right: 10%;">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="-10.069 -11.872 10.069 11.872" width="20px">\n <g transform="rotate(180) scale(0.7)">\n <path d="M10.069,1.544,8.525,0,0,8.513,8.493,16.96l1.544-1.544-6.8-6.9Z"/>\n </g>\n </svg>\n </div>\n </div>\n <div style="clear: both;"></div>\n ',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(),this.first||(this.first=this.prevListener(),this.last=!1,this._styleButtons())}),this.nextBtn.addEventListener("click",e=>{e.preventDefault(),this.last||(this.first=!1,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(e){this.prevListener=e}setNextListener(e){this.nextListener=e}setState(e,t,s){this.calcSelectorTxt.innerHTML=e,this.first=t,this.last=s,this._styleButtons()}}},function(e,t,s){"use strict";let i=s(1),n=s(5);class a extends n{constructor(){super({left:54,right:20,top:20,bottom:30})}drawBars(e,t,s,n,a,l){for(let r=0;r<e.length;++r){let o,d=(t[r]-s)*this.xRel,h=e[r]*this.yRel;o=0==a?(n-s)/l*this.xRel:a*this.xRel,i.addRect(this.plotArea,d,-h,o,h,"bar")}}}e.exports=class{constructor(){this.freqGraph=new a}attach(e,t,s){this.freqGraph.attach(e,t,s)}drawPoints(e,t,s){let i,n,a,l=e.occurrences,r=e.values,o=Math.max(...l);if(1==r.length){let e=r[0];0!==e?(i=.9*e,n=1.1*e+.2*e/s):(i=0,n=e+1),a=0}else i=r[0],a=r[1]-r[0],n=r[r.length-1]+a;this.freqGraph.setRangeAndLabels(t,i,n,"Occurrence",0,o),this.freqGraph.drawAxis(n===i?null:5,2,i>1e3?0:2),this.freqGraph.drawBars(l,r,i,n,a,s)}clear(){this.freqGraph.clear()}}},function(e,t,s){"use strict";let i,n,a,l,r,o=document.querySelector("body"),d=[],h=[],c=[],p=!1;function u(e,t){let s=document.createElement("div");return s.setAttribute("class","user-guidance"),s.innerHTML='<img src="img/tip'+e+'.png" width="'+t+'px" />',s.style.position="absolute",s.style.display="none",o.appendChild(s),s}function m(e){if(8===e&&!p)return;if(4===e&&!p)return;let t=h[e].domTarget.getBoundingClientRect();d[e].style.top=t.top+h[e].top+window.pageYOffset+"px",d[e].style.left=t.left+h[e].left+"px",d[e].style.display="block"}function y(e){d[e].style.display="none",localStorage.setItem("tip"+e,"off"),c[e]="off"}function v(e){switch(e.preventDefault(),e.target.style.display="none",i){case 2:i=5;break;case 6:i=8;break;default:i++}g(i),m(i)}function f(e,t){null===c[e]&&(t?m(e):d[e].style.display="none")}function g(e){i=e,localStorage.setItem("currentTip",e)}e.exports={init:function(e,t,s,i){n=e,a=t,l=s,r=i,c[3]=localStorage.getItem("tip3"),c[4]=localStorage.getItem("tip4"),c[7]=localStorage.getItem("tip7"),0===d.length&&(d[1]=u(1,220),h[1]={domTarget:n,top:-70,left:-240},d[2]=u(2,280),h[2]={domTarget:n,top:-110,left:80},d[3]=u(3,180),h[3]={domTarget:a,top:180,left:720},d[4]=u(4,240),h[4]={domTarget:l,top:45,left:-250},d[5]=u(5,210),h[5]={domTarget:n,top:-130,left:70},d[6]=u(6,240),h[6]={domTarget:n,top:-100,left:370},d[7]=u(7,220),h[7]={domTarget:r,top:160,left:-40},d[8]=u(8,240),h[8]={domTarget:l,top:30,left:760},d[1].addEventListener("click",v),d[2].addEventListener("click",v),d[3].addEventListener("click",e=>{y(3)}),d[4].addEventListener("click",e=>{y(4)}),d[5].addEventListener("click",v),d[6].addEventListener("click",v),d[7].addEventListener("click",e=>{y(7)}),d[8].addEventListener("click",e=>{e.target.style.display="none",g(10)}));let o=localStorage.getItem("currentTip");null===o?g(1):(o=parseInt(o),o<10&&g(o))},setFinal:function(){p=!0,i<10&&(d[i].style.display="none",g(8),m(i)),f(4,!0)},show:function(e,t,s){i<10&&(e?m(i):d[i].style.display="none"),f(3,e&&t),f(4,e),f(7,e&&s)},showIndependentTip:f}},function(e,t,s){"use strict";let i=s(0),n=(s(4),s(17)),a=s(18),l=s(19),r=s(10),o=s(33),d=s(15),h=s(2),c=document.getElementById("content"),p=document.querySelector("title");window.allowNewLoadPopup=!0;var u=window.location,m=u.protocol+"//"+u.host+"/"+u.pathname;a.subscribe("authenticated",e=>{let t=document.location.hash.substring(2);if(t.lastIndexOf("/")===t.length-1&&(t=t.substring(0,t.length-1)),t.indexOf("/")>0){"material"===t.split("/")[0]&&(b.style.visibility="visible")}});var y=new Keycloak({url:window.nomadEnv.keycloakBase,realm:window.nomadEnv.keycloakRealm,clientId:window.nomadEnv.keycloakClientId});window.keycloak=y;let v=document.querySelector("#login-button"),f=document.querySelector("#logout-button"),g=document.querySelector("#user-name");y.init({onLoad:"check-sso",silentCheckSsoRedirectUri:m+"silent-check-sso.html",promiseType:"native"}).then(e=>{e?y.loadUserProfile().then((function(e){g.textContent=`${e.firstName} ${e.lastName}`,v.style.display="none",f.style.display="inline",a.publish("authenticated")})).catch((function(){console.log("Failed to load user profile.")})):(v.style.display="inline",f.style.display="none",g.textContent="Guest")}),v.onclick=()=>{y.login({redirectUri:m+"#/search"}).catch(()=>{console.log("Authentication error.")})},f.onclick=()=>{y.logout()};let b=document.getElementById("calc-flagging-tab");b.style.top=window.innerHeight/2+"px",b.addEventListener("click",e=>{n.show(w.getCurrentPageStatus())});let x,w,S,_,E=new class{constructor(){this.element=document.querySelector("#breadcrumb-placeholder"),this.element.innerHTML='\n <span class="goto-page Search">Search</span>\n <span class="goto-page Results"> > <span>Results</span></span>\n <span class="goto-page Overview"> > <span>Overview</span></span>\n <span class="Details">\n > \n <select class="details-dropdown" >\n <option value="structure">Structure</option>\n <option value="electronicstruct">Electronic structure</option>\n <option value="methodology">Methodology</option>\n <option value="thermalprops">Thermal Properties</option>\n \x3c!-- elasticconst--\x3e\n </select>\n </span>\n ',this.resultsSel=this.element.querySelector(".Results"),this.overviewSel=this.element.querySelector(".Overview"),this.detailsSel=this.element.querySelector(".Details"),this.detailsDropDown=this.element.querySelector(".details-dropdown"),this.element.querySelector(".Search").addEventListener("click",e=>{i.setBrowserHashPath("search")}),this.resultsSel.addEventListener("click",e=>{i.setBrowserHashPath("search/results")}),this.overviewSel.addEventListener("click",()=>{i.setBrowserHashPath("material",i.materialId)}),this.detailsDropDown.addEventListener("change",e=>{i.setBrowserHashPath("material",h.getMaterialData().material_id+"/"+e.target.value)});let e=this;this.detailsDropDown.addEventListener("focus",(function t(){let s=e.detailsDropDown.querySelector('option[value="electronicstruct"]');h.hasElecStructureData()||e.detailsDropDown.removeChild(s);let i=e.detailsDropDown.querySelector('option[value="thermalprops"]');h.hasThermalData()||e.detailsDropDown.removeChild(i),e.detailsDropDown.removeEventListener("focus",t)}))}setState(e,t){this.resultsSel.querySelector("span").style.fontWeight="normal";let s=this.overviewSel.querySelector("span");s.style.fontWeight="normal","search"===e?(this.overviewSel.style.display="none",this.detailsSel.style.display="none","results"===t?(this.resultsSel.style.display="inline",this.resultsSel.querySelector("span").style.fontWeight="bold",this.element.style.visibility="visible"):this.element.style.visibility="hidden"):"material"===e&&(this.element.style.visibility="visible",this.resultsSel.style.display=i.searchResults?"inline":"none",this.overviewSel.style.display="inline",void 0===t?(this.detailsSel.style.display="none",s.style.fontWeight="bold"):(this.detailsSel.style.display="inline",this.detailsDropDown.value=t))}};function T(e){_&&c.removeChild(_),_=e,c.appendChild(_)}a.subscribe("show-material",e=>{console.log("Handling event show-material: "+e.material_id+" view: "+e.view),E.setState("material",e.view),void 0===S&&(w=new r,S=w.element),T(S),w.setMaterialView(e),d.show(!1),y.authenticated&&(b.style.visibility="visible")}),a.subscribe("show-search",e=>{console.log("Handling event show-search: "+e),b.style.visibility="hidden",p.innerHTML="NOMAD Encyclopedia - Search",E.setState("search",e),void 0===e?x.showSearchPage():"results"===e&&x.showResultsPage(),T(x.element)}),l.add("search",e=>a.publish("show-search",e)),l.add("material",(e,t)=>a.publish("show-material",{material_id:e,view:t})),x=new o,""===document.location.hash&&(document.location+="#/search"),l.route();document.querySelector("#user-name");let C=function(e){let t=("; "+document.cookie).split("; "+e+"=");if(2===t.length)return t.pop().split(";").shift()}("user_info");if(void 0!==C){let e=JSON.parse((L=C).substring(1,L.length-1).replace(/\\054/g,",").replace(/\\/g,""));setAppAuthenticated(e)}var L},function(e,t,s){"use strict";let i=s(0),n=s(2),a=document.querySelector("#flagging-form-popup-bg"),l=document.querySelector("#flagging-form-popup");l.innerHTML='\n\n<div> <img src="img/cross.svg" height="12px"\n style="float: right; cursor: pointer" />\n</div>\n\n<div class="form-wrapper">\n <div class="popup-title"> Error reporting</div>\n <br>\n <div> Material: <span id="error-material-id"></span></div>\n <select id="flagging-category" name="category">\n <option value="">Select a category *</option>\n <option value="structure">Structure</option>\n <option value="electronicstruct">Electronic structure</option>\n <option value="methodology">Methodology</option>\n <option value="thermalprops">Thermal properties</option>\n </select>\n\n <select id="flagging-subcategory" name="subcategory">\n\n </select>\n\n <textarea id="subject" name="subject" style="height:200px"\n placeholder="Write a short explanation about the error" ></textarea>\n\n <div id="form-validation-msg"> </div>\n\n <div style="display: flex; justify-content: space-evenly;">\n <button style="display: block">Send</button>\n </div>\n\n\n</div>\n';let r=l.querySelector("#flagging-category"),o=r.querySelector('option[value="electronicstruct"]'),d=r.querySelector('option[value="thermalprops"]'),h=l.querySelector("#flagging-subcategory"),c=l.querySelector("img"),p=l.querySelector("#form-validation-msg"),u=l.querySelector("button"),m=l.querySelector("#error-material-id"),y=null;function v(){a.style.visibility="hidden",l.style.visibility="hidden",r.selectedIndex=0,h.selectedIndex=0,l.querySelector("textarea").value="",p.innerHTML=""}function f(e,t){let s=document.createElement("option");return s.value=void 0===t?e:t,s.innerHTML=e,s}c.addEventListener("click",e=>{v()}),u.addEventListener("click",e=>{let t=r.options[r.selectedIndex];var s=window.keycloak;if(!s.authenticated)return void(p.innerHTML="Your authentication has expired. Please login again.");if(!r.disabled&&""===t.value)return void(p.innerHTML="The category fields must be set");if(r.disabled&&""===h.value&&t.value!==i.MAT_VIEW.methodology)return void(p.innerHTML="The subcategory fields must be set");p.innerHTML="Sending report...";let a=l.querySelector("textarea").value,o=n.getMaterialData().material_id;s.loadUserProfile().then((function(e){let s={};s.server=i.getServerLocation(),s.username=e.username,s.email=e.email,s.first_name=e.firstName,s.last_name=e.lastName,s.message=a,s.category=t.text,r.disabled?s.subcategory=h.options[h.selectedIndex].text:s.representatives=n.getRepresentatives(),i.serverReqPOST(i.getReportURL(o),JSON.stringify(s),e=>{204===e.target.status?v():p.innerHTML="Could not connect to the service. Please check your connection and try again later."})})).catch(e=>{console.log(e),p.innerHTML="Could not connect to the service. Please check your connection and try again later."})}),e.exports={show:function(e){y=e.eStructCalcs,o.style.display=n.hasElecStructureData()?"block":"none",d.style.display=n.hasThermalData()?"block":"none",function(e){h.innerHTML="";let t=n.getMaterialData().material_id;if(m.textContent=t,null===e)r.disabled=!1,h.style.display="none";else switch(r.disabled=!0,h.style.display="block",h.appendChild(f("Choose the subcategory *","")),e){case i.MAT_VIEW.structure:r.selectedIndex=1,h.appendChild(f("Structure representation")),h.appendChild(f("Calculation tree")),h.appendChild(f("Summary")),h.appendChild(f("Specific calculation"));break;case i.MAT_VIEW.electronicstruct:r.selectedIndex=2,h.appendChild(f("Calculation tree")),h.appendChild(f("Summary")),h.appendChild(f("Band structure")),h.appendChild(f("DOS")),h.appendChild(f("Brillouin zone"));break;case i.MAT_VIEW.methodology:r.selectedIndex=3,h.style.display="none";break;case i.MAT_VIEW.thermalprops:r.selectedIndex=4,h.appendChild(f("Calculation tree")),h.appendChild(f("Phonon dispersion")),h.appendChild(f("Phonon DOS")),h.appendChild(f("Specific heat")),h.appendChild(f("Helmholtz free energy"))}}(e.pageId);let t=l.getBoundingClientRect(),s=(window.innerWidth-t.width)/2,c=(window.innerHeight-t.height)/2;l.style.left=s+"px",l.style.top=c-20+"px",l.style.visibility="visible",a.style.visibility="visible"},hide:v}},function(e,t,s){"use strict";var i=new Map,n=-1;e.exports={subscribe:function(e,t){if("function"!=typeof t)return!1;i.has(e)||i.set(e,new Map);var s="uid_"+String(++n);return i.get(e).set(s,t),s},publish:function(e,t){return!!(i.has(e)&&i.get(e).size>0)&&(setTimeout((function(){i.get(e).forEach((function(e,s){e(t)}))}),0),!0)}}},function(e,t,s){"use strict";let i=s(4),n=new Map;window.addEventListener("hashchange",r);var a=[window.location.hash],l=window.history.length;function r(){var e=window.location.hash,t=window.history.length;a.length&&l==t?a[a.length-2]==e?(a=a.slice(0,-1),i.reset(),window.allowNewLoadPopup=!1):(window.allowNewLoadPopup=!0,a.push(e)):(window.allowNewLoadPopup=!0,a.push(e),l=t);let s,r,o,d=document.location.hash.substring(2);d.lastIndexOf("/")===d.length-1&&(d=d.substring(0,d.length-1));let h=d.indexOf("&state");if(-1!=h&&(d=d.substring(0,h)),d.indexOf("/")>0){let e=d.split("/");s=e[0],r=e[1],o=e[2]}else s=d;n.has(s)&&n.get(s)(r,o)}e.exports={add:function(e,t){n.set(e,t)},route:r}},function(e,t,s){"use strict";let i=s(0),n=s(3),a=s(4),l=s(8),r=s(11),o=s(12),d=(s(10),s(2)),h=s(22).SimilarityFinder;e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","overview"),this.materialId;this.calcMaterialId=null,this.element.innerHTML='\n\n <div class="material-title">\n </div>\n\n <div style="float: left; width: 40%;">\n\n <div id="structure-ov" class="view-box">\n <div class="title">Structure\n <img style="float: right" class="to-detail" src="img/more.svg" />\n <div style="clear: both;"></div>\n </div>\n\n <div class="viz-box-container" style="height: 260px; position: relative">\n <div class="viz-box" style="height: 90%"></div>\n </div>\n\n <div class="footer">\n <div ><b><span>Material type</span></b>:\n <span class="material-type-field" ></span>\n </div>\n <div class="space-group-field" style="display: none">\n <b><span info-sys-data="space-group">Space group</span></b>:\n <span class="space-group-value" ></span>\n </div>\n <div class="structure-type-field" style="display: none">\n <b><span info-sys-data="structure-type">Structure type</span></b>:\n <span class="structure-type-value" ></span>\n </div>\n </div>\n </div>\n\n\n\x3c!-- ***** Elastic Constants Box\n\n <div id="elastic-ov" class="view-box">\n <div class="title">Elastic constants\n <img style="float: right" class="to-detail" src="img/more.svg" />\n <div style="clear: both;"></div>\n </div>\n\n <div class="info-fields">\n Not analyzed yet\n </div>\n\n </div>\n--\x3e\n\n\n <div id="methodology-ov" class="view-box">\n <div class="title">Methodology\n <img style="float: right" class="to-detail" src="img/more.svg" />\n <div style="clear: both;"></div>\n </div>\n\n <div class="info-fields">\n\n <div class="info-fields-label" > Available calculations </div>\n\n <div style="float: left; width: 45%" >\n <b><span info-sys-data="functional-type">Functional</span></b>\n <div class="functional-field" > </div>\n </div>\n <div style="float: right; width: 45%" >\n <b><span info-sys-data="code-name">Code</span></b>\n <div class="code-field"> </div>\n </div>\n <div style="clear: both;"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style="float: right; width: 60%;">\n\n <div id="e-structure-ov" class="view-box" style="display: block">\n <div class="title">Electronic structure\n <img style="float: right" class="to-detail" src="img/more.svg" />\n <div style="clear: both;"></div>\n </div>\n\n <div > \x3c!-- style="margin: 12% 0; " --\x3e\n\n <div style="float: left; width: 60%; ">\n <div style="padding: 20px 0 20px 30px">\n <div class="info-fields-label">\n <span info-sys-data="band-structure">Band structure</span>\n </div>\n <div>\n <div id="band-plotter" > </div>\n </div>\n\n <div class="footer-bs-calc"></div>\n </div>\n </div>\n\n <div style="float: left; width: 40%; ">\n <div style="padding: 20px 30px 20px 40px">\n <div class="info-fields-label">\n <span info-sys-data="DOS">DOS</span>\n </div>\n\n <div>\n <div id="dos-plotter" > </div>\n </div>\n <div class="footer-dos-calc"></div>\n </div>\n </div>\n\n\n\n <div style="clear: both;"></div>\n <table style="width: 100%">\n <tr>\n <td class="spin-legend" style="font-size: 0.9em; padding: 6px 30px 10px; display: none;">\n <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg>\n Spin <span style=\'font-size: 1.1em\'>⇧</span> \n\n <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg>\n Spin <span style=\'font-size: 1.1em\'>⇩</span>\n </td>\n <td class = "similarity-data-field">\n <div style="float: right; padding: 0px 50px 10px 0px; margin-top: 1px;" class="similarity-finder">\n </div>\n </td>\n </tr>\n </table>\n </div>\n <div style="clear: both;"></div>\n \x3c!--\n <div class="footer">\n\n <b>Band gap</b>: <span class="e-struct-field" ></span>\n </div>\n --\x3e\n </div>\n\n <div id="thermal-props-ov" class="view-box" style="visibility: hidden">\n <div class="title">Vibrational and thermal properties\n <img style="float: right" class="to-detail thermal-props" src="img/more.svg" />\n <div style="clear: both;"></div>\n </div>\n\n <div style="padding: 36px; ">\n <div class="info-fields-label">\n <span info-sys-data="heat-capacity-cv">Specific heat</span>\n </div>\n\n\n <div>\n <div id="heat-plotter" > </div>\n </div>\n <div class="footer-heat-calc" style="text-align: center"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style="clear: both;"></div>\n ',this.materialTitle=this.element.getElementsByClassName("material-title")[0],this.systemType=this.element.querySelector(".material-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.functional=this.element.querySelector(".functional-field"),this.code=this.element.querySelector(".code-field");let e=this.element.getElementsByClassName("to-detail");this.structureDetailBtn=e[0],this.electronicStructDetailBtn=e[2],this.methodologyDetailBtn=e[1],this.thermalDetailBtn=e[3],this.vizBox=this.element.getElementsByClassName("viz-box")[0],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"),n.addToInfoSystem(this.element),this.eStructCalcs={bs:null,dos:null}}attachAndSetEvents(e){e.appendChild(this.element),this._events()}_events(){this.structureDetailBtn.addEventListener("click",e=>{i.setBrowserHashPath("material",this.materialId+"/"+i.MAT_VIEW.structure)}),this.electronicStructDetailBtn.addEventListener("click",e=>{i.setBrowserHashPath("material",this.materialId+"/"+i.MAT_VIEW.electronicstruct)}),this.methodologyDetailBtn.addEventListener("click",e=>{i.setBrowserHashPath("material",this.materialId+"/"+i.MAT_VIEW.methodology)}),this.thermalDetailBtn.addEventListener("click",e=>{i.setBrowserHashPath("material",this.materialId+"/"+i.MAT_VIEW.thermalprops)})}getEStructChosenCalcs(){return this.eStructCalcs}setDetailViewsListener(e){this.detailViewsListener=e}setVisible(){this.element.style.display="block"}setMaterialData(){let e=d.getMaterialData();this.materialTitle.innerHTML=i.getMaterialTitle(e),this.materialId=e.material_id;let t="bulk"===e.material_type;if(this.systemType.textContent=e.material_type,this.structTypeField.style.display=t&&null!==e.structure_type?"block":"none",this.spaceGroupField.style.display=t?"block":"none",t&&(this.structTypeValue.textContent=e.structure_type,this.spaceGroupValue.textContent=e.space_group_number+" ("+e.space_group_international_short_symbol+")",n.addElementToInfoSystem(this.spaceGroupValue,"space-group.value:"+e.space_group_number)),this.similarityFinder&&this.similarityFinder.element.remove(),e.similarity){this.similarityFinder=new h({left:40,right:16,top:0,bottom:30});const t=this.element.querySelector(".similarity-finder");this.similarityFinder.setSimilarityData(e.similarity),t.appendChild(this.similarityFinder.element),this.similartyDataField=this.element.querySelector(".similarity-data-field"),n.addToInfoSystem(this.similartyDataField)}}isLoaded(e,t,s){let i=d.getMaterialData().material_id;return this.calcMaterialId===i&&(!(e&&!this.bsLoaded)&&(!(t&&!this.dosLoaded)&&!(s&&!this.phononLoaded)))}setCalcsData(e){let t=d.getMaterialData(),s=d.getCalculations(),h=new Map,c=new Map,p=d.getCalc(d.getRepresentatives().electronic_band_structure),u=d.getCalc(d.getRepresentatives().electronic_dos),m=d.getCalc(d.getRepresentatives().thermodynamical_properties);if(this.isLoaded(void 0!==p,void 0!==u,void 0!==m))return;this.calcMaterialId=t.material_id,this.bsLoaded=!1,this.dosLoaded=!1,this.phononLoaded=!1;for(let e=0;e<s.length;e++){if(h.has(s[e].functional_type)){let t=h.get(s[e].functional_type);h.set(s[e].functional_type,++t)}else h.set(s[e].functional_type,1);let t=s[e].code_name.trim();if(c.has(t)){let e=c.get(t);c.set(t,++e)}else c.set(t,1)}void 0!==p&&(this.eStructCalcs.bs=p.calc_id),void 0!==u&&(this.eStructCalcs.dos=u.calc_id);let y=null;void 0!==p?y=p.calc_id:void 0!==u&&(y=u.calc_id),e.eStruct=null===y?null:+y,e.thermalProps=void 0===m?null:+m.calc_id,this.functional.textContent="";let v=document.createElement("div");h.forEach((e,t)=>{let s=document.createElement("span");s.setAttribute("info-sys-data","functional-type.value:"+i.getDefault(t)),s.textContent=e+" "+i.getDefault(t),v.appendChild(s);let n=document.createElement("br");v.appendChild(n)}),this.functional.append(v),n.addToInfoSystem(this.functional),this.code.textContent="";let f=document.createElement("div");if(c.forEach((e,t)=>{let s=document.createElement("span");s.setAttribute("info-sys-data","code-name.value:"+t),s.textContent=e+" "+t,f.appendChild(s);let i=document.createElement("br");f.appendChild(i)}),this.code.append(f),n.addToInfoSystem(this.code),d.hasElecStructureData()){document.getElementById("e-structure-ov").style.display="block";let e=()=>{this.dosLoaded&&this.bsLoaded&&(document.getElementById("e-structure-ov").style.visibility="visible")};if(null===this.bandPlotter&&(this.bandPlotter=new l,this.bandPlotter.attach(document.getElementById("band-plotter"),void 0,316)),null===this.dosPlotter&&(this.dosPlotter=new r({left:40,right:20,top:0,bottom:30}),this.dosPlotter.attach(document.getElementById("dos-plotter"),void 0,317)),void 0===p)this.bandPlotter.setNoData(),this.bsCalcIdBox.innerHTML="",this.bsLoaded=!0;else{let t=i.getMaterialCalcURL(this.materialId,p.calc_id);a.show("overview_electronic_band_structure");let s=JSON.stringify({properties:["electronic_band_structure"]});i.serverReqPOST(t,s,t=>{if(200===t.target.status){let e=JSON.parse(t.target.response).electronic_band_structure;this.bandPlotter.setBandStructureData(e),this.bsCalcIdBox.innerHTML="From calculation <b>"+i.getShortCode(p.calc_id)+'</b><br><span style="font-size: 0.8em">('+p.functional_type+" - "+p.code_name+")</span>",2===e.section_k_band_segment[0].band_energies.length&&(this.spinLegend.style.display="block")}else this.bandPlotter.setNoData();this.bsLoaded=!0,e(),a.hide("overview_electronic_band_structure")})}if(void 0===u)this.dosPlotter.setNoData(),this.dosCalcIdBox.innerHTML="",this.dosLoaded=!0;else{let t=i.getMaterialCalcURL(this.materialId,u.calc_id);a.show("overview_electronic_dos");let s=JSON.stringify({properties:["electronic_dos"]});i.serverReqPOST(t,s,t=>{if(200===t.target.status){let e=JSON.parse(t.target.response).electronic_dos;this.dosPlotter.setPoints(e,u),this.dosCalcIdBox.innerHTML="From calculation <b>"+i.getShortCode(u.calc_id)+'</b><br><span style="font-size: 0.8em">('+u.functional_type+" - "+u.code_name+")</span>",2===e.dos_values.length&&(this.spinLegend.style.display="block")}else this.dosPlotter.setNoData();this.dosLoaded=!0,e(),a.hide("overview_electronic_dos")})}}if(d.hasThermalData())if(document.getElementById("thermal-props-ov").style.display="block",null===this.heatPlotter&&(this.heatPlotter=new o,this.heatPlotter.attach(document.getElementById("heat-plotter"),void 0,317)),void 0===m)this.heatPlotter.setNoData(),this.heatCalcIdBox.innerHTML="";else{let e=i.getMaterialCalcURL(this.materialId,m.calc_id);a.show();let t=JSON.stringify({properties:["thermodynamical_properties"]});i.serverReqPOST(e,t,e=>{if(200===e.target.status){let t=JSON.parse(e.target.response).thermodynamical_properties,s=t.thermodynamical_property_temperature;this.heatPlotter.setData(s,t.specific_heat_capacity),this.heatCalcIdBox.innerHTML="From calculation <b>"+i.getShortCode(m.calc_id)+'</b></b> <span style="font-size: 0.8em">('+m.functional_type+" - "+m.code_name+")</span>",this.phononLoaded=!0,document.getElementById("thermal-props-ov").style.visibility="visible"}a.hide()})}else document.getElementById("thermal-props-ov").style.display="none"}}},function(e,t,s){s(0);e.exports=class{constructor(e){this.off=!0,this.element=document.createElement("span"),this.element.innerHTML+=`\n <img src="${e}_off.png" width="24px"\n style="margin-bottom: -1px; cursor: pointer"/>\n `,this.image=this.element.querySelector("img"),this.element.addEventListener("click",t=>{this.off=!this.off;let s=this.off?e+"_off":e;this.image.setAttribute("src",s+".png"),this.listener(this.off)})}setListener(e){this.listener=e}}},function(e,t,s){let i=s(0);e.exports={SimilarityFinder:class{constructor(){this.folded=!0,this.element=document.createElement("span"),this.element.style="float : right; border: 2px solid #DDD; padding: 2px 2px 0px 3px;",this.element.innerHTML+=`\n <span info-sys-data="similar-materials" style="vertical-align: 30%;">Similar materials</span>\n <img style="cursor: pointer" src="${i.IMAGE_DIR}folded.png" />\n\n <div class="vr-download-panel" style="position: relative; display: none;">\n\n </div>\n `,this.element.tabIndex="0",this.element.style.outline="none",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?i.IMAGE_DIR+"folded.png":i.IMAGE_DIR+"unfolded.png",this.foldingPanel.style.display=this.folded?"none":"block"}),this.element.addEventListener("blur",e=>{setTimeout(()=>{this.folded=!0,this.foldBtn.src=i.IMAGE_DIR+"folded.png",this.foldingPanel.style.display="none"},300)})}setSimilarityData(e){e.sort((function(e,t){return e.value<t.value?1:e.value>t.value?-1:0}));const t=e.slice(0,5),s=document.createElement("table");s.setAttribute("class","similar-materials-panel-unfolded"),s.style="width: 230px; padding-left: 5px;",this.foldingPanel.appendChild(s);const n=document.createElement("tr");n.style="padding: 5px; ",n.innerHTML='<th style = "text-align: left;">Formula (space group)</th><th>:</th><th style = "text-align: left;">Tc</th>',s.appendChild(n),t.forEach((function(e){const t=document.createElement("tr");t.style="padding: 5px; font-family: 'Arimo', sans-serif; font-size: 10pt; ";const n=`${window.location.toString().replace(/#.*$/,"")}#/material/${e.material_id}`;t.innerHTML=`<td><a href="${n}" target="_${e.material_id}" style="color:#777; font-family: 'Arimo', sans-serif; font-size: 10pt;">${i.getSubscriptedFormula(e.formula)} (${e.space_group_number})</a></td><td>:</td> <td>${e.value.toPrecision(3)}</td>`,s.appendChild(t)}))}}}},function(e,t,s){"use strict";let i=s(6),n=s(0),a=s(3),l=s(7),r=s(13),o=s(14),d=s(24),h=s(2);class c{constructor(e){this.groupCalcs=null,this.element=document.createElement("div"),this.element.innerHTML='\n <div>\n\n <div class="group-components" style="display: none">\n <div style="padding: 10px 0 30px 10px; " class="eos-host">\n </div>\n\n <div style="padding-top: 10px; " class="calc-selector-host">\n </div>\n </div>\n\n <div class="info-fields">\n <div><b>Lattice constants</b></div>\n <div class="latt-constants"></div>\n <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b>:\n <span class="volume-value" ></span>\n </div>\n \x3c!-- <div><b>Pressure</b>: <span class="" ></span> </div>--\x3e\n <div class="density-field"><b>Density</b>:\n <div class="stats-fields" >\n <span info-sys-data="mass-density">Mass density</span> =\n <span class="mass-density-value" ></span>\n </div>\n <div class="stats-fields" >\n <span info-sys-data="atomic-density">Atomic density</span> =\n <span class="atomic-density-value" ></span>\n </div>\n </div>\n\n <div class="energy-field"><b><span info-sys-data="energies">Energies</span></b> (code-specific)</div>\n <div class="energy-descomp"> </div>\n\n <div class="wyckoff-pos-calc-field" >\n <b><span info-sys-data="free-wyckoff-parameters">Wyckoff sites</span></b>\n (fractional coordinates)\n <div class="wyckoff-pos-calc-table"> </div>\n </div>\n\n </div>\n\n </div>\n ',this.groupComponents=this.element.querySelector(".group-components"),this.calcSelector=new r("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 d,this.eosViewer.attach(this.element.querySelector(".eos-host"),320,280),this.eosViewer.setClickPointListener(e=>{this.groupCalcUpdate(e+"")}),a.addToInfoSystem(this.element),this._events()}_events(){this.calcSelector.setPrevListener(e=>{if(this.groupIndex>0)return this.groupCalcUpdate(this.groupCalcs[--this.groupIndex]+""),0===this.groupIndex}),this.calcSelector.setNextListener(e=>{if(this.groupIndex<this.groupCalcs.length-1)return this.groupCalcUpdate(this.groupCalcs[++this.groupIndex]+""),this.groupIndex===this.groupCalcs.length-1})}update(e,t){if(this.representative=e,this.isGroup=t,t){let t=h.getGroupType(e),s=h.getGroupId(e),i=h.getMaterialData().material_id,a=n.serverReq(n.getMaterialGroupURL(i,t,s),()=>{if(200===a.status){let e=JSON.parse(a.response),t=e.volumes.map(e=>e/1e-30),s=e.energies.map(e=>e/1602176565e-28),i=e.calculations,n=s[0];this.groupCalcs=i,this.groupIndex=0,this.groupComponents.style.display="block",this.eosViewer.clear(),this.eosViewer.draw(t,s,this.groupCalcs,n),this.groupCalcUpdate(i[0])}})}else this.groupComponents.style.display="none",this.groupCalcUpdate(e)}groupCalcUpdate(e){if(null!==e){if(null!==this.groupCalcs&&(this.groupIndex=this.groupCalcs.indexOf(e),this.groupIndex>=0)){let t=n.getShortCode(e)+" ("+(this.groupIndex+1)+"/"+this.groupCalcs.length+")";this.calcSelector.setState(t,0===this.groupIndex,this.groupIndex===this.groupCalcs.length-1),this.eosViewer.selectCalc(e)}let t=h.getMaterialData(),s=t.material_id,i="2D"===t.material_type,a="bulk"===t.material_type,l=(h.getCalc(e),h.getMaterialData().has_free_wyckoff_parameters),r=["energies","lattice_parameters","mass_density","atomic_density","cell_volume"];l&&r.push("wyckoff_sets");let o=JSON.stringify({properties:r}),d=n.serverReqPOST(n.getMaterialCalcURL(s,e),o,()=>{if(200===d.status){let e=JSON.parse(d.response),t=e.lattice_parameters,s=i||a?`<div>b = ${n.m2Angstrom(t.b)}</div>`:"";s+=a?`<div>c = ${n.m2Angstrom(t.c)}</div>`:"";let r=a?`<div>α = ${n.rad2degree(t.alpha)}</div>\n <div>β = ${n.rad2degree(t.beta)}</div>`:"";r+=i||a?`<div>γ = ${n.rad2degree(t.gamma)}</div>`:"",this.lattConstantsField.innerHTML=`\n <div style="float: left; ">\n <div>a = ${n.m2Angstrom(t.a)}</div>\n ${s}\n </div>\n <div style="float: left; padding-left: 40px;">\n ${r}\n </div>\n <div style="clear: both;padding: 0"></div>\n `,this.densityField.style.display=a?"block":"none",this.volumeField.style.display=a?"block":"none",a&&(this.volumeValue.innerHTML=n.m3ToAngstrom3(e.cell_volume),this.atomicDensityValue.innerHTML=n.toAngstromMinus3(e.atomic_density),this.massDensityValue.innerHTML=e.mass_density.toFixed(1)+" kg/m<sup>3</sup>");let o=!1,h=e.energies;if(void 0!==h){let e=h.energy_total;void 0!==e&&(o=!0,this.energyDescompValue.innerHTML="<div>Total E = "+n.J2eV(e)+" eV</div>")}if(this.energyField.style.display=o?"block":"none",this.energyDescompValue.style.display=o?"block":"none",this.wyckoffPosField.style.display=l?"block":"none",l){let t=new Map;e.wyckoff_sets.forEach(e=>{let s="",i=!1,n=e.variables;if(void 0!==n&&(i=!0,["x","y","z"].forEach(e=>{if(e in n){let t=n[e];s+=e+" = "+t.toFixed(2)+"<br>"}})),i){let i=[];i.push(e.wyckoff_letter),i.push(s),t.has(e.element)?t.get(e.element).push(i):t.set(e.element,[i])}});let s="";t.forEach((e,t)=>{e.sort((e,t)=>e[0]>t[0]?1:-1);let i=!0;s+='<tr > <td style="width: 30%;">'+t+" </td>",e.forEach(e=>{i?(i=!1,s+='<td style="width: 30%; ">'+e[0]+'</td><td style="width: 40%;">'+e[1]+"</td></tr>"):s+="<tr><td> </td><td>"+e[0]+"</td><td>"+e[1]+"</td></tr>"})}),this.wyckoffPosTable.innerHTML='<table id="calc-wyckoff">'+s+"</table>"}}})}}}class p{constructor(e){this.calcMapByFunctional=null,this.quantitiesMap=null,this.hostElement=e,this.graphTrigger=null,this.viewType="text",this.functional=null,this.nBins=15,this.hostElement.innerHTML+='\n <div style="float: left" >\n <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab"\n viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;">\n <rect x="0" y="0" width="2" height="15" />\n <rect x="3" y="5" width="1.8" height="7" />\n <rect x="6" y="3" width="1.8" height="9" />\n <rect x="9" y="6" width="1.8" height="6" />\n <rect x="12" y="2" width="1.8" height="10" />\n <rect x="2" y="13" width="13" height="2" />\n </svg>\n <svg xmlns="http://www.w3.org/2000/svg" class="text-tab"\n viewBox="0 0 15 15" width="15" height="15" style="fill: #777;">\n <rect x="0" y="1" width="15" height="2.5" />\n <rect x="0" y="6" width="15" height="2.5" />\n <rect x="0" y="11" width="15" height="2.5" />\n </svg>\n </div>\n\n <div class="functional-tabs" style="float: right">\n </div>\n\n <div style="clear: both;"></div>\n\n <div class="content-placeholder" >\n\n <div style="display: block" class="text-panel" >\n <div><b>Lattice constants</b>:\n <div class="stats-fields latt-constants-field" >\n </div>\n </div>\n <div class="volume-field"><b><span info-sys-data="cell-volume">Volume</span></b> (Å<sup>3</sup>):\n <div class="stats-fields volume-value" > </div>\n </div>\n <div class="density-field"><b>Density</b> :\n <div >\n <div class="stats-fields" >\n <span info-sys-data="mass-density">Mass density</span> (kg/m<sup>3</sup>) =\n <span class="mass-density-value" ></span>\n </div>\n <div class="stats-fields" >\n <span info-sys-data="atomic-density">Atomic density</span> (Å<sup>-3</sup>) =\n <span class="atomic-density-value" ></span>\n </div>\n </div>\n </div>\n </div>\n\n <div style="display:none" class="chart-panel" >\n <div class="charts-placeholder" > </div>\n <div class="charts-selector" >\n\n </div>\n </div>\n\n </div>\n ',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.statsViewer=new o;let t=this.hostElement.querySelector(".charts-placeholder");this.statsViewer.attach(t,350,200),this.chartsSelector=this.hostElement.querySelector(".charts-selector"),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=>{"tab"===e.target.className&&(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(0===e.target.className.indexOf("quantity")){this.statsViewer.clear();let t=e.target.getAttribute("data-quantity"),s=this.quantitiesMap.get(t),i=s.label,n=s.stats;this.statsViewer.drawPoints(n.histogram,i,this.nBins),this.chartsSelector.querySelector(".quantity-selected").className="quantity",e.target.className="quantity-selected"}})}_setData(){let e="2D"===h.getMaterialData().material_type,t="bulk"===h.getMaterialData().material_type,s=(this.functional,Array.from(this.calcMapByFunctional.get(this.functional))),i=h.getMaterialData().material_id,a=new Map;a.set("lattice_a",{label:"a (Å)"}),e|t&&(a.set("lattice_b",{label:"b (Å)"}),a.set("gamma",{label:"&gamma"})),t&&(a.set("cell_volume",{label:"Volume (ų)"}),a.set("atomic_density",{label:"Atomic density (Å⁻³)"}),a.set("mass_density",{label:"Mass density (kg/m³)"}),a.set("lattice_c",{label:"c ()"}),a.set("alpha",{label:"&alpha"}),a.set("beta",{label:"&beta"}));let l=JSON.stringify({calculations:s,properties:Array.from(a.keys()),n_histogram_bins:this.nBins});n.serverReqPOST(n.getMaterialStatsURL(i),l,s=>{let i=JSON.parse(s.target.response);for(let e of a.keys()){let t=i[e];"cell_volume"==e?(t.min*=1e30,t.avg*=1e30,t.max*=1e30,t.histogram.values=t.histogram.values.map(e=>1e30*e)):"atomic_density"==e?(t.min*=1e-30,t.avg*=1e-30,t.max*=1e-30,t.histogram.values=t.histogram.values.map(e=>1e-30*e)):e.startsWith("lattice_")&&(t.min*=1e10,t.avg*=1e10,t.max*=1e10,t.histogram.values=t.histogram.values.map(e=>1e10*e));let s=a.get(e).label;t.equal=t.min===t.max;let l=s.split(":");t.label=l[0],2===l.length?t.units=l[1]:t.units="";let r,o=3;"mass_density"===e&&(o=1),r="alpha"==e||"beta"==e||"gamma"==e?n.rad2degree(t.avg.toFixed(o)):t.avg.toFixed(o)+' <span style="font-size: 0.9em">['+t.min.toFixed(o)+" , "+t.max.toFixed(o)+"]</span>",a.get(e).html=r,a.get(e).stats=t}let l=e||t?`<div>b (Å) = ${a.get("lattice_b").html}</div>`:"";l+=t?`<div>c (Å) = ${a.get("lattice_c").html}</div>`:"";let r=t?`<div>α = ${a.get("alpha").html}</div>\n <div>β = ${a.get("beta").html}</div>`:"";r+=e||t?`<div>γ = ${a.get("gamma").html}</div>`:"",this.lattConstantsField.innerHTML=`\n <div style="float: left; ">\n <div>a (Å) = ${a.get("lattice_a").html}</div>\n ${l}\n </div>\n <div style="float: left; padding-left: 40px;">\n ${r}\n </div>\n <div style="clear: both;padding: 0"></div>\n `;let o='\n <span class="quantity-selected" data-quantity="lattice_a">a</span>\n ';(e||t)&&(o+='<span class="quantity" data-quantity="lattice_b">b</span>'),this.densityField.style.display=t?"block":"none",this.volumeField.style.display=t?"block":"none",t&&(this.volumeFieldValue.innerHTML=a.get("cell_volume").html,this.massDensityValue.innerHTML=a.get("mass_density").html,this.atomicDensityValue.innerHTML=a.get("atomic_density").html,o+='\n <span class="quantity" data-quantity="lattice_c">c</span>\n <span class="quantity" data-quantity="cell_volume">volume</span>\n <span class="quantity" data-quantity="mass_density">mass density</span>\n <span class="quantity" data-quantity="atomic_density">atomic density</span>\n '),this.chartsSelector.innerHTML=o,this.quantitiesMap=a;let d=i.lattice_a.histogram;this.statsViewer.drawPoints(d,a.get("lattice_a").label,this.nBins)})}build(e){this.calcMapByFunctional=e,this.graphTrigger=null,this.statsViewer.clear(),this.unfoldedElement=null,this.functionalQuantityMap=new Map,this.functionalTabs.innerHTML="",this.calcMapByFunctional.forEach((e,t)=>{this.functionalTabs.innerHTML+='<span class="tab" data-tab="'+t+'">'+t+"</span>"});let t=Array.from(this.calcMapByFunctional.keys());(null===this.functional||t.indexOf(this.functional)<0)&&(this.functional=t[0]),this._setData(),this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]').className="tab-selected",a.addToInfoSystem(this.hostElement)}}e.exports=class extends i{constructor(){super("Structure"),this.navTree=new l,this.groupsData,this.element.innerHTML+='\n\n <div style="float: left; width: 36%;">\n <div class="view-box">\n <div class="title">Structure </div>\n <div class="viz-box-container" style="height: 400px; position: relative">\n <div class="viz-box" style="height: 90%"></div>\n </div>\n\n <div class="footer-flex-wrapper">\n\n <div class="fields-container">\n <div><b><span>System type</span></b>:\n <span class="struct-field" ></span>\n </div>\n <div class="structure-type-field" style="display: none">\n <b><span info-sys-data="structure-type">Structure type</span></b>:\n <span class="structure-type-value" ></span>\n </div>\n <div class="structure-prototype-field" style="display: none">\n <b><span info-sys-data="structure-prototype">Structure prototype</span></b>:\n <span class="structure-prototype-value" ></span>\n </div>\n <div class="strukturbericht-field" style="display: none">\n <b><span info-sys-data="strukturbericht">Strukturbericht designation</span></b>:\n <span class="strukturbericht-value" ></span>\n </div>\n </div>\n\n <div class="footer-flex" style="display: none">\n\n <div class="fields-container"\n style="flex-basis: 70%; border-right: 1px solid #E4E4E4; ">\n\n <div>\n <b><span info-sys-data="crystal-system">Lattice</span></b>:\n <span class="lattice-value" ></span>\n </div>\n <div>\n <b><span info-sys-data="space-group">Space group</span></b>:\n <span class="space-group-value" ></span>\n </div>\n <div>\n <b><span info-sys-data="point-group">Point group</span></b>:\n <span class="point-group-value" ></span>\n </div>\n </div>\n\n <div style="flex-basis: 30%; margin-left: 30px;">\n <div class="fields-container">\n <div><b><span info-sys-data="wyckoff-position-population">Wyckoff sites</span></b></div>\n <div class="wyckoff-sites-value"> </div>\n </div>\n </div>\n\n </div>\n\n </div>\n\n </div>\n </div>\n\n <div style="float: left; width: 36%;">\n <div class="view-box">\n <div class="title">Calculations</div>\n <div class="navTreeWrapper"></div>\n\n <div class="summary-title">Summary </div>\n <div style="font-size: 0.85em; text-align: center; padding: 4px;">Based on the calculations selected above</div>\n\n <div class="info-fields summary-box">\n \x3c!-- Lattice constants Cell volume, Density panel dynamically generated\n --\x3e\n </div>\n </div>\n </div>\n\n <div style="float: right; width: 28%;">\n <div class="calc-specifics-box">\n <div style="padding-top: 10px; " >\n <div class="tree-leaf-title"></div>\n </div>\n <div class="tree-leaf-viewer-host"></div>\n </div>\n </div>\n </div>\n </div>\n ',this.navTreeWrapper=this.element.getElementsByClassName("navTreeWrapper")[0];let e=this.element.getElementsByClassName("struct-field");this.systemTypeField=e[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.summaryByFunctionals=new p(this.summaryBox),this.calcSpecificsBox=this.element.querySelector(".calc-specifics-box"),this.treeLeafViewer=new c,this.element.querySelector(".tree-leaf-viewer-host").appendChild(this.treeLeafViewer.element),this.vizBox=this.element.querySelector(".viz-box"),a.addToInfoSystem(this.element)}setMaterialData(){let e=h.getMaterialData(),t=h.getIdealizedStructure();super.setMaterialData(e);let s=null===e.material_name?e.formula:e.material_name;if(this.navTree.build(s,"structure"),this.navTree.selectAll(),this.navTree.setHeight(250),this.navTree.setMarkedLeafIfNoneMarked(null),this.attachNavTree(this.navTree),this.updateSelection(this.navTree.getTreeSelectedCalcs()),this.updateMarkedLeaf(this.navTree.getMarkedLeaf()),this.navTree.setTreeSelectionListener(e=>{this.updateSelection(e)}),this.navTree.setLeafMarkedListener(e=>{this.updateMarkedLeaf(e)}),this.isBulk="bulk"===e.material_type,this.systemTypeField.textContent=e.material_type,this.structTypeField.style.display=this.isBulk&&null!==e.structure_type?"block":"none",this.structPrototypeField.style.display=this.isBulk&&null!==e.structure_prototype?"block":"none",this.strukturberichtField.style.display=this.isBulk&&null!==e.strukturbericht_designation?"block":"none",this.lowerBox.style.display=this.isBulk?"flex":"none",this.isBulk){this.structTypeValue.textContent=e.structure_type,this.structPrototypeValue.textContent=e.structure_prototype,this.strukturberichtValue.textContent=e.strukturbericht_designation,this.spaceGroupValue.textContent=e.space_group_number+" ("+e.space_group_international_short_symbol+")",this.pointGroupValue.textContent=e.point_group,this.latticeValue.textContent=e.crystal_system;let s=new Map;new Set;for(var i=0;i<t.wyckoff_sets.length;i++){let e=t.wyckoff_sets[i],n=e.element;if(s.has(n))s.get(n).add(e.wyckoff_letter);else{let t=new Set;t.add(e.wyckoff_letter),s.set(n,t)}}let n="";s.forEach((e,t)=>{let s=!0;n+="<tr> <td>"+t+": </td>",e.forEach(e=>{s?(s=!1,n+="<td>"+e+"</td></tr>"):n+="<tr><td> </td><td>"+e+"</td></tr>"})}),this.wyckoffValue.innerHTML="<table>"+n+"</table>"}a.addElementToInfoSystem(this.spaceGroupValue,"space-group.value:"+e.space_group_number),a.addElementToInfoSystem(this.latticeValue,"crystal-system.value:"+e.crystal_system),a.addElementToInfoSystem(this.pointGroupValue,"point-group.value:"+e.point_group)}updateSelection(e){if(e.size>0){let t=[];e.forEach(e=>{if(h.isGroup(e)){let s=h.getGroupType(e),i=h.getGroupId(e),n=h.getGroups().get(s).get(i);t.push(h.getCalc(n[0]))}else t.push(h.getCalc(e))}),this.summaryBox.style.visibility="visible";let s=n.getCalcMapByFunctional(t);this.summaryByFunctionals.build(s)}else this.summaryBox.style.visibility="hidden"}updateMarkedLeaf(e){let t=!1;if(null!==e)if(this.calcSpecificsBox.style.visibility="visible",t=h.isGroup(e),t){let t,s=h.getGroupType(e),i=h.getGroupId(e);t=h.getGroups().get(s).get(i),this.leafTitle.innerHTML=n.getShortCode(e)+" ("+t.length+")"}else this.leafTitle.innerHTML=n.getShortCode(e);else this.calcSpecificsBox.style.visibility="hidden";this.treeLeafViewer.update(e,t)}}},function(e,t,s){"use strict";let i=s(0),n=s(1),a=s(5);e.exports=class extends a{constructor(){super({left:60,right:20,top:30,bottom:40}),this.tooltip,this.calcPointMap=new Map,this.pointSelected=null}draw(e,t,s,a){for(let e=0;e<t.length;e++)555!==t[e]&&(t[e]-=a);let l=function(e){let t=[];return e.forEach(e=>{555!==e&&t.push(e)}),t}(t),r=Math.min.apply(null,e),o=Math.max.apply(null,e),d=0,h=Math.max.apply(null,l);if(r===o)r-=1,o+=1;else{let e=o-r;r-=.1*e,o+=.1*e}if(d===h)d-=1,h+=1;else{let e=h-d;d-=.15*e,h+=.1*e}this.setRangeAndLabels("Volume (ų)",r,o,"E - Eₘᵢₙ (eV)",d,h),this.drawAxis(2,2),n.addLine(this.plotArea,0,this.y(0),this.plotRangeX,this.y(0),"zeroline"),n.addText(this.plotArea,this.x(o),this.y(0)+12,"Eₘᵢₙ: "+a.toFixed(3)+" eV","end","axis-steps");for(let a=0;a<e.length;a++){let l=0===a?"eos-viewer-sel":"eos-viewer",r=0===a?6:3,o=555===t[a]?20:this.y(t[a]),d=n.addPoint(this.plotArea,this.x(e[a]),o,r,l);0===a&&(this.pointSelected=d),d.addEventListener("mouseover",e=>{this.tooltip=n.addText(this.plotArea,e.target.getBBox().x+10,e.target.getBBox().y-10,"Calc "+i.getShortCode(s[a]),"middle","tooltip")}),d.addEventListener("mouseout",e=>{n.removeElement(this.tooltip)}),d.addEventListener("click",e=>{this.clickPointListener(s[a])}),this.calcPointMap.set(s[a],d)}}selectCalc(e){this.pointSelected.setAttribute("class","eos-viewer"),this.pointSelected.setAttribute("r",3),this.pointSelected=this.calcPointMap.get(e),this.pointSelected.setAttribute("class","eos-viewer-sel"),this.pointSelected.setAttribute("r",6)}setClickPointListener(e){this.clickPointListener=e}x(e){return this.xRel*(e-this.xMin)}y(e){return-this.yRel*(e-this.yMin)}}},function(e,t,s){"use strict";let i=s(6),n=s(0),a=s(7),l=s(3),r=(s(13),s(14)),o=s(26),d=(new(s(5)),s(2)),h=s(4);class c{constructor(e){this.calcMapByFunctional=null,this.hostElement=e,this.viewType="text",this.functional=null,this.nBins=15,this.hostElement.innerHTML+='\n <div style="float: left" >\n <svg xmlns="http://www.w3.org/2000/svg" class="chart-tab"\n viewBox="0 0 15 15" width="15" height="15" style="fill: #c7c7c7;">\n <rect x="0" y="0" width="2" height="15" />\n <rect x="3" y="5" width="1.8" height="7" />\n <rect x="6" y="3" width="1.8" height="9" />\n <rect x="9" y="6" width="1.8" height="6" />\n <rect x="12" y="2" width="1.8" height="10" />\n <rect x="2" y="13" width="13" height="2" />\n </svg>\n <svg xmlns="http://www.w3.org/2000/svg" class="text-tab"\n viewBox="0 0 15 15" width="15" height="15" style="fill: #777;">\n <rect x="0" y="1" width="15" height="2.5" />\n <rect x="0" y="6" width="15" height="2.5" />\n <rect x="0" y="11" width="15" height="2.5" />\n </svg>\n </div>\n\n <div class="functional-tabs" style="float: right">\n </div>\n\n <div style="clear: both;"></div>\n\n <div class="content-placeholder" >\n\n <div style="display: block" class="text-panel" >\n\n <div><b><span info-sys-data="band gap">Band gap</span></b> (eV):\n <div class="stats-fields summary-bandgap-field" > </div>\n </div>\n </div>\n\n <div style="display:none" class="chart-panel" >\n <div class="charts-placeholder" > </div>\n </div>\n\n </div>\n ',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 r,this.chartsPlaceholder=this.hostElement.querySelector(".charts-placeholder"),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=>{"tab"===e.target.className&&(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(){this.functional;let e=Array.from(this.calcMapByFunctional.get(this.functional)),t=d.getMaterialData().material_id,s=JSON.stringify({calculations:e,properties:["band_gap"],n_histogram_bins:this.nBins});n.serverReqPOST(n.getMaterialStatsURL(t),s,e=>{let t=JSON.parse(e.target.response).band_gap;if(void 0===t)return this.bandgapField.innerHTML="No band gap data available",this.statsViewer.clear(),void(this.chartsPlaceholder.textContent="No band gap data available");this.chartsPlaceholder.textContent="",this.statsViewer.attach(this.chartsPlaceholder,250,150),t.min*=6241509e12,t.avg*=6241509e12,t.max*=6241509e12,t.histogram.values=t.histogram.values.map(e=>6241509e12*e);let s=t.avg.toFixed(3)+' <span style="font-size: 0.9em">['+t.min.toFixed(3)+" , "+t.max.toFixed(3)+"]</span>";this.bandgapField.innerHTML=s,this.statsViewer.clear(),this.statsViewer.drawPoints(t.histogram,"Band gap (eV)",this.nBins)})}build(e){this.calcMapByFunctional=e,this.graphTrigger=null,this.statsViewer.clear(),this.unfoldedElement=null,this.functionalQuantityMap=new Map,this.functionalTabs.innerHTML="",this.calcMapByFunctional.forEach((e,t)=>{this.functionalTabs.innerHTML+='<span class="tab" data-tab="'+t+'">'+t+"</span>"});let t=Array.from(this.calcMapByFunctional.keys());(null===this.functional||t.indexOf(this.functional)<0)&&(this.functional=t[0]),this._setData(),this.functionalTabs.querySelector('[data-tab="'+this.functional+'"]').className="tab-selected",l.addToInfoSystem(this.hostElement)}}class p{constructor(e){this.hostElement=e,this.loaded=!1;this.bzViewer=new matviewer.BrillouinZoneViewer(this.hostElement,{view:{autoResize:!1,autoFit:!0,fitMargin:.018}})}setCalcData(e){this.bzViewer.load(this._getBZDataForViewer(e)),this.hostElement.style.visibility="visible",this.loaded=!0,this.resize()}setNoData(){this.hostElement.style.visibility="hidden"}resize(){this.bzViewer.resizeCanvasToHostElement(),this.loaded&&(this.bzViewer.fitToCanvas(),this.bzViewer.render())}_getBZDataForViewer(e){let t=e.brillouin_zone,s=[],i=[];return e.section_k_band_segment.forEach(e=>{s.push(e.band_segm_labels),i.push(e.band_segm_start_end)}),{vertices:t.vertices,faces:t.faces,basis:e.reciprocal_cell,labels:s,segments:i}}}e.exports=class extends i{constructor(){super("Electronic Structure"),this.navTree=new a,this.element.innerHTML+='\n <div style="float: left; width: 30%;">\n <div class="view-box">\n <div class="title">Calculations </div>\n <div class="navTreeWrapper"></div>\n\n <div class="summary-title">Summary</div>\n <div class="footer summary-box" style="border-top: 0"></div>\n </div>\n </div>\n\n <div style="float: right; width: 70%;">\n <div class="view-box e-structure-box">\n\n <div class="title">Electronic structure</div>\n\n <div style="padding-top: 10px; " >\n <div class="tree-leaf-title"></div>\n </div>\n\n <div>\n\n <div style="padding: 30px 100px 20px 100px; ">\n <div class="info-fields-label" style="float: left; width: 54%; ">\n <span info-sys-data="band-structure">Band structure</span>\n </div>\n <div class="info-fields-label" style="float: left;">\n <span info-sys-data="DOS">DOS</span>\n </div>\n <div style="clear: both;"></div>\n\n <div class="calc-bs-dos-plotter" >\n </div>\n\n <div>\n <div class="band-gap-field" style="float: left; width: 56%; text-align: right">\n <b><span info-sys-data="band-gap">Band gap</span></b>:\n <span class="band-gap-value" ></span>\n </div>\n <div style="clear: both;"></div>\n\n </div>\n </div>\n\n <div class="spin-legend" style="font-size: 0.9em; padding: 0 30px 10px; display: none">\n <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin1"/></svg>\n Spin <span style=\'font-size: 1.1em\'>⇧</span> \n <svg width="15px" height="10px"> <polyline points="0,5 15,5" class="plotSpin2"/></svg>\n Spin <span style=\'font-size: 1.1em\'>⇩</span>\n </div>\n\n </div>\n\n <div class="footer lower-section">\n\n <div style="float: left">\n <div style="padding: 16px; ">\n <div class="info-fields-label" >\n <span info-sys-data="brillouin-zone-viewer">Brillouin zone</span>\n </div>\n <div class="bz-viewer-wrapper" style="width: 400px; height: 400px">\n </div>\n </div>\n </div>\n\n <div class="band" style="float: right; width: 40%;">\n <div style="padding: 16px; ">\n <div class="info-fields-label" >\n \x3c!-- <span info-sys-data="fermi-surface">Fermi surface </span> --\x3e\n </div>\n <div class="fermi-box" > </div>\n </div>\n </div>\n\n <div style="clear: both;"></div>\n\n </div> \x3c!-- footer --\x3e\n\n </div>\n\n </div> \x3c!-- view-box --\x3e\n ',this.navTreeWrapper=this.element.getElementsByClassName("navTreeWrapper")[0],this.leafTitle=this.element.querySelector(".tree-leaf-title"),this.rightBox=this.element.querySelector(".e-structure-box"),this.summaryByFunctionals=null,this.summaryBox=this.element.querySelector(".summary-box"),this.summaryByFunctionals=new c(this.summaryBox),this.bsDosPlotter=new o,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],this.bzViewerWrapper=new p(this.element.querySelector(".bz-viewer-wrapper")),l.addToInfoSystem(this.element)}setVisible(){this.element.style.display="block",this.bzViewerWrapper.resize()}setMaterialData(){let e=d.getMaterialData();super.setMaterialData(e);let t=null===e.material_name?e.formula:e.material_name;this.navTree.build(t,"electronic"),this.navTree.selectAll(),this.navTree.setHeight(400),this.navTree.setMarkedLeafIfNoneMarked(null),this.attachNavTree(this.navTree),this.updateSelection(this.navTree.getTreeSelectedCalcs()),this.updateMarkedLeaf(this.navTree.getMarkedLeaf()),this.navTree.setTreeSelectionListener(e=>{this.updateSelection(e)}),this.navTree.setLeafMarkedListener(e=>{this.updateMarkedLeaf(e)})}updateSelection(e){if(e.size>0){let t=[];e.forEach(e=>{t.push(d.getCalc(e))});let s=n.getCalcMapByFunctional(t);this.summaryByFunctionals.build(s)}else this.summaryBox.style.visibility="hidden"}updateMarkedLeaf(e){null===e?this.leafTitle.innerHTML="NO SELECTION":(this.leafTitle.innerHTML=n.getShortCode(e),this._loadGraphData(e))}_updateSelectorState(e){this.leafTitle.innerHTML=n.getShortCode(e)}_loadGraphData(e){let t=d.getCalc(e);if(this.bsDosPlotter.isAttached()||this.bsDosPlotter.attach(this.element.querySelector(".calc-bs-dos-plotter"),void 0,360),null===t||!t.has_band_structure&&!t.has_dos)this.bsDosPlotter.setNoData(),this.bzViewerWrapper.setNoData(),this.bandGapField.style.display="none",this.lowerSection.style.display="none";else{h.show("electronic_structure");let e=d.getMaterialData().material_id,s=n.getMaterialCalcURL(e,t.calc_id),i=JSON.stringify({properties:["electronic_band_structure","electronic_dos","band_gap"]});n.serverReqPOST(s,i,e=>{let s=JSON.parse(e.target.response),i=s.electronic_dos,a=s.electronic_band_structure,l=s.band_gap;(function(e,t){if(void 0!==e&&2===e.section_k_band_segment[0].band_energies.length)return!0;if(void 0!==t&&2===t.dos_values.length)return!0;return!1})(a,i)&&(this.spinLegend.style.display="block"),this.bsDosPlotter.setUpAndData(a,i,t),void 0!==l?(this.bandGapValue.textContent=n.J2eV(l,2)+" eV ",this.bandGapField.style.display="block"):(this.bandGapValue.textContent="",this.bandGapField.style.display="none"),void 0!==a&&void 0!==a.brillouin_zone?(this.lowerSection.style.display="block",this.bzViewerWrapper.setCalcData(a)):(this.lowerSection.style.display="none",this.bzViewerWrapper.setNoData()),h.hide("electronic_structure")})}}setPrevCalcListener(e){this.prevCalcListener=e}setNextCalcListener(e){this.nextCalcListener=e}}},function(e,t,s){let i=s(8),n=s(11);s(1);e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("style","margin: 0 auto"),this.parentElement=null,this.bsPlotter=new i,this.dosPlotter=new n({left:4,right:20,top:0,bottom:30}),this.dosYAxisLabeled=!1}attach(e,t,s){e.appendChild(this.element),this.bsPlotter.attach(this.element,s,s),this.height=s,this.dosPlotter.attach(this.element,this.height/2+20,s),this.parentElement=e}isAttached(){return null!==this.parentElement}setUpAndData(e,t,s){let i;if(this.hasDispData=null!=e,this.hasDosData=null!=t,i=!(!this.hasDosData||this.hasDispData),this.dosYAxisLabeled!==i){this.element.removeChild(this.dosPlotter.svg);let e=i?40:4;this.dosPlotter=new n({left:e,right:20,top:0,bottom:30});let t=this.height/2+e;this.dosPlotter.attach(this.element,t,this.height)}this.dosYAxisLabeled=i,this.hasDispData?(this.bsPlotter.setBandStructureData(e),this.hasDosData&&this.bsPlotter.setRepaintListener((e,t)=>{this.dosPlotter.setYZoomAndOffset(e,t),this.dosPlotter.repaint()})):this.bsPlotter.setNoData(),this.hasDosData?(this.dosPlotter.setPoints(t,s),this.hasDispData&&(this.bsPlotter.setExternalYAxisMax(this.dosPlotter.getYAxisMax()),this.dosPlotter.setRepaintListener((e,t)=>{this.bsPlotter.setYZoomAndOffset(e,t),this.bsPlotter.repaint()}),this.dosPlotter.svg.removeChild(this.dosPlotter.yLabelText),this.dosPlotter.yLabelText=null)):this.dosPlotter.setNoData(),this.dosPlotter.setYAxisLabelsVisibility(i)}setNoData(){this.bsPlotter.setNoData(),this.dosPlotter.setNoData()}}},function(e,t,s){"use strict";let i=s(6),n=s(0),a=s(3),l=s(2),r=s(28);var o=window.location,d=o.protocol+"//"+o.host+"/"+o.pathname;let h=d.slice(0,d.lastIndexOf("/",d.lastIndexOf("/")-1))+"/gui/entry/id";const c=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"],["QHA calculation","QHA"]]);class p{constructor(e){this.element=document.createElement("div"),this.element.className=e,this.filtersOn=[],this.folded=!0,this.element.innerHTML+=`\n <div style="padding: 0rem 2.5rem">\n <div style="display: flex; justify-content: flex-end;">\n <div class="filter-groups-c-folded" >\n <span style="vertical-align: top;">Filtering </span>\n </div>\n <div class="filter-c-btn" >\n <img src="${n.IMAGE_DIR}folded.png" />\n \x3c!--<button class="on">filter</button> --\x3e\n </div>\n </div>\n <div class="filter-groups-c-unfolded" style="display: none; padding-bottom: 1rem">\n <table style="width: 100%;">\n <thead>\n <tr>\n <th style="width: 20%;">\n <span>Type</span>\n </th>\n <th style="width: 20%;">\n <span info-sys-data="functional-type">Density functional</span>\n </th>\n <th style="width: 20%;">\n <span info-sys-data="code-name">Code</span>\n </th>\n <th style="width: 20%;">\n <span info-sys-data="pseudopotential-type">Potential</span>\n </th>\n <th style="width: 20%;">\n <span info-sys-data="basis-set-type">Basis set</span>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr id="filter-items-row"></tr>\n </tbody>\n </table>\n </div>\n </div>\n `,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?n.IMAGE_DIR+"folded.png":n.IMAGE_DIR+"unfolded.png",this.unfoldedPanel.style.display=this.folded?"none":"block"}),this.element.addEventListener("click",e=>{if("INPUT"===e.target.tagName){let t=this.filtersOn.indexOf(e.target.value);t>=0?this.filtersOn.splice(t,1):this.filtersOn.push(e.target.value),this.itemListener(this.filtersOn)}})}addGroupsItems(e){let t=new Map,s=new Map,i=new Map,n=new Map,a=new Map;e.forEach(e=>{t.has(e.type)||t.set(e.type,c.get(e.type)),s.has(e.functional_type)||s.set(e.functional_type,e.functional_type),i.has(e.code)||i.set(e.code,e.code),n.has(e.core_electron_treatment)||n.set(e.core_electron_treatment,e.core_electron_treatment),a.has(e.basis_set_type)||a.set(e.basis_set_type,e.basis_set_type)}),this.filterItemsRow.innerHTML="",this.filtersOn=[],this.addGroupItems(t),this.addGroupItems(s),this.addGroupItems(i),this.addGroupItems(n),this.addGroupItems(a)}addGroupItems(e){let t="<td> ";e.forEach((e,s)=>{this.filtersOn.push(s),t+='<input type="checkbox" value="'+s+'" checked><span style="vertical-align: 20%">'+e+"</span> <br> "}),this.filterItemsRow.innerHTML+=t+"</td>"}setItemListener(e){this.itemListener=e}}function u(e){return null==e?"":"("+e+")"}e.exports=class extends i{constructor(){super("Methodology"),this.sortedCalcs=[],this.markedCalc=null,this.element.innerHTML+='\n\n <div>\n <div class="view-box">\n <div class="title">Methodology</div>\n\n <div class="filter-placeholder"></div>\n\n <div class="dataTableWrapper"></div>\n </div>\n </div>\n ',this.dataTableWrapper=this.element.querySelector(".dataTableWrapper"),this.dataTableWrapper.innerHTML+='\n <table id="methodology-data">\n <thead>\n <tr>\n <th style="width: 12%;">\n <span>Calculation ID</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 16%;">\n <span>Type</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 18%;">\n <span info-sys-data="functional-type">Density functional</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 12%;">\n <span info-sys-data="code-name">Code</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 16%;">\n <span info-sys-data="pseudopotential-type">Potential</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 11%;">\n <span info-sys-data="basis-set-type">Basis set</span>\n <span class="sorting-button"></span>\n </th>\n <th style="width: 4%;">\n <span info-sys-data="basis-set-type">Link</span>\n </th>\n </tr>\n </thead>\n <tbody>\n </tbody>\n </table>\n ',this.tbody=this.dataTableWrapper.querySelector("tbody"),this.moreInfoRow=document.createElement("tr"),this.moreInfoRow.className="moreinfo",this.moreInfoCalcId=null,this.filterComponent=new p("meth-filter-component"),this.element.querySelector(".filter-placeholder").appendChild(this.filterComponent.element),this.filterComponent.setItemListener(e=>{this.sortedCalcs.forEach(t=>{let s=t.dataCalcProps.split(","),i=!0;s.forEach(t=>{e.indexOf(t)<0&&(i=!1)}),t.visible=!!i}),this._render()}),this.sortingButtonWrappers=this.dataTableWrapper.querySelectorAll(".sorting-button"),this.sortingButtons=[];let e=new Map([["calc_id",void 0],["type",void 0],["functional_type",void 0],["code",void 0],["core_electron_treatment",void 0],["basis_set_type",void 0]]).keys();this.sortingButtonWrappers.forEach(t=>{let s=e.next().value,i=new r(s);t.appendChild(i.element),this.sortingButtons.push(i),i.setListener((e,t)=>{this.sortingButtons.forEach(e=>{e!==i&&e.init()}),this._sortRowsCalcDataBy(e,t),this._render()})}),a.addToInfoSystem(this.element)}updateSelection(e){this.sortedCalcs=[],l.getCalculations().forEach(e=>{let t=e.run_type;l.getGroups().has(e.calc_id)&&(t=l.getGroupType(e.calc_id)),this.sortedCalcs.push({calc_id:e.calc_id,type:t,functional_type:e.functional_type,code:e.code_name,core_electron_treatment:e.core_electron_treatment,basis_set_type:e.basis_set_type,dataCalcProps:t+","+e.functional_type+","+e.code_name+","+e.core_electron_treatment+","+e.basis_set_type,visible:!0})}),this.filterComponent.addGroupsItems(this.sortedCalcs),this._sortRowsCalcDataBy(!0,"calc_id"),this._render()}_sortRowsCalcDataBy(e,t){this.sortedCalcs.sort((s,i)=>{let n=e?-1:1;return s[t]<i[t]?n:-n})}updateMarkedLeaf(e){}_render(){let e="";this.sortedCalcs.forEach(t=>{t.visible&&(e+=function(e){let t=l.getCalc(e.calc_id),s=e.type,i=`<a href="${h}/${t.upload_id}/${t.calc_id}" target="blank"><img src="img/download.svg" height="20px" /> </a>`,a=l.getMaterialData().material_id,r=t.calc_id,o=`<a href="https://labdev-nomad.esc.rzg.mpg.de/remotevis/cM/start/${a}+${r}" target="blank"><img src="img/monitor_icon.png" height="20px" /> </a>`;return`\n <tr data-calc-id="${t.calc_id}" class="data-row">\n <td>${n.getShortCode(t.calc_id)}</td>\n <td>\n <span info-sys-data="calculation-type.value:${s}">\n ${s}</span>\n </td>\n <td>\n <span info-sys-data="functional-type.value:${e.functional_type}">\n ${e.functional_type}</span>\n ${u(t.functional_long_name)}\n </td>\n <td>\n <span info-sys-data="code-name.value:${t.code_name}">\n ${t.code_name}</span>\n ${u(t.code_version)}\n </td>\n\n <td>\n <span info-sys-data="core-electron-treatment.value:${e.core_electron_treatment}">\n ${e.core_electron_treatment}</span>\n </td>\n <td>\n <span info-sys-data="basis-set-type.value:${t.basis_set_type}">\n ${t.basis_set_type}</span>\n ${u(t.basis_set_quality_quantifier)}\n </td>\n\n <td style="padding-top: 8px;padding-bottom: 4px;">${i}\n </td>\n\n \x3c!--\n <td style="padding-top: 8px;padding-bottom: 4px;">${o}\n </td>--\x3e\n\n </tr>`}(t))}),this.tbody.innerHTML=e,a.addToInfoSystem(this.tbody)}}},function(e,t,s){s(0);e.exports=class{constructor(e){this.id=e,this.ascending=!0,this.element=document.createElement("span"),this.element.innerHTML+='\n <img src="img/sorting_init.png" width="12px"\n style="margin-bottom: -1px; cursor: pointer"/>\n ',this.image=this.element.querySelector("img"),"id"===e&&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)})}init(){this.image.setAttribute("src","img/sorting_init.png")}setListener(e){this.listener=e}}},function(e,t,s){"use strict";let i=s(6),n=s(0),a=s(7),l=s(3),r=s(2),o=s(4),d=s(12),h=s(30),c=s(31);e.exports=class extends i{constructor(){super("Thermal Properties"),this.firstId,this.lastId,this.navTree=new a,this.element.innerHTML+='\n <div style="float: left; width: 30%;">\n <div class="view-box">\n <div class="title">Calculations </div>\n <div class="navTreeWrapper"></div>\n </div>\n </div>\n\n <div style="float: right; width: 70%;">\n <div class="view-box thermal-properties-box">\n\n <div class="title">Vibrational and thermal properties</div>\n\n <div style="padding-top: 10px;">\n <div class="tree-leaf-title"></div>\n </div>\n\n <div class="calc-disp-dos-plotter" style="padding: 30px 100px; ">\n <div class="info-fields-label" style="float: left; width: 52%; ">\n <span info-sys-data="phonon-dispersion">Phonon dispersion </span>\n </div>\n <div class="info-fields-label" style="float: left;">\n <span info-sys-data="phonon-DOS">Phonon DOS </span>\n </div>\n <div style="clear: both;"></div>\n </div>\n\n\n <div class="band" >\n <div style="padding: 30px 50px; display: flex; justify-content: space-around; ">\n\n <div >\n <div class="info-fields-label" >\n <span info-sys-data="specific-heat-cv">Specific heat</span>\n </div>\n <div class="heat-plotter" > </div>\n </div>\n\n <div>\n <div class="info-fields-label" >\n <span info-sys-data="helmholtz-free-energy">Helmholtz free energy</span>\n </div>\n <div class="helmholtz-plotter" > </div>\n </div>\n\n </div>\n </div>\n\n </div>\n </div>\n ',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 c,this.heatPlotter=new d,this.helmholtzPlotter=new h,l.addToInfoSystem(this.element)}_events(){super._events()}setMaterialData(){let e=r.getMaterialData();super.setMaterialData(e);let t=null===e.material_name?e.formula:e.material_name;this.navTree.build(t,"thermal"),this.navTree.selectAll(),this.navTree.setHeight(600),this.navTree.setMarkedLeafIfNoneMarked(null),this.attachNavTree(this.navTree),this.updateSelection(this.navTree.getTreeSelectedCalcs()),this.updateMarkedLeaf(this.navTree.getMarkedLeaf()),this.navTree.setTreeSelectionListener(e=>{this.updateSelection(e)}),this.navTree.setLeafMarkedListener(e=>{this.updateMarkedLeaf(e)})}updateSelection(e){}updateMarkedLeaf(e){if(null===e)return this.leafTitle.innerHTML="NO SELECTION",this.dispDosPlotter.setNoData(),this.heatPlotter.setNoData(),void this.helmholtzPlotter.setNoData();this.leafTitle.innerHTML=n.getShortCode(e);let t=r.getCalc(e);this.dispDosPlotter.isAttached()||(this.dispDosPlotter.attach(this.element.querySelector(".calc-disp-dos-plotter"),void 0,360),this.heatPlotter.attach(this.element.querySelector(".heat-plotter"),317,317),this.helmholtzPlotter.attach(this.element.querySelector(".helmholtz-plotter"),317,317)),o.show("thermal_properties");let s=r.getMaterialData().material_id,i=n.getMaterialCalcURL(s,t.calc_id),a=JSON.stringify({properties:["phonon_band_structure","phonon_dos","thermodynamical_properties"]});n.serverReqPOST(i,a,e=>{let t=JSON.parse(e.target.response),s=t.phonon_dos,i=t.phonon_band_structure,n=t.thermodynamical_properties;if(void 0!==s?this.dispDosPlotter.setUpAndData(i,s):this.dispDosPlotter.setNoData(),void 0!==n){let e=n.thermodynamical_property_temperature;this.heatPlotter.setData(e,n.specific_heat_capacity),this.helmholtzPlotter.setData(e,n.specific_vibrational_free_energy_at_constant_volume)}else this.heatPlotter.setNoData(),this.helmholtzPlotter.setNoData();document.getElementById("thermal-props-ov").style.visibility="visible",o.hide("thermal_properties")})}setPrevCalcListener(e){this.prevCalcListener=e}setNextCalcListener(e){this.nextCalcListener=e}}},function(e,t,s){"use strict";let i=s(1),n=s(5);e.exports=class extends n{constructor(){super({left:70,right:16,top:10,bottom:32}),this.tooltip}setData(e,t){this.clear();let s,n=[],a=[];for(let s=0;s<e.length;++s){let i=e[s],l=t[s];null!==i&null!==l&&(n.push(i),a.push(l))}for(let t=0;t<e.length;++t){if(e[t]>600){s=t-1;break}}void 0===s&&(s=e.length-1),e=e.slice(0,s+1),t=t.slice(0,s+1);let l=Math.max.apply(null,t),r=Math.min.apply(null,t),o=5e3*Math.ceil(l/5e3),d=5e3*Math.floor(r/5e3),h=e[s],c=50*Math.floor(h/50);this.setRangeAndLabels("T (K)",0,c,"F (J/kg)",d,o),this.drawAxis(4,4,2);let p="";e.forEach((e,s)=>{let i=t[s];p+=" "+this.xRel*e+" -"+this.yRel*(i-this.yMin)}),i.addPolyline(this.plotArea,p,"plotSpin1")}}},function(e,t,s){let i=s(8),n=s(9),a=s(1),l=s(0);class r extends n{constructor(){super({left:4,right:16,top:0,bottom:30}),this.outOfRangeColorActivated=!1}attach(e,t,s){super.attach(e,s/2+this.margins.left,s)}setPoints(e){this.pointsSpin1=[],this.pointsSpin2=[],this._reset();let t=e.dos_values[0],s=null;2===e.dos_values.length&&(s=e.dos_values[1]);let i=e.dos_energies,n=[],r=[];for(var o=0;o<i.length;o++){let e,a=5034117012222e10*i[o],l=.029979*t[o];null!==s&&(e=.029979246*s[o]),n.push(l),null!==s&&n.push(e),r.push(a),this.pointsSpin1.push({x:l,y:a}),null!==s&&this.pointsSpin2.push({x:e,y:a})}let d=Math.max.apply(null,n),h=Math.max.apply(null,r),c=Math.min.apply(null,r),p=l.generateDiagramSteps(d),u=p[0],m=p[1];this.setAxisRangeAndLabels(null,0,u[u.length-1],null,-50,320,c,h,100),a.addText(this.axisGroup,this.plotRangeX/2,this.margins.bottom,"DOS (states/cm⁻¹)","middle","axis-steps-big");for(let e=0;e<u.length;e++){let t=this.plotRangeX*u[e]/u[u.length-1];a.addLine(this.axisGroup,t,0,t,3,1),a.addText(this.axisGroup,t,13,0===e?"0":u[e].toFixed(m),"middle","axis-steps-smaller")}this.repaint()}repaintData(){let e="";for(var t=0;t<this.pointsSpin1.length;t++)e+=" "+this.xRel*this.pointsSpin1[t].x+" "+this.transformY(this.pointsSpin1[t].y);a.addPolyline(this.plotContent,e,"plotSpin1"),e="";for(t=0;t<this.pointsSpin2.length;t++)e+=" "+this.xRel*this.pointsSpin2[t].x+" "+this.transformY(this.pointsSpin2[t].y);a.addPolyline(this.plotContent,e,"plotSpin2")}}e.exports=class{constructor(){this.element=document.createElement("div"),this.parentElement=null,this.dispPlotter=new i,this.dispPlotter.setPhononMode(),this.dosPlotter=new r}attach(e,t,s){e.appendChild(this.element),this.dispPlotter.attach(this.element,s,s),this.dosPlotter.attach(this.element,void 0,s),this.parentElement=e}isAttached(){return null!==this.parentElement}setUpAndData(e,t){this.hasDispData=null!=e,this.hasDosData=null!=t,this.hasDispData?(this.dispPlotter.setBandStructureData(e),this.hasDosData&&this.dispPlotter.setRepaintListener((e,t)=>{this.dosPlotter.setYZoomAndOffset(e,t),this.dosPlotter.repaint()})):this.dispPlotter.setNoData(),this.hasDosData?(this.dosPlotter.setPoints(t),this.hasDispData&&this.dosPlotter.setRepaintListener((e,t)=>{this.dispPlotter.setYZoomAndOffset(e,t),this.dispPlotter.repaint()})):this.dosPlotter.setNoData()}setNoData(){this.dispPlotter.setNoData(),this.dosPlotter.setNoData()}}},function(e,t,s){"use strict";let i=s(6),n=(s(0),s(3));e.exports=class extends i{constructor(){super("Elastic constants"),this.sortedLeafs=[],this.markedCalc=null,this.element.innerHTML+='\n\n <div style="float: left; width: 27%;">\n <div class="view-box">\n <div class="title">Calculation </div>\n <div class="navTreeWrapper"></div>\n </div>\n </div>\n\n <div style="float: right; width: 73%;">\n <div class="view-box">\n <div class="title">Elastic constants</div>\n\n <div>Parameters</div>\n <div ></div>\n\n\n </div>\n </div>\n\n <div style="clear: both;"></div>\n ',this.navTreeWrapper=this.element.querySelector(".navTreeWrapper"),n.addToInfoSystem(this.element),this._events()}_events(){}updateSelection(e){console.log("ElasticDetails updateSelection ",e)}updateMarkedLeaf(e){}}},function(e,t,s){"use strict";let i=s(0),n=s(15),a=s(34),l=s(35),r=s(36);function o(e){return e.split("-").join("_")}class d{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","formula-box"),this.element.innerHTML='\n <div style="padding-bottom: 40px;">\n <input type="text" class="formula-text-field"\n placeholder="Add formula to the search query above" >\n <button class="adding-formula-btn" disabled>Add to query</button>\n </div>\n <div>\n <input type="text" class="material-name-text-field"\n placeholder="Add material name to the search query above" >\n <button class="adding-material-name-btn" disabled>Add to query</button>\n </div>\n ',this.formulaTextField=this.element.querySelector(".formula-text-field"),this.formulaButton=this.element.querySelector(".adding-formula-btn"),this.materialTextField=this.element.querySelector(".material-name-text-field"),this.materialButton=this.element.querySelector(".adding-material-name-btn"),this.formulaButton.addEventListener("click",e=>{this.addFormulaListener(this.formulaTextField.value),this.formulaTextField.value=""}),this.materialButton.addEventListener("click",e=>{this.addMaterialListener(this.materialTextField.value),this.materialTextField.value=""}),this.formulaTextField.addEventListener("input",e=>{this.formulaButton.disabled=""===this.formulaTextField.value}),this.materialTextField.addEventListener("input",e=>{this.materialButton.disabled=""===this.materialTextField.value})}setAddFormulaListener(e){this.addFormulaListener=e}setAddMaterialListener(e){this.addMaterialListener=e}disable(e){this.formulaTextField.disabled=e,this.formulaButton.disabled=!0,this.materialTextField.disabled=e,this.materialButton.disabled=!0}}e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","search-module"),this.element.innerHTML='\n <div class="search-page">\n\n <div class="searchline" style="visibility: visible">\n <div class="search-query-wrapper" style="float: left;">\n <div class="search-query-box" style="float: left;">\n </div>\n <button class="clean-btn" style="float: right;">Clear all</button>\n </div>\n\n <button style="float: right" class="search-btn" >Search</button>\n <div style="clear: both;"></div>\n <div style="display: flex">\n <div class="perm-tooltip search-option" style="margin-right: 10px">\n <input id="exclusive-search" name="exclusive-search" type="checkbox" >\n <label for="exclusive-search" class="perm-tooltip">Exclusive elements</label>\n <span class="tooltiptext">If selected, the query will return materials with a composition that exclusively contains the selected atoms.</span>\n </div>\n <div class="perm-tooltip search-option">\n <input id="restricted-search" name="restricted-search" type="checkbox" >\n <label for="restricted-search" class="perm-tooltip">Restrict to individual calculations</label>\n <span class="tooltiptext">If selected, the query will return materials that have individual calculations matching all your search criteria.</span>\n </div>\n </div>\n </div>\n\n\n <div class="add-buttons" style="display: flex">\n <div class="tab-buttons" style="width: 70%">\n <button class="element-add-btn" id="add-tab-selected">Element</button>\n <button class="formula-add-btn" style="padding: 10px 20px;" >Formula/Material</button>\n <button class="props-add-btn" >Properties</button>\n </div>\n \x3c!--<div class="bool-buttons" style="width: 28%; display: inline-block" >\n <button disabled >AND</button>\n <button disabled >OR</button>\n <button disabled >NOT</button>\n <button disabled >(</button> <button disabled >)</button>\n </div> --\x3e\n </div>\n\n\n <div>\n <div class="triangle element-tri" style="margin: -10px 64px; visibility: visible"></div>\n <div class="triangle formula-tri" style="margin: -10px 30px; visibility: hidden"></div>\n <div class="triangle props-tri" style="margin: -10px 70px; visibility: hidden"></div>\n </div>\n\n\n <div class="add-panel">\n </div>\n\n </div> \x3c!-- search-page --\x3e\n\n\n <div class="results-page" style="display: none">\n </div>\n\n ',this.searchPage=this.element.querySelector(".search-page"),this.searchQueryBox=this.element.getElementsByClassName("search-query-box")[0],this.searchLine=this.element.querySelector(".searchline"),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.addPanel=this.element.querySelector(".add-panel"),this.elementTable=new a,this.elementTable.setClickListener(e=>{this.addElementsInSearchQuery(e),this.addFormulaButton.disabled=!0}),this.elementTable.setDeselectListener(e=>this.removeElementORFormulaInSearchQuery(e)),this.propertiesBox=new r,this.propertiesBox.setAddPropertiesListener(e=>{this.addPropertiesInSearchQuery(e)}),this.formulaBox=new d,this.formulaBox.setAddFormulaListener(e=>{""!==e.trim()&&(this.addTagInSearchQuery(e,"F"),this.addElementButton.disabled=!0,this.formulaBox.disable(!0))}),this.formulaBox.setAddMaterialListener(e=>{""!==e.trim()&&(this.addTagInSearchQuery(e,"material-name"),this.addElementButton.disabled=!0,this.formulaBox.disable(!0))}),this.materialList=new l,this.resultsPage=this.element.querySelector(".results-page"),this.materialList.attachAndSetEvents(this.resultsPage),this.currentTab="element",this.addPanel.appendChild(this.elementTable.element),this.userGuidance=!0,this.showingSearchBox=!1,this.searchQuery=[],this.queryTypes=[],this._events()}_events(){this.mainButton.addEventListener("click",e=>{if(0===this.searchQuery.lenght)i.showUserMsg("No query");else{let e={},t=[];e.search_by={},this.searchQuery.forEach((s,i)=>{if("F"===this.queryTypes[i])e.search_by.formula=s;else if("E"===this.queryTypes[i])t.push(s);else if("S"!==this.queryTypes[i])if(0===this.queryTypes[i].indexOf("band-gap")){let t=s.split("<"),i={};3==t.length?(i.min=parseFloat(t[0]),i.max=parseFloat(t[2])):s.startsWith("Band Gap")?(i.min=null,i.max=parseFloat(t[1])):(i.min=parseFloat(t[0]),i.max=null),e.band_gap=i,console.log(e.band_gap)}else 0===this.queryTypes[i].indexOf("has")?e[o(this.queryTypes[i])]=!0:e[o(this.queryTypes[i])]=s.split(" | ")}),t.length>0&&(e.search_by.elements=t);let s=this.element.querySelector("#exclusive-search:checked"),n=this.element.querySelector("#restricted-search:checked");e.search_by.exclusive=null===s?"0":"1",e.search_by.restricted=null===n?"0":"1",this.materialList.setSearch(e),i.setBrowserHashPath("search","results")}}),this.cleanButton.addEventListener("click",e=>{this.searchQuery=[],this.queryTypes=[],this.updateSearchQuery(),this.addFormulaButton.disabled=!1,this.addElementButton.disabled=!1,this.formulaBox.disable(!1),this.elementTable.deselectAllElements()}),this.addButtonsBox.addEventListener("click",e=>{if(e.target!==e.currentTarget){let t=e.target.className,s=t.indexOf("add-btn");if(s>0){let i,a=t.substring(0,s-1);"element"===a?i=this.elementTable.element:"props"===a?i=this.propertiesBox.element:"formula"===a&&(i=this.formulaBox.element),this.addPanel.replaceChild(i,this.addPanel.lastChild);let l=this.element.querySelector("."+this.currentTab+"-add-btn");this._setTabSelectedStyles(l,!1),this._setTabSelectedStyles(e.target,!0),this.element.querySelector("."+this.currentTab+"-tri").style.visibility="hidden",this.element.querySelector("."+a+"-tri").style.visibility="visible",this.currentTab=a,this.userGuidance&&("element"===a?(n.showIndependentTip(7,!1),n.showIndependentTip(3,!0)):"props"===a?(n.showIndependentTip(3,!1),n.showIndependentTip(7,!0)):"formula"===a&&(n.showIndependentTip(3,!1),n.showIndependentTip(7,!1)))}}}),this.searchQueryBox.addEventListener("click",e=>{if("remove-label"===e.target.className){let t=e.target.parentElement.getAttribute("data-el");this.removeElementORFormulaInSearchQuery(t)}})}_setTabSelectedStyles(e,t){e.id=t?"add-tab-selected":""}_showSearchBox(){this.showingSearchBox||(this.showingSearchBox=!0,this.searchLine.style.visibility="visible",this.userGuidance&&n.setFinal())}_addItemInSearchQuery(e,t){this.searchQuery.push(e),this.queryTypes.push(t)}addTagInSearchQuery(e,t){"E"===t&&this.searchQuery.indexOf(e)>=0||(this.searchQuery.length>0&&this._addItemInSearchQuery("&","S"),this._addItemInSearchQuery(e,t),this.updateSearchQuery(),this._showSearchBox())}addElementsInSearchQuery(e){let t=e.length;for(;t--;)this.addTagInSearchQuery(e[t],"E");return!0}addPropertiesInSearchQuery(e){e.forEach((e,t)=>{let s=this.queryTypes.indexOf(t);if(s<0)this.addTagInSearchQuery(e.join(" | "),t);else{if("band-gap"===t)return this.searchQuery[s]=e[0],this.updateSearchQuery(),void this._showSearchBox();let i=this.searchQuery[s].split(" | ");e.forEach(e=>{i.indexOf(e)<0&&i.push(e)}),this.searchQuery[s]=i.join(" | "),this.updateSearchQuery(),this._showSearchBox()}})}removeElementORFormulaInSearchQuery(e){let t=this.searchQuery.indexOf(e);if(t>=0){if(this.searchQuery.splice(t,1),this.queryTypes.splice(t,1),t>0){let e=this.searchQuery[t-1];"&"!==e&&"|"!==e||(this.searchQuery.splice(t-1,1),this.queryTypes.splice(t-1,1))}"S"===this.queryTypes[0]&&(this.searchQuery.splice(0,1),this.queryTypes.splice(0,1)),this.updateSearchQuery(),i.ELEMENTS.indexOf(e)>=0?(this.elementTable.deselectElement(e),this.queryTypes.indexOf("E")<0&&(this.addFormulaButton.disabled=!1)):(this.addElementButton.disabled=!1,this.formulaBox.disable(!1))}return!0}updateSearchQuery(){let e="";for(let s=0;s<this.searchQuery.length;s++){let n=this.queryTypes[s];e+="S"===n?`<span class="search-query-symbol" > ${this.searchQuery[s]} </span>`:`<span class="search-label" data-el="${t=this.searchQuery[s]}" >\n <img src="img/tag.svg" height="16px" class="remove-label"\n style="vertical-align: bottom"/>\n ${"F"===n?i.getSubscriptedFormula(t):t}\n <img src="img/cross.svg" height="6px" class="remove-label"\n style="vertical-align: middle; padding: 4px 3px 6px 5px;" />\n </span>`}var t;console.log("this.updateSearchQuery: ",this.searchQuery,this.queryTypes),this.searchQueryBox.innerHTML=e}showResultsPage(){this.searchPage.style.display="none",this.resultsPage.style.display="block",this.userGuidance&&n.show(!1)}showSearchPage(){this.searchPage.style.display="block",this.resultsPage.style.display="none",this.userGuidance&&setTimeout(()=>{n.init(this.addButtonsBox,this.elementTable.element,this.searchLine,this.propertiesBox.tabsElement),n.show(!0,"element"===this.currentTab,"props"===this.currentTab)},400)}}},function(e,t,s){"use strict";let i=s(0);const n=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"]]]),a=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"]]]),l=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 r=["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"];function o(e){let t;return a.forEach((function(s,i){s.indexOf(e)>=0&&(t=i)})),t}function d(e){let t=i.ELEMENTS[e-1];return'<td class="cell '+o(t)+'" data-el="el-'+t+'"><b>'+t+"</b> <div>"+e+"</div> </td>"}function h(e){let t=null,s=null;if(e.target.className.indexOf("cell ")>=0?(t=e.target,s=e.target.className):e.target.parentElement.className.indexOf("cell ")>=0&&(t=e.target.parentElement,s=e.target.parentElement.className),null===t)return null;{let e=t.innerHTML,s=e.substring(3,e.indexOf("<",3));return" "===s?null:s}}e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","elementable");let e='<div class="element-info"></div>';e+='<div class="ptWrapper">',e+='<table id="pt-main">',e+="<tbody>",e+="<tr>"+d(1),e+='<td class="cellpad" colspan="16"></td>',e+=d(2)+"</tr>";let t=t=>{e+="<tr>"+d(t)+d(t+1),e+='<td class="cellpad" colspan="10"></td>';for(let s=t+2;s<t+8;s++)e+=d(s);e+="</tr>"};t(3),t(11);let s=19;for(let t=0;t<4;t++){e+="<tr>";for(let t=0;t<18;t++)57===s||89===s?(e+=(n=s,'<td class="cellpad '+o(i.ELEMENTS[n-1])+'" data-el="el-X"><b> </b> <div> </div> </td>'),s+=15):(e+=d(s),s++);e+="</tr>"}var n;e+="</tbody></table>",e+='<div id="specialRows"><table id="pt-laac">';for(let t=0;t<2;t++){e+="<tr>",s=0===t?57:89;for(let t=0;t<15;t++)e+=d(s),s++;e+="</tr>"}e+="</table></div>",e+='<div class="legend">\n <div class="alkali-metals">Alkali metals</div>\n <div class="alkaline-earth-metals">Alkaline earth metals</div>\n <div class="transition-metals">Transition metals</div>\n <div class="post-transition-metals">Post-transition metals</div>\n <div class="metalloids">Metalloids</div>\n <div class="other-non-metals">Other nonmetals</div>\n <div class="halogens">Halogens</div>\n <div class="noble-gases">Noble gases</div>\n <div class="lanthanoids">Lanthanoids</div>\n <div class="actinoids">Actinoids</div>\n </div>',e+="</div>",this.element.innerHTML=e,this.elementInfo=this.element.getElementsByClassName("element-info")[0],this.tableZone=this.element.getElementsByClassName("ptWrapper")[0],this._events()}_events(){this.tableZone.addEventListener("click",e=>{if(e.target!==e.currentTarget){let s=e.target.className,i=e.target;if(""===s&&(i=e.target.parentElement,s=e.target.parentElement.className),s.indexOf("cellpad")>=0)return;if(s.indexOf("group-sel")>=0){let e=n.get(parseInt(i.getAttribute("data-group")));this.clickListener(e);for(var t=0;t<e.length;t++)this.selectElement(e[t])}else if(s.indexOf("cell")>=0){let e=i.innerHTML,t=e.substring(3,e.indexOf("<",3));if(" "===t)return;s.indexOf("el-selected")>=0?this.deselectListener(t):(this.clickListener([t]),this.selectElement(t))}}},!0),this.tableZone.addEventListener("mouseover",e=>{let t=h(e);if(null!==t){this.elementInfo.style.display="block";let e=l.get(o(t));this.elementInfo.style.borderColor=e;let s=i.ELEMENTS.indexOf(t)+1;this.elementInfo.innerHTML=`\n <div>\n <div style="float: right; padding: 3px 4px;border-left: 3px solid ${e};\n border-bottom: 3px solid ${e}" > ${s} </div>\n <div style="clear: right;"></div>\n </div>\n <div class="symbol">${t} </div>\n <div class="">${r[s-1]} </div>\n `}}),this.tableZone.addEventListener("mouseout",e=>{null!==h(e)&&(this.elementInfo.style.display="none")})}setClickListener(e){this.clickListener=e}setDeselectListener(e){this.deselectListener=e}selectElement(e){this.element.querySelector('td[data-el="el-'+e+'"]').className="cell el-selected"}deselectElement(e){this.element.querySelector('td[data-el="el-'+e+'"]').className="cell "+o(e)}deselectAllElements(){let e=this.element.querySelectorAll("td.el-selected");for(let t=0;t<e.length;++t){let s=e[t].getAttribute("data-el").substring(3);e[t].className="cell "+o(s)}}}},function(e,t,s){"use strict";let i=s(0),n=s(3),a=s(4);e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","matlist"),this.formula=!1,this.matMap=new Map,this.page=0,this.nPages=0,this.before=null,this.afters=new Map,this.searchJson=null,this.element.innerHTML='\n <div class="title"> Results <span class="n-materials"></span></div>\n <div class="pag-header">\n <span class="prevButton">\n <img src="img/prev.svg" style="display: inline;" width="7px"/> prev\n </span> \n <span class="page"> X </span> \n <span class="nextButton"> next \n <img src="img/next.svg" width="7px" />\n </span>\n </div>\n <div class="data-container"> </div>',this.titleBox=this.element.getElementsByClassName("title")[0],this.nMaterials=this.element.getElementsByClassName("n-materials")[0],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(e){e.appendChild(this.element),this._events()}_events(){this.nextButton.addEventListener("click",e=>{this.page++,this._search()}),this.prevButton.addEventListener("click",e=>{1!==this.page&&(this.page--,this._search())}),this.resultsContainer.addEventListener("click",e=>{if(e.target!==e.currentTarget){let t;"mat-row"===e.target.className?t=e.target:"mat-row"===e.target.parentElement.className?t=e.target.parentElement:"mat-row"===e.target.parentElement.parentElement.className&&(t=e.target.parentElement.parentElement),t&&i.setBrowserHashPath("material",t.getAttribute("data-mat-id")),e.stopPropagation()}})}_printMatMap(){console.log("MATMAP:"),this.matMap.forEach((function(e,t,s){console.log(t+" "+JSON.stringify(e))}))}setSearch(e){this.resultsContainer.style.visibility="hidden",this.searchJson=e,this.page=1,this._search()}_search(){this.searchJson.search_by.page=this.page,this.searchJson.search_by.after=this.afters.get(this.page),this.searchJson.search_by.per_page=10;let e=JSON.stringify(this.searchJson);1===this.page?this.prevButton.style.visibility="hidden":this.prevButton.style.visibility="visible",a.reset(),window.allowNewLoadPopup=!0,a.show(),i.serverReqPOST(i.getSearchURL(),e,e=>{let t=JSON.parse(e.target.response);if(200===e.target.status){this.afters.set(this.page+1,t.pages.after),1===this.page&&(this.nMaterials.innerHTML="(total: "+i.numberWithCommas(t.pages.total)+")",this.nPages=Math.ceil(t.pages.total/this.searchJson.search_by.per_page)),0===t.results.length||this.page===this.nPages?this.nextButton.style.visibility="hidden":this.nextButton.style.visibility="visible";let e=t.results;0===t.results.length?(this.setData([]),i.searchResults=!1):1===t.results.length?(i.setBrowserHashPath("material",e[0].material_id),i.searchResults=!1):(this.setData(e),i.searchResults=!0)}else this.setData([]),this.nextButton.style.visibility="hidden";this._updateUI(),this.resultsContainer.style.visibility="visible",a.hide()}).addEventListener("error",e=>{console.log("Search ERROR - Not valid query "),this.setData([]),this._updateUI(),this.resultsContainer.style.visibility="visible",a.hide()})}setData(e){this.matMap.clear(),e.forEach(e=>{if(this.matMap.has(e.formula_reduced)){this.matMap.get(e.formula_reduced).push(e)}else{let t=[];t.push(e),this.matMap.set(e.formula_reduced,t)}})}reset(){this.formula=!1,this.matMap.clear(),this.page=0,this.searchJson=null,this._updateUI()}_updateUI(){this.pageElement.innerHTML="page "+this.page+"/"+i.numberWithCommas(this.nPages);let e="";0===this.matMap.size?(1===this.page&&(this.resultsNrTag.style.display="none",this.titleBox.style.display="none"),e+='<div class="not-found">No results found</div>'):(this.resultsNrTag.style.display="block",this.titleBox.style.display="block",e+='\n <table>\n <thead> <tr>\n <th style="width: 24%;"></th>\n <th style="width: 20%;">\n <span info-sys-data="material-type">Material type</span>\n </th>\n <th style="width: 16%;">\n <span info-sys-data="space-group">Space group</span>\n </th>\n <th style="width: 22%;">\n <span info-sys-data="structure-type">Structure type</span>\n </th>\n <th style="width: 18%;">Nº calculations</th>\n </tr> </thead>\n <tbody>\n ',this.matMap.forEach((t,s)=>{let n=i.getSubscriptedFormula(s);e+='<tr> <td class="formula" colspan="5"><b>'+n+"</b>",t.length>1&&(e+='<span style="font-size: 0.86em;"> ('+t.length+" structures)</span>"),e+="</td></tr>",t.forEach(t=>{let s=void 0!==t.material_name?t.material_name:n;e+=`<tr class="mat-row" data-mat-id="${t.material_id}">\n <td > ${s} </td>\n <td style="text-align:center" >\n \x3c!--<span info-sys-data="material-type">--\x3e${t.material_type} \x3c!--</span>--\x3e\n </td>\n <td style="text-align:center" >\n ${void 0===t.space_group_number?"":t.space_group_number}\n </td>\n <td> ${void 0===t.structure_type?"":t.structure_type} </td>\n <td style="text-align:center" > ${t.n_matches} </td>\n </tr>`})}),e+=" </tbody> </table>"),this.resultsContainer.innerHTML=e,n.addToInfoSystem(this.resultsContainer)}}},function(e,t,s){"use strict";let i=s(0),n=s(3);e.exports=class{constructor(){this.element=document.createElement("div"),this.element.setAttribute("id","search-properties-box"),this.element.innerHTML='\n <div class="props-box-tabs-wrapper" style="float: left; width: 25%; ">\n\n <div class="properties-box-tabs" >\n <div data-tab="structure" class="props-tab-selected" >Structure </div>\n <div data-tab="results">Properties </div>\n <div data-tab="method">Method </div>\n \x3c!-- <div class="contributors-tab">Contributors </div> --\x3e\n </div>\n\n </div>\n\n <div class="properties-box-panel" style="float: left;width: 75%;height: 100%">\n\n <div class="props-tab-panel-wrapper" style="height: 86%" >\n\n <div class="structure-panel props-tab-panel" style="display: block">\n\n\n <div style="float: left; width: 47%;">\n\n <div class="field">\n <div class="field-title"><span info-sys-data="space-group">Space group number</span></div>\n <input type="text" class="space-group-number-field">\n </div>\n\n <div class="field">\n <div class="field-title">Material type</div>\n <input type="checkbox" class="material-type-field" value="bulk"> Bulk<br>\n <input type="checkbox" class="material-type-field" value="2D"> 2D<br>\n <input type="checkbox" class="material-type-field" value="1D"> 1D<br>\n </div>\n\n \x3c!--\n <div class="field">\n <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>\n Min: <input type="text" class="mass-density-min-field"> \n Max: <input type="text" class="mass-density-max-field">\n </div>\n --\x3e\n\n </div>\n\n <div style="float: left; width: 5%;"></div>\n\n <div style="float: right; width: 47%;">\n\n <div class="field">\n <div class="field-title"><span info-sys-data="structure-type">Structure type</span></div>\n <select class="structure-type-field" style="max-width: 174px">\n <option></option>\n </select>\n </div>\n\n <div class="field">\n <div class="field-title"><span info-sys-data="crystal-system">Crystal system</span></div>\n <input type="checkbox" class="crystal-system-field" value="cubic">\n <span info-sys-data="crystal-system.value:cubic">Cubic</span><br>\n <input type="checkbox" class="crystal-system-field" value="hexagonal">\n <span info-sys-data="crystal-system.value:hexagonal">Hexagonal</span><br>\n <input type="checkbox" class="crystal-system-field" value="trigonal">\n <span info-sys-data="crystal-system.value:trigonal">Trigonal</span><br>\n <input type="checkbox" class="crystal-system-field" value="tetragonal">\n <span info-sys-data="crystal-system.value:tetragonal">Tetragonal</span><br>\n <input type="checkbox" class="crystal-system-field" value="orthorhombic">\n <span info-sys-data="crystal-system.value:orthorhombic">Orthorhombic</span><br>\n <input type="checkbox" class="crystal-system-field" value="monoclinic">\n <span info-sys-data="crystal-system.value:monoclinic">Monoclinic</span><br>\n <input type="checkbox" class="crystal-system-field" value="triclinic">\n <span info-sys-data="crystal-system.value:triclinic">Triclinic</span><br>\n </div>\n\n </div>\n <div style="clear: both;"></div>\n </div>\n\n <div class="results-panel props-tab-panel" >\n\n <div class="field" style="background-color: #CCC; padding: 10px;">\n <div style="font-weight: bold; padding-bottom: 6px" >Results containing...</div>\n <div style="float: left; widt: 47%;">\n <input type="checkbox" class="has-band-structure-field" value="Band structure">\n <span info-sys-data="has-band-structure">Band structure</span><br>\n <input type="checkbox" class="has-dos-field" value="DOS">\n <span info-sys-data="has-dos">DOS</span><br>\n \x3c!--\n <input type="checkbox" class="has-fermi-surface-field" value="Fermi surface">\n <span info-sys-data="has-fermi-surface">Fermi surface</span><br>\n --\x3e\n </div>\n <div style="float: right; width: 47%;">\n <input type="checkbox" class="has-thermal-properties-field" value="Thermal properties">\n <span info-sys-data="has-thermal-properties">Thermal properties</span>\n <br>\n \x3c!-- <input type="checkbox" class="results-containing-field" value="EOS" disabled>Equation of state<br>--\x3e\n </div>\n <div style="clear: both;"></div>\n </div>\n\n <div style="float: left; width: 47%;">\n <div class="field">\n <div class="field-title"><span info-sys-data="band-gap">Band gap</span> <span style="font-weight: normal;">(eV)</span></div>\n <div style="display: flex; height: 20px; align-items: center; margin-top: 5px">\n <input type="text" class="band-gap-min-field">\n <p style="padding: 5px">-</p>\n <input type="text" class="band-gap-max-field">\n </div>\n </div>\n </div>\n\n \x3c!--<div style="float: left; width: 5%;"> </div> --\x3e\n\n \x3c!--\n <div style="float: right; width: 47%;">\n <div class="field" style="padding-top: 28px;">\n <input type="radio" name="band-gap-type" value="d"> Direct<br>\n <input type="radio" name="band-gap-type" value="i"> Indirect<br>\n <input type="radio" name="band-gap-type" value="d/i" checked> Both<br>\n </div>\n </div>\n --\x3e\n <div style="clear: both;"></div>\n\n </div>\n\n <div class="method-panel props-tab-panel" >\n <div style="float: left; width: 47%;">\n\n <div class="field">\n <div class="field-title"><span info-sys-data="functional-type">Functional type</span></div>\n <input type="checkbox" class="functional-type-field" value="LDA">\n <span info-sys-data="functional-type.value:LDA">LDA</span><br>\n <input type="checkbox" class="functional-type-field" value="GGA">\n <span info-sys-data="functional-type.value:GGA">GGA</span><br>\n <input type="checkbox" class="functional-type-field" value="meta-GGA">\n <span info-sys-data="functional-type.value:meta-GGA">meta-GGA</span><br>\n <input type="checkbox" class="functional-type-field" value="hybrid-GGA">\n <span info-sys-data="functional-type.value:hybrid-GGA">hybrid-GGA</span><br>\n <input type="checkbox" class="functional-type-field" value="hybrid-meta-GGA">\n <span info-sys-data="functional-type.value:hybrid-meta-GGA">hybrid-meta-GGA</span><br>\n <input type="checkbox" class="functional-type-field" value="HF">\n <span info-sys-data="functional-type.value:HF">HF</span><br>\n <input type="checkbox" class="functional-type-field" value="GW">\n <span info-sys-data="functional-type.value:GW">GW</span><br>\n </div>\n\n <div class="field">\n <div class="field-title"><span info-sys-data="code-name">Code name</span></div>\n <select class="code-name-field" >\n <option></option>\n </select>\n </div>\n\n </div>\n\n <div style="float: left; width: 5%;"> </div>\n\n <div style="float: right; width: 47%;">\n <div class="field">\n <div class="field-title"><span info-sys-data="basis-set">Basis set</span></div>\n <input type="checkbox" class="basis-set-field" value="(L)APW+lo">\n <span info-sys-data="basis-set.value:(L)APW+lo">(L)APW+lo</span><br>\n <input type="checkbox" class="basis-set-field" value="LCAO">\n <span info-sys-data="basis-set.value:LCAO">LCAO</span><br>\n <input type="checkbox" class="basis-set-field" value="gaussians">\n <span info-sys-data="basis-set.value:gaussians">gaussians</span><br>\n <input type="checkbox" class="basis-set-field" value="numeric AOs">\n <span info-sys-data="basis-set.value:numeric AOs">numeric AOs</span><br>\n <input type="checkbox" class="basis-set-field" value="plane waves">\n <span info-sys-data="basis-set.value:plane waves">plane waves</span><br>\n <input type="checkbox" class="basis-set-field" value="psinc functions">\n <span info-sys-data="basis-set.value:psinc functions">psinc functions</span><br>\n <input type="checkbox" class="basis-set-field" value="real-space grid">\n <span info-sys-data="basis-set.value:real-space grid">real-space grid</span><br>\n </div>\n </div>\n\n <div style="clear: both;"></div>\n </div>\n\n </div>\n\n\n <div class="properties-box-enter-button" style="height: 14%">\n <button disabled>Add to query</button>\n </div>\n\n </div>\n\n <div style="clear: both;"></div>\n ',this.tabsElement=this.element.querySelector(".properties-box-tabs"),this.tabSelected=this.element.querySelector('[data-tab="structure"]'),this.tabPanelSelected=this.element.querySelector(".structure-panel"),this.addButton=this.element.querySelector(".properties-box-enter-button button");let e=this.element.querySelector(".code-name-field"),t=i.serverReq(i.getSuggestionURL("code_name"),()=>{JSON.parse(t.response).code_name.forEach(t=>{e.innerHTML+="<option>"+t+"</option>"})}),s=this.element.querySelector(".structure-type-field"),a=i.serverReq(i.getSuggestionURL("structure_type"),()=>{JSON.parse(a.response).structure_type.forEach(e=>{s.innerHTML+="<option>"+e+"</option>"})});function l(e,t){["input","keydown","keyup","mousedown","mouseup","select","contextmenu","drop"].forEach((function(s){e.addEventListener(s,(function(){t(this.value)?(this.oldValue=this.value,this.oldSelectionStart=this.selectionStart,this.oldSelectionEnd=this.selectionEnd):this.hasOwnProperty("oldValue")?(this.value=this.oldValue,this.setSelectionRange(this.oldSelectionStart,this.oldSelectionEnd)):this.value=""}))}))}n.addToInfoSystem(this.element),this._events(),l(this.element.getElementsByClassName("band-gap-min-field")[0],(function(e){return/^\d*\.?\d*$/.test(e)})),l(this.element.getElementsByClassName("band-gap-max-field")[0],(function(e){return/^\d*\.?\d*$/.test(e)}))}_events(){this.tabsElement.addEventListener("click",e=>{if(e.target!==e.currentTarget){null!==this.tabSelected&&(this.tabSelected.className=""),this.tabSelected=e.target,this.tabSelected.className="props-tab-selected";let t=e.target.getAttribute("data-tab");null!==this.tabPanelSelected&&(this.tabPanelSelected.style.display="none"),this.tabPanelSelected=this.element.getElementsByClassName(t+"-panel")[0],this.tabPanelSelected.style.display="block",this.checkPropsValues()}}),this.addButton.addEventListener("click",e=>{let t=this.getPropsWithValueFromCurrentTab(!0);this.addPropertiesListener(t),this.addButton.disabled=!0});let e=this.element.querySelector(".properties-box-panel");e.addEventListener("input",e=>{this.checkPropsValues()}),e.addEventListener("change",e=>{this.checkPropsValues()})}checkPropsValues(){let e=this.getPropsWithValueFromCurrentTab(!1);this.addButton.disabled=0===e.size}getPropsWithValueFromCurrentTab(e){let t=this.tabSelected.getAttribute("data-tab"),s=new Map;return"structure"===t?(this.addPropsFromTextFields(s,["space-group-number"],e),this.addPropsFromDropdownList(s,["structure-type"],e),this.addPropsFromCheckboxes(s,["material-type","crystal-system"],e)):"results"===t?(this.addBandgapProps(s,e),this.addPropsFromCheckboxes(s,["has-band-structure","has-dos","has-fermi-surface","has-thermal-properties"],e)):"method"===t&&(this.addPropsFromCheckboxes(s,["functional-type","basis-set"],e),this.addPropsFromDropdownList(s,["code-name"],e)),s}addPropsFromTextFields(e,t,s){t.forEach(t=>{let i=this.element.querySelector("."+t+"-field");""!==i.value&&(e.set(t,[i.value]),s&&(i.value=""))})}addPropsFromCheckboxes(e,t,s){t.forEach(t=>{let i=this.element.querySelectorAll("."+t+"-field"),n=[];for(var a=0;a<i.length;a++)i[a].checked&&(n.push(i[a].value),s&&(i[a].checked=!1));n.length>0&&e.set(t,n)})}addPropsFromDropdownList(e,t,s){t.forEach(t=>{let i=this.element.querySelector("."+t+"-field"),n=i.options[i.selectedIndex].value;n.length>2&&e.set(t,[n]),s&&(i.selectedIndex=0)})}addBandgapProps(e,t){let s=document.querySelector(".band-gap-min-field"),i=document.querySelector(".band-gap-max-field"),n="Band Gap",a=!0;""!==s.value&&(a=!1,n=s.value+" < "+n,t&&(s.value="")),""!==i.value&&(a=!1,n+=" < "+i.value,t&&(i.value="")),a||e.set("band-gap",[n])}addMassDensityProps(e,t){let s=document.querySelector(".mass-density-min-field"),i=document.querySelector(".mass-density-max-field"),n="mass-density",a="Mass Density";""!==s.value&&(a=s.value+" < "+a,n+=":"+s.value,t&&(s.value="")),""!==i.value&&(a+=" < "+i.value,n+=":"+i.value,t&&(i.value="")),"Mass Density"!==a&&e.set(n,[a])}setAddPropertiesListener(e){this.addPropertiesListener=e}}}]); \ No newline at end of file +/* + * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development"). + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). + */ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ "./src/common/FlaggingFormPopup.js": +/*!*****************************************!*\ + !*** ./src/common/FlaggingFormPopup.js ***! + \*****************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component is the popup window (and the darkened background) with a form\n inside used for users to report (flagging) errors on the calculations\n */\n\n\n\nlet util = __webpack_require__(/*! ./util.js */ \"./src/common/util.js\");\nlet DataStore = __webpack_require__(/*! ../material-mod/DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n// main DOM elements\nlet flaggingPopupBackground = document.querySelector('#flagging-form-popup-bg');\nlet flaggingFormPopup = document.querySelector('#flagging-form-popup');\nflaggingFormPopup.innerHTML =`\n\n<div> <img src=\"img/cross.svg\" height=\"12px\"\n style=\"float: right; cursor: pointer\" />\n</div>\n\n<div class=\"form-wrapper\">\n <div class=\"popup-title\"> Error reporting</div>\n <br>\n <div> Material: <span id=\"error-material-id\"></span></div>\n <select id=\"flagging-category\" name=\"category\">\n <option value=\"\">Select a category *</option>\n <option value=\"structure\">Structure</option>\n <option value=\"electronicstruct\">Electronic structure</option>\n <option value=\"methodology\">Methodology</option>\n <option value=\"thermalprops\">Thermal properties</option>\n </select>\n\n <select id=\"flagging-subcategory\" name=\"subcategory\">\n\n </select>\n\n <textarea id=\"subject\" name=\"subject\" style=\"height:200px\"\n placeholder=\"Write a short explanation about the error\" ></textarea>\n\n <div id=\"form-validation-msg\"> </div>\n\n <div style=\"display: flex; justify-content: space-evenly;\">\n <button style=\"display: block\">Send</button>\n </div>\n\n\n</div>\n`;\nlet categoryField = flaggingFormPopup.querySelector('#flagging-category');\nlet eStructOption = categoryField.querySelector('option[value=\"electronicstruct\"]');\nlet thermalOption = categoryField.querySelector('option[value=\"thermalprops\"]');\nlet subcategoryField = flaggingFormPopup.querySelector('#flagging-subcategory');\nlet closeButton = flaggingFormPopup.querySelector('img');\nlet validationMsg = flaggingFormPopup.querySelector('#form-validation-msg');\nlet sendButton = flaggingFormPopup.querySelector('button');\nlet materialIdField = flaggingFormPopup.querySelector('#error-material-id');\nlet overviewEStructCalcs = null;\n\nfunction _setCurrentPage(pageId) {\n subcategoryField.innerHTML = '';\n let materialId = DataStore.getMaterialData().material_id;\n materialIdField.textContent = materialId;\n\n if (pageId === null) {\n categoryField.disabled = false;\n subcategoryField.style.display = 'none';\n } else {\n categoryField.disabled = true;\n subcategoryField.style.display = 'block';\n subcategoryField.appendChild(createOption('Choose the subcategory *', ''));\n\n switch (pageId) {\n\n case util.MAT_VIEW.structure:\n categoryField.selectedIndex = 1;\n subcategoryField.appendChild(createOption('Structure representation'));\n subcategoryField.appendChild(createOption('Calculation tree'));\n subcategoryField.appendChild(createOption('Summary'));\n subcategoryField.appendChild(createOption('Specific calculation'));\n break;\n\n case util.MAT_VIEW.electronicstruct:\n categoryField.selectedIndex = 2;\n subcategoryField.appendChild(createOption('Calculation tree'));\n subcategoryField.appendChild(createOption('Summary'));\n subcategoryField.appendChild(createOption('Band structure'));\n subcategoryField.appendChild(createOption('DOS'));\n subcategoryField.appendChild(createOption('Brillouin zone'));\n break;\n\n case util.MAT_VIEW.methodology:\n categoryField.selectedIndex = 3;\n subcategoryField.style.display = 'none';\n break;\n\n case util.MAT_VIEW.thermalprops:\n categoryField.selectedIndex = 4;\n subcategoryField.appendChild(createOption('Calculation tree'));\n subcategoryField.appendChild(createOption('Phonon dispersion'));\n subcategoryField.appendChild(createOption('Phonon DOS'));\n subcategoryField.appendChild(createOption('Specific heat'));\n subcategoryField.appendChild(createOption('Helmholtz free energy'));\n break;\n }\n }\n}\n\nfunction show(pageStatus) {\n overviewEStructCalcs = pageStatus.eStructCalcs;\n\n // Show/hide some dropdown list options\n eStructOption.style.display = (DataStore.hasElecStructureData() ? 'block' : 'none');\n thermalOption.style.display = (DataStore.hasThermalData() ? 'block' : 'none');\n\n _setCurrentPage(pageStatus.pageId);\n\n let ttRect = flaggingFormPopup.getBoundingClientRect();\n let leftPos = (window.innerWidth - ttRect.width)/2;\n let topPos = (window.innerHeight -ttRect.height)/2;\n flaggingFormPopup.style.left = leftPos+'px';\n flaggingFormPopup.style.top = (topPos-20)+'px';\n\n flaggingFormPopup.style.visibility = 'visible';\n flaggingPopupBackground.style.visibility = 'visible';\n}\n\nfunction hide(){\n flaggingPopupBackground.style.visibility = 'hidden';\n flaggingFormPopup.style.visibility = 'hidden';\n // reset UI\n categoryField.selectedIndex = 0;\n subcategoryField.selectedIndex = 0;\n flaggingFormPopup.querySelector('textarea').value = '';\n validationMsg.innerHTML = '';\n}\n\nfunction createOption(text, value){\n let opt = document.createElement('option');\n opt.value = (value === undefined ? text : value);\n opt.innerHTML = text;\n return opt;\n}\n\ncloseButton.addEventListener('click', e => {\n hide();\n});\n\nsendButton.addEventListener('click', e => {\n\n // First double-check that the authentication is still valid.\n let categoryChosen = categoryField.options[categoryField.selectedIndex];\n var keycloak = window.keycloak;\n if (!keycloak.authenticated) {\n validationMsg.innerHTML = \"Your authentication has expired. Please login again.\";\n return;\n }\n\n // Check fields\n if (!categoryField.disabled && categoryChosen.value === '') { // Overview case\n validationMsg.innerHTML = 'The category fields must be set';\n return;\n } else if (categoryField.disabled && subcategoryField.value === '' // Details pages case\n && categoryChosen.value !== util.MAT_VIEW.methodology) {\n validationMsg.innerHTML = 'The subcategory fields must be set';\n return;\n }\n \n // Send report\n validationMsg.innerHTML = 'Sending report...';\n let textareaText = flaggingFormPopup.querySelector('textarea').value;\n let materialId = DataStore.getMaterialData().material_id;\n keycloak.loadUserProfile()\n .then(function(profile) {\n let report = {};\n report.server = util.getServerLocation();\n report.username = profile.username;\n report.email = profile.email;\n report.first_name = profile.firstName;\n report.last_name = profile.lastName;\n report.message = textareaText;\n report.category = categoryChosen.text;\n\n // Overview page\n if (!categoryField.disabled) {\n report.representatives = DataStore.getRepresentatives();\n } else { // Details pages\n report.subcategory = subcategoryField.options[subcategoryField.selectedIndex].text;\n }\n\n util.serverReqPOST(util.getReportURL(materialId), JSON.stringify(report), e => {\n if (e.target.status === 204) {\n hide();\n } else {\n validationMsg.innerHTML = \"Could not connect to the service. Please check your connection and try again later.\";\n }\n });\n })\n .catch(error => {\n console.log(error);\n validationMsg.innerHTML = \"Could not connect to the service. Please check your connection and try again later.\";\n });\n});\n\n// EXPORTS\nmodule.exports = { show, hide };\n\n\n//# sourceURL=webpack:///./src/common/FlaggingFormPopup.js?"); + +/***/ }), + +/***/ "./src/common/InfoSys.js": +/*!*******************************!*\ + !*** ./src/common/InfoSys.js ***! + \*******************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements an information system for the user that provides info\n related to the concepts or quantities shown on the UI\n The literals are read form the file infosys.json\n */\n\n\n\nlet util = __webpack_require__(/*! ./util.js */ \"./src/common/util.js\");\nlet SwitchComponent = __webpack_require__(/*! ./SwitchComponent.js */ \"./src/common/SwitchComponent.js\");\n\nconst INFOSYS_FILE_PATH = 'infosys.json';\n\nlet tooltip = document.querySelector('#info-tooltip');\nlet tooltipContent = document.querySelector('#tooltip-content');\n\nlet elements = [];\nlet timerSet = null;\nlet on = false;\nlet data = null;\n\nfunction clearCurrentTimeoutAnSetANew(){\n if (timerSet !== null) window.clearTimeout(timerSet);\n timerSet = window.setTimeout(t => tooltip.style.display = 'none', 1000);\n}\n\n\nlet switchComponent = new SwitchComponent(util.IMAGE_DIR+'switch');\ndocument.querySelector('#info-sys-switch-box').appendChild(switchComponent.element);\n\nswitchComponent.setListener( off => {\n on = !off;\n if (off) {\n elements.forEach( element => {\n element.removeEventListener('mouseover', mouseOver);\n element.className = '';\n });\n }else{\n if (data === null)\n util.serverReq(INFOSYS_FILE_PATH, e => data = JSON.parse(e.target.response));\n\n elements.forEach(enableTooltip);\n }\n\n\n tooltip.addEventListener('mouseover', e => {\n window.clearTimeout(timerSet);\n });\n\n tooltip.addEventListener('mouseout', e => {\n clearCurrentTimeoutAnSetANew();\n });\n\n});\n\n\nfunction addToInfoSystem(baseElement){\n let infosysLabels = baseElement.querySelectorAll('span[info-sys-data]');\n\n for (let i = 0; i < infosysLabels.length; ++i)\n elements.push(infosysLabels[i]);\n\n //if (on) infosysLabels.forEach(enableTooltip);\n if (on)\n for (let i = 0; i < infosysLabels.length; ++i)\n enableTooltip(infosysLabels[i]);\n}\n\n\nfunction addElementToInfoSystem(element, value){\n elements.push(element);\n element.setAttribute('info-sys-data',value);\n\n if (on) enableTooltip(element);\n}\n\n\nfunction enableTooltip(element){\n element.addEventListener('mouseover', mouseOver);\n element.addEventListener('mouseout', e => {\n clearCurrentTimeoutAnSetANew();\n element.style.cursor = 'inherit';\n });\n element.className = 'info-sys-label';\n}\n\n\nfunction mouseOver(e){\n let r = e.target.getBoundingClientRect();\n let quantity = e.target.getAttribute('info-sys-data');\n\n let index = quantity.indexOf('.value');\n if (index > 0){ // quantity value\n let quantityObject = data[quantity.split('-').join(' ').substring(0, index)];\n //console.log('VALUE', quantityObject);\n if (quantityObject.value_template === undefined){ //direct value\n let valueObj = quantityObject.values[quantity.split(':')[1]];\n tooltipContent.innerHTML = getHTML(valueObj);\n }else{ // value template\n let object = quantityObject.value_template;\n object.text = templating(object.text, quantity.split(':')[1]);\n object.link = templating(object.link, quantity.split(':')[1]);\n tooltipContent.innerHTML = getHTML(object);\n //console.log('VALUE TEMPLATE: ', object);\n }\n }else // quantity name\n tooltipContent.innerHTML = getHTML(data[quantity.split('-').join(' ')]);\n\n tooltip.style.visibility = 'hidden';\n tooltip.style.display = 'block';\n let ttRect = tooltip.getBoundingClientRect();\n let leftOffset = ttRect.width - r.width;\n let leftPos = r.left -leftOffset/2;\n if (leftPos + ttRect.width > window.innerWidth)\n leftPos = window.innerWidth -ttRect.width;\n //let topOffset = ttRect.height + 20 - window.pageYOffset;\n let topOffset = - window.pageYOffset;\n tooltip.style.left = (leftPos < 0 ? 5 : leftPos)+'px';\n tooltip.style.top = (r.top + r.height -topOffset)+'px';\n tooltip.style.visibility = 'visible';\n window.clearTimeout(timerSet);\n e.target.style.cursor = 'help';\n}\n\n\nfunction templating(s, param) {\n let initIndex = s.indexOf('${');\n let finalIndex = s.indexOf('}');\n if (initIndex >= 0 && finalIndex >= 0 && finalIndex > initIndex){\n return s.substring(0,initIndex)+param+s.substring(finalIndex+1);\n }else return s;\n}\n\nfunction getHTML(object) {\n let html = '';\n if (object.text === undefined){\n //html = 'NO TEXT!! Comment: '+object.comment;\n }else{ // there is text attr\n html += object.text;\n }\n if (object.link !== undefined){\n if (object.text !== undefined) html += '<br>';\n html += '<a href=\"'+object.link+'\" target=\"_blank\">More information</a>';\n }\n\n return html;\n}\n\n\n// EXPORTS\nmodule.exports = { addToInfoSystem, addElementToInfoSystem };\n\n\n//# sourceURL=webpack:///./src/common/InfoSys.js?"); + +/***/ }), + +/***/ "./src/common/InteractivePlotterBase.js": +/*!**********************************************!*\ + !*** ./src/common/InteractivePlotterBase.js ***! + \**********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n This is the base class that models a highly interactive plotter.\n It is inherited by several classes implementing specifc plotters.\n This plotter implements zoom and y-axis offset\n */\n\n\n\n\n\nlet svg = __webpack_require__(/*! ./SVG.js */ \"./src/common/SVG.js\");\n\nconst xmlns=\"http://www.w3.org/2000/svg\";\nconst xlink=\"http://www.w3.org/1999/xlink\";\n\n\nclass InteractivePlotterBase{\n\n constructor(margins = {left: 20, right: 0, top: 0, bottom: 20}) {\n this.margins= margins;\n this.svg = document.createElementNS(xmlns, \"svg\");\n\n this.parentElement= null;\n this.plotContent = null;\n this.axisGroup = null;\n this.yAxisLabelsGroup = null;\n this.yLabelText = null; // If null, y axis label and numbers are not painted\n this.noDataGroup = null;\n\n this.yZoom = 1; // Initial zoom\n this.yOffset = 0; // Initial y offset = 0\n this.repaintListener = null;\n this.nodataLabel = null;\n\n this.outOfRangeColorActivated = true;\n }\n\n\n attach(element, width, height){\n this.parentElement= element;\n this.parentElement.appendChild(this.svg);\n this.width = (width !== undefined ? width : this.parentElement.clientWidth);\n this.height = (height !== undefined ? height : this.svg.width);\n this.svg.setAttribute(\"width\", this.width);\n this.svg.setAttribute(\"height\", this.height);\n this.plotRangeX = this.width - this.margins.left - this.margins.right;\n this.plotRangeY = this.height - this.margins.top - this.margins.bottom;\n\n // y axis area (zoomable)\n this.yAxisArea = document.createElementNS(xmlns, \"svg\");\n this.svg.appendChild(this.yAxisArea);\n let yAxisAreaWidth = this.margins.left;\n this.plotAreaHeight = this.height - this.margins.bottom - this.margins.top;\n this.yAxisArea.setAttribute(\"width\", yAxisAreaWidth);\n this.yAxisArea.setAttribute(\"height\", this.plotAreaHeight);//-OVERLAP_CORRECTOR);\n this.yAxisArea.setAttribute(\"x\", 0);\n this.yAxisArea.setAttribute(\"y\", 0);//OVERLAP_CORRECTOR);\n\n // SVG plot area window\n this.plotArea = document.createElementNS(xmlns, \"svg\");\n this.svg.appendChild(this.plotArea);\n this.plotAreaWidth = this.width - this.margins.left - this.margins.right;\n this.plotArea.setAttribute(\"width\", this.plotAreaWidth);\n this.plotArea.setAttribute(\"height\", this.plotAreaHeight);\n this.plotArea.setAttribute(\"x\", this.margins.left);\n this.plotArea.setAttribute(\"y\", this.margins.top);\n\n // Rect filling plot area in order to support styles and events\n this.plotAreaBg = svg.addRect(this.plotArea, 0, 0, this.plotAreaWidth, this.plotAreaHeight);\n this.plotAreaBg.setAttribute('class', 'moveable-plot');\n this.plotAreaBg.setAttribute('opacity', 0.0);\n\n this. _events();\n }\n\n\n isAttached(){\n return this.parentElement !== null;\n }\n\n\n setAxisRangeAndLabels(xLabel, xMin, xMax, yLabel, yMinInit, yMaxInit,\n yMin, yMax, yLabelGap, decimals = 2){\n this.xLabel= xLabel;\n this.xMin = xMin;\n this.xMax = xMax;\n this.yMinInit= yMinInit;\n this.yMaxInit= yMaxInit;\n this.yMin= yMin;\n this.yMax= yMax;\n this.yLabelGapInit = yLabelGap;\n this.xRel= this.plotRangeX/(this.xMax-this.xMin);\n this.yRel= this.plotRangeY/(this.yMaxInit-this.yMinInit);\n\n this._resetAxisGroup();\n\n // Draw axes\n svg.addLine(this.axisGroup, 0,0,this.plotRangeX,0, 'main-axis');\n \tsvg.addLine(this.axisGroup, 0,0,0,-this.plotRangeY ,'main-axis');\n svg.addLine(this.axisGroup, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY, 'main-axis');\n \tsvg.addLine(this.axisGroup, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY, 'main-axis');\n\n // Paint x and y axes labels\n if (yLabel !== null){\n this.yLabelText = svg.addText(this.svg, 0, 0, yLabel, 'middle', 'axis-steps-big');\n this.yLabelText.setAttribute('transform','translate(15,'+(this.plotRangeY/2+this.margins.top)+') rotate(-90)');\n }\n if (xLabel !== null)\n svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom-12, this.xLabel, 'middle', 'axis-steps-big');\n\n // initialize y axis steps (dynamic construction) container\n this._resetYAxisLabelGroup();\n\n // transformY precalcultation 1\n this.precalculation_1 = this.plotAreaHeight + this.yMinInit*this.yRel;\n this.precalculation_2 = this.precalculation_1;//- this.yOffset // when this quantity changes\n }\n\n\n _events(){\n\n this.plotArea.addEventListener('wheel', (e) => {\n e.preventDefault();\n if (e.deltaY > 0 && this.yZoom > 0.5 ) this.yZoom -= 0.2;\n else if (e.deltaY < 0 && this.yZoom < 2) this.yZoom += 0.2;\n this.repaint();\n if (this.repaintListener !== null)\n this.repaintListener(this.yZoom, this.yOffset);\n });\n\n let initPosY;\n this.plotArea.addEventListener('mousedown', (e) => {\n e.preventDefault();\n initPosY = e.clientY + this.yOffset;\n //console.log('mousedown: e.clientY + this.yOffset', e.clientY, this.yOffset);\n this.plotArea.addEventListener('mousemove', moveListener);\n\n this.plotArea.addEventListener('mouseup', (e) => {\n this.plotArea.removeEventListener('mousemove', moveListener);\n });\n this.plotArea.addEventListener('mouseout', (e) => {\n this.plotArea.removeEventListener('mousemove', moveListener);\n });\n });\n\n let self = this;\n function moveListener(e) {\n //console.log('Y offset:', e.clientY - initPosY);\n // Bad if (initPosY - e.clientY > this.yMax || initPosY - e.clientY < this.yMin)\n self.yOffset = initPosY - e.clientY ;\n self.precalculation_2 = self.precalculation_1 - self.yOffset;\n self.repaint();\n if (self.repaintListener !== null)\n self.repaintListener(self.yZoom, self.yOffset);\n }\n }\n\n\n setYZoomAndOffset(yZoom, yOffset){\n this.yZoom = yZoom;\n this.yOffset = yOffset;\n this.precalculation_2 = this.precalculation_1 - this.yOffset;\n }\n\n\n setExternalYAxisMax(externalYAxisMax){\n this.externalYAxisMax = externalYAxisMax;\n }\n\n\n getYAxisMax(){\n return this.yAxisMax;\n }\n\n\n repaint(){\n // repaint Y axis\n this._resetYAxisLabelGroup();\n\n let yLabelGap;\n if (this.yZoom > 1) yLabelGap = this.yLabelGapInit/5;\n else yLabelGap = this.yLabelGapInit;\n\n let min = Math.floor(this.yMin/yLabelGap)*yLabelGap;\n let max = Math.ceil(this.yMax/yLabelGap)*yLabelGap;\n this.yAxisMax = max;\n if (this.externalYAxisMax !== undefined) max = this.externalYAxisMax;\n\n if (this.yLabelText !== null) {\n for (let i = min; i < max+1; i = i + yLabelGap) {\n svg.addLine(this.yAxisLabelsGroup, this.margins.left,\n this.transformY(i), this.margins.left-3, this.transformY(i));\n svg.addText(this.yAxisLabelsGroup, this.margins.left-5,\n this.transformY(i)+5, i, 'end', 'axis-steps');\n }\n }\n\n // repaint plot content\n this._resetPlotContent();\n\n // Out of range areas\n if (this.outOfRangeColorActivated){\n let area = svg.addRect(this.plotContent, 0, this.transformY(this.yMax)-2*this.plotAreaHeight, this.plotAreaWidth, 2*this.plotAreaHeight);\n area.setAttribute('class', 'out-of-range');\n let area1 = svg.addRect(this.plotContent, 0, this.transformY(this.yMin), this.plotAreaWidth, 2*this.plotAreaHeight);\n area1.setAttribute('class', 'out-of-range');\n }\n\n // Zero line\n svg.addLine(this.plotContent, 0, this.transformY(0), this.plotRangeX,\n this.transformY(0), 'zeroline');\n\n // repaint data lines\n this.repaintData(min, max);\n\n // Add the top layer: rect for events\n this.plotArea.removeChild(this.plotAreaBg);\n this.plotArea.appendChild(this.plotAreaBg);\n }\n\n\n setRepaintListener(listener) {\n this.repaintListener = listener;\n }\n\n\n transformY(y) {\n // Precalculation usage\n // this.plotAreaHeight -y*this.yZoom*this.yRel + this.yMinInit*this.yRel - this.yOffset\n // -y*this.yZoom*this.yRel (calculated here) this.plotAreaHeight + this.yMinInit*this.yRel - this.yOffset\n let result = -y*this.yZoom*this.yRel + this.precalculation_2;\n return result;\n }\n\n\n _resetPlotContent(){\n // Try to delete with textContent property\n if (this.plotContent !== null)\n this.plotArea.removeChild(this.plotContent);\n this.plotContent = document.createElementNS(xmlns, \"g\");\n this.plotArea.appendChild(this.plotContent);\n }\n\n\n _resetAxisGroup(){\n if (this.axisGroup !== null)\n this.svg.removeChild(this.axisGroup);\n this.axisGroup = document.createElementNS(xmlns, 'g');\n this.svg.appendChild(this.axisGroup);\n\n // The y axis is inverted so the y coordinate has to be multiplied by -1\n this.axisGroup.setAttribute(\"transform\", 'matrix(1 0 0 1 '+\n this.margins.left+' '+(this.height - this.margins.bottom)+')');\n }\n\n\n _resetYAxisLabelGroup(){\n if (this.yLabelText === null) return;\n\n if (this.yAxisLabelsGroup !== null)\n this.yAxisArea.removeChild(this.yAxisLabelsGroup);\n this.yAxisLabelsGroup = document.createElementNS(xmlns, 'g');\n this.yAxisArea.appendChild(this.yAxisLabelsGroup);\n //this.yAxisLabelsGroup.setAttribute(\"transform\", 'matrix(1 0 0 1 0 -'+OVERLAP_CORRECTOR+')');\n }\n\n\n _reset() {\n this.yZoom = 1; // Initial zoom\n this.yOffset = 0; // Initial y offset = 0\n\n // initialize plot content (dynamic construction) container\n this._resetPlotContent();\n\n if (this.yLabelText !== null){\n this.svg.removeChild(this.yLabelText);\n this.yLabelText = null;\n }\n\n if (this.noDataGroup !== null){\n this.svg.removeChild(this.noDataGroup);\n this.noDataGroup = null;\n }\n }\n\n setNoData(){\n this._resetYAxisLabelGroup();\n this._resetPlotContent();\n this._resetAxisGroup();\n if (this.noDataGroup === null){\n this.noDataGroup = document.createElementNS(xmlns, 'g');\n this.svg.appendChild(this.noDataGroup);\n svg.addRect(this.noDataGroup, 0, 0, this.width, this.height);\n this.noDataGroup.setAttribute('fill', '#EEE');\n svg.addText(this.noDataGroup, this.width/2, this.height/2+10,\n 'NO DATA', 'middle', 'nodata');\n }\n\n }\n\n}\n\n// EXPORTS\nmodule.exports = InteractivePlotterBase;\n\n\n//# sourceURL=webpack:///./src/common/InteractivePlotterBase.js?"); + +/***/ }), + +/***/ "./src/common/LoadingPopup.js": +/*!************************************!*\ + !*** ./src/common/LoadingPopup.js ***! + \************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component is the loading popup window \n */\n\n\n\n\nlet util = __webpack_require__(/*! ./util.js */ \"./src/common/util.js\");\n\nlet loadingPopup = document.querySelector('#loading-popup');\nlet loadSet = new Set();\n\nfunction show(id) {\n if (!window.allowNewLoadPopup) {\n return;\n }\n loadSet.add(id, true);\n let ttRect = loadingPopup.getBoundingClientRect();\n let leftPos = (window.innerWidth - ttRect.width)/2;\n let topPos = (window.innerHeight -ttRect.height)/2;\n loadingPopup.style.left = leftPos+'px';\n loadingPopup.style.top = (topPos-100)+'px';\n loadingPopup.style.visibility = 'visible';\n}\n\nfunction hide(id) {\n loadSet.delete(id);\n if (loadSet.size === 0) {\n loadingPopup.style.visibility = 'hidden';\n }\n}\n\nfunction reset() {\n loadSet = new Set();\n loadingPopup.style.visibility = 'hidden';\n}\n\n// EXPORTS\nmodule.exports = {show, hide, reset};\n\n\n//# sourceURL=webpack:///./src/common/LoadingPopup.js?"); + +/***/ }), + +/***/ "./src/common/PlotterBase.js": +/*!***********************************!*\ + !*** ./src/common/PlotterBase.js ***! + \***********************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n This is the base class that models a regular plotter.\n It is inherited by several classes implementing specifc plotters\n */\n\n\n\n\nlet svg = __webpack_require__(/*! ./SVG.js */ \"./src/common/SVG.js\");\n\nconst xmlns=\"http://www.w3.org/2000/svg\";\nconst xlink=\"http://www.w3.org/1999/xlink\";\n\n\nclass PlotterBase{\n\n constructor(margins = {left: 20, right: 0, top: 10, bottom: 20}) {\n this.svg = document.createElementNS(xmlns, \"svg\");\n this.plotArea = document.createElementNS(xmlns, \"g\");\n this.svg.appendChild(this.plotArea);\n\n this.margins= margins;\n this.parentElement= null;\n this.yLabelText = null;\n this.noDataGroup = null;\n }\n\n\n attach(element, width, height){\n this.parentElement= element;\n this.parentElement.appendChild(this.svg);\n this.width = (width !== undefined ? width : this.parentElement.clientWidth);\n this.height = (height !== undefined ? height : this.svg.width);\n this.svg.setAttribute(\"width\", this.width);\n this.svg.setAttribute(\"height\", this.height);\n this.plotRangeX = this.width - this.margins.left - this.margins.right;\n this.plotRangeY = this.height - this.margins.top - this.margins.bottom;\n }\n\n\n isAttached(){\n return this.parentElement !== null;\n }\n\n\n setRangeAndLabels(xLabel, xMin, xMax, yLabel, yMin, yMax){\n this.xLabel= xLabel;\n this.xMin = xMin;\n this.xMax = xMax;\n this.yLabel= yLabel;\n this.yMin= yMin;\n this.yMax= yMax;\n\n this.xRel= this.plotRangeX/(this.xMax-this.xMin);\n this.yRel= this.plotRangeY/(this.yMax-this.yMin);\n }\n\n\n drawAxis(xSteps = 0, ySteps = 0, decimals = 2){\n\n\t this.plotArea.setAttribute(\"transform\", 'matrix(1 0 0 1 '+\n this.margins.left+' '+(this.height - this.margins.bottom)+')');\n\n this.yLabelText = svg.addText(this.svg, 0, 0, this.yLabel, 'middle', 'axis-steps-big');\n this.yLabelText.setAttribute('transform','translate(13,'\n +(this.plotRangeY/2+this.margins.top)+') rotate(-90)');\n\t svg.addText(this.plotArea, this.plotRangeX/2, this.margins.bottom-1,\n this.xLabel, 'middle', 'axis-steps-big');\n\n if (xSteps !== null){\n let xStep= this.plotRangeX/ xSteps;\n for (let i = 0; i <= xSteps; i++) {\n svg.addLine(this.plotArea, xStep*i, 0, xStep*i, 4, 1);\n svg.addText(this.plotArea, xStep*i, 14,\n +((xStep*i/this.xRel)+this.xMin).toFixed(decimals), 'middle', 'statisticsviewersteps');\n }\n }\n\n if (ySteps === null && this.yMax > 0 && this.yMin < 0){\n let i = 1;\n while(this.yMax*i > this.yMin) {\n svg.addLine(this.plotArea, 0, this.transformY(this.yMax*i), -3, this.transformY(this.yMax*i), 1);\n let temp = this.yMax*i;\n let numberText = Math.abs(temp) >= 10000 ? temp.toExponential(decimals) : temp.toFixed(decimals);\n svg.addText(this.plotArea, -5, this.transformY(this.yMax*i)+3, numberText,\n 'end', 'statisticsviewersteps');\n i--;\n }\n }\n\n if (ySteps !== null){\n let yStep= this.plotRangeY/ ySteps;\n for (let i = 0; i <= ySteps; i++) {\n svg.addLine(this.plotArea, 0, -yStep*i, -3, -yStep*i, 1);\n let numberToPaint= (yStep*i/this.yRel) + this.yMin;\n numberToPaint = Math.abs(numberToPaint) >= 10000 ? numberToPaint.toExponential(decimals) : numberToPaint.toFixed(decimals);\n if (Math.abs(numberToPaint) < 0.01) numberToPaint = 0;\n svg.addText(this.plotArea,-5, -(yStep*i-3), numberToPaint, 'end', 'statisticsviewersteps');\n }\n }\n\n\t svg.addLine(this.plotArea, 0, 0, this.plotRangeX+1 ,0 ,'main-axis');\n\t svg.addLine(this.plotArea, 0,0,0,-(this.plotRangeY+1) ,'main-axis');\n svg.addLine(this.plotArea, this.plotRangeX, 0, this.plotRangeX, -this.plotRangeY ,'main-axis');\n \tsvg.addLine(this.plotArea, 0,-this.plotRangeY, this.plotRangeX, -this.plotRangeY,'main-axis');\n }\n\n\n clear(){\n this.svg.removeChild(this.plotArea);\n this.plotArea = document.createElementNS(xmlns, \"g\");\n this.svg.appendChild(this.plotArea);\n this.plotArea.setAttribute(\"transform\", 'matrix(1 0 0 1 '+\n this.margins.left+' '+(this.height - this.margins.bottom)+')');\n\n if (this.yLabelText !== null){\n this.svg.removeChild(this.yLabelText);\n this.yLabelText = null;\n }\n if (this.noDataGroup !== null){\n this.svg.removeChild(this.noDataGroup);\n this.noDataGroup = null;\n }\n }\n\n\n setNoData(){\n this.clear();\n if (this.noDataGroup === null){\n this.noDataGroup = document.createElementNS(xmlns, 'g');\n this.svg.appendChild(this.noDataGroup);\n svg.addRect(this.noDataGroup, 0, 0, this.width, this.height);\n this.noDataGroup.setAttribute('fill', '#EEE');\n svg.addText(this.noDataGroup, this.width/2, this.height/2+10,\n 'NO DATA', 'middle', 'nodata');\n }\n\n }\n\n\n // Transform from y-axis units to y-axis pixels (svg-coordinates)\n transformY(y){\n return -this.yRel*(y - this.yMin);\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = PlotterBase;\n\n\n//# sourceURL=webpack:///./src/common/PlotterBase.js?"); + +/***/ }), + +/***/ "./src/common/PubSub.js": +/*!******************************!*\ + !*** ./src/common/PubSub.js ***! + \******************************/ +/***/ ((module) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the design pattern pub/sub, which allows one module\n\t to broadcast messages to other modules.\n */\n\n\n\nvar messages = new Map();\nvar lastUid = -1;\n\n /**\n *\tsubscribe( message, func ) -> String\n *\t- message (String): The message to subscribe to\n *\t- func (Function): The function to call when a new message is published\n *\tSubscribes the passed function to the passed message. Every returned token\n *\tis unique and should be stored if you need to unsubscribe\n **/\nfunction subscribe( message, func ){\n //console.log('SUBSCRIBING Message '+message);\n\tif ( typeof func !== 'function'){\n\t\treturn false;\n\t}\n\t// message is not registered yet\n\tif ( !messages.has( message ) ){\n\t\tmessages.set( message, new Map());\n\t}\n\t// forcing token as String, to allow for future expansions without breaking usage\n\t// and allow for easy use as key names for the 'messages' object\n\tvar token = 'uid_' + String(++lastUid);\n\tmessages.get(message).set(token, func); //messages[message][token] = func;\n //print();\n\t// return token for unsubscribing\n\treturn token;\n}\n\n\nfunction publish( message, data){\n\n var hasSubscribers = messages.has( message )\n && (messages.get( message ).size > 0);\n\n\tif ( !hasSubscribers ){\n\t\treturn false;\n }\n\n var deliver = function (){\n //deliverMessage(message, data);\n var subscribers = messages.get(message);\n //console.log('DELIVERING Message '+message);\n subscribers.forEach(function(func, token) {\n func(data);\n //console.log('EXE funct '+ func);\n });\n };\n\n setTimeout( deliver, 0 ); // async\n\treturn true;\n}\n\n\nfunction print(){\n console.log('PubSub data: ');\n messages.forEach(function(functions, msg) {\n console.log(msg + ': ');\n\n functions.forEach(function(func, token) {\n console.log(' '+token + ': ' + func);\n });\n });\n}\n\n// EXPORTS\nmodule.exports = { subscribe: subscribe, publish: publish };\n\n\n//# sourceURL=webpack:///./src/common/PubSub.js?"); + +/***/ }), + +/***/ "./src/common/Router.js": +/*!******************************!*\ + !*** ./src/common/Router.js ***! + \******************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the app Routing system: the feature that allows\n the page navigation in a single-page environment.\n */\n\n\n\n\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet routes = new Map();\n\n\nfunction add(route, func){\n routes.set(route, func);\n}\n\n\nwindow.addEventListener(\"hashchange\", route);\n\nvar hashHistory = [window.location.hash];\nvar historyLength = window.history.length;\nfunction route() {\n\n var hash = window.location.hash, length = window.history.length;\n if (hashHistory.length && historyLength == length) {\n if (hashHistory[hashHistory.length - 2] == hash) {\n hashHistory = hashHistory.slice(0, -1);\n LoadingPopup.reset();\n window.allowNewLoadPopup = false;\n } else {\n window.allowNewLoadPopup = true;\n hashHistory.push(hash);\n }\n } else {\n window.allowNewLoadPopup = true;\n hashHistory.push(hash);\n historyLength = length;\n }\n\n let hashPath = document.location.hash.substring(2);\n let command, param, subparam;\n\n // Remove the ending /\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n\n // Remove state parameters from authentication\n let stateIndex = hashPath.indexOf('&state');\n if (stateIndex != -1) {\n hashPath = hashPath.substring(0, stateIndex);\n }\n\n if (hashPath.indexOf('/') > 0){\n let a= hashPath.split('/');\n command= a[0];\n param= a[1];\n subparam= a[2];\n }\n else command = hashPath;\n\n if (routes.has(command)) {\n routes.get(command)(param, subparam);\n }\n};\n\n\nfunction print(){\n console.log('Router data: ');\n routes.forEach(function(func, url) {\n console.log(url + ': ' + func);\n });\n}\n\n\n// EXPORTS\nmodule.exports = { add: add, route: route };\n\n\n//# sourceURL=webpack:///./src/common/Router.js?"); + +/***/ }), + +/***/ "./src/common/SVG.js": +/*!***************************!*\ + !*** ./src/common/SVG.js ***! + \***************************/ +/***/ ((module) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n SVG drawing util library\n */\n\n\n\n\nconst xmlns=\"http://www.w3.org/2000/svg\";\nconst xlink=\"http://www.w3.org/1999/xlink\";\n\n\nfunction addPoint(parent, x, y, r, className) {\n let e = document.createElementNS(xmlns, \"circle\");\n e.setAttribute(\"r\", r); // e.setAttributeNS(null, \"r\", 5);\n e.setAttribute(\"cx\", x);\n e.setAttribute(\"cy\", y);\n if (className !== undefined) e.setAttribute(\"class\", className);\n parent.appendChild(e);\n return e;\n}\n\nfunction addCircle(parent, x, y, r, fillColor, strokeColor, strokeWidth) {\n let e = document.createElementNS(xmlns, \"circle\");\n e.setAttribute(\"r\", r); // e.setAttributeNS(null, \"r\", 5);\n e.setAttribute(\"cx\", x);\n e.setAttribute(\"cy\", y);\n e.setAttribute(\"fill\", fillColor);\n e.setAttribute(\"stroke\", strokeColor);\n e.setAttribute(\"stroke-width\", strokeWidth);\n parent.appendChild(e);\n return e;\n}\n\n\nfunction addLine(parent, x1, y1, x2, y2, className) {\n let e = document.createElementNS(xmlns, \"line\");\n e.setAttribute(\"x1\", x1);\n e.setAttribute(\"y1\", y1);\n e.setAttribute(\"x2\", x2);\n e.setAttribute(\"y2\", y2);\n if (className !== undefined) e.setAttribute(\"class\", className);\n //e.setAttribute(\"stroke-width\", stroke);\n parent.appendChild(e);\n return e;\n}\n\n\nfunction addRect(parent, x, y, w, h, className) {\n let e = document.createElementNS(xmlns, \"rect\");\n e.setAttribute(\"x\", x);\n e.setAttribute(\"y\", y);\n e.setAttribute(\"width\", w);\n e.setAttribute(\"height\", h);\n if (className !== undefined) e.setAttribute(\"class\", className);\n parent.appendChild(e);\n return e;\n}\n\n\n\nfunction addText(parent, x, y, text, textAnchor = 'start', className) {\n let e = document.createElementNS(xmlns, \"text\");\n e.setAttribute(\"x\", x);\n e.setAttribute(\"y\", y);\n e.textContent= text;\n //e.setAttribute(\"stroke\", 'black');\n e.setAttribute(\"text-anchor\", textAnchor);\n if (className !== undefined) e.setAttribute(\"class\", className);\n parent.appendChild(e);\n return e;\n}\n\n\nfunction addPolyline(parent, points, className) {\n let e = document.createElementNS(xmlns, \"polyline\");\n e.setAttribute(\"points\", points);\n if (className !== undefined) e.setAttribute(\"class\", className);\n //e.setAttribute(\"stroke-width\", stroke);\n parent.appendChild(e);\n}\n\n\nfunction removeElement(element){\n element.parentElement.removeChild(element);\n}\n\n\n\n// EXPORTS\nmodule.exports = {\n addPoint: addPoint,\n addCircle,\n addLine: addLine,\n addRect: addRect,\n addText: addText,\n addPolyline: addPolyline,\n removeElement: removeElement\n};\n\n\n//# sourceURL=webpack:///./src/common/SVG.js?"); + +/***/ }), + +/***/ "./src/common/SwitchComponent.js": +/*!***************************************!*\ + !*** ./src/common/SwitchComponent.js ***! + \***************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component implements a generic switch/toggle button\n */\n\n\nlet util = __webpack_require__(/*! ./util.js */ \"./src/common/util.js\");\n\nclass SwitchComponent {\n\n constructor(imageBasePath) {\n\n this.off = true;\n\n this.element = document.createElement('span');\n this.element.innerHTML+=`\n <img src=\"${imageBasePath}_off.png\" width=\"24px\"\n style=\"margin-bottom: -1px; cursor: pointer\"/>\n `;\n this.image = this.element.querySelector('img');\n\n this.element.addEventListener('click', e => {\n this.off = !this.off;\n let imagePath = (this.off ? imageBasePath+'_off' : imageBasePath);\n this.image.setAttribute('src',imagePath+'.png');\n this.listener(this.off);\n });\n }\n\n\n setListener(listener){\n this.listener = listener;\n }\n}\n\n// EXPORTS\nmodule.exports = SwitchComponent;\n\n\n//# sourceURL=webpack:///./src/common/SwitchComponent.js?"); + +/***/ }), + +/***/ "./src/common/UserGuidance.js": +/*!************************************!*\ + !*** ./src/common/UserGuidance.js ***! + \************************************/ +/***/ ((module) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the user guidance system. This complex system for\n showing tips to the user is specified in the project documentation \n */\n\n\n\n\n//let util = require('util.js');\n\nlet body = document.querySelector('body');\nlet tips = [];\nlet tipData = [];\nlet currentTip;// = 1;\nlet independentTips = []; // if null => showing else if 'off' not showing\nlet addBox, table, searchBox, propertiesTabs;\nlet searchBarShowing = false;\n\n\nfunction createUserMsg(i, width){\n let element= document.createElement('div');\n element.setAttribute('class','user-guidance');\n element.innerHTML = '<img src=\"img/tip'+i+'.png\" width=\"'+width+'px\" />';\n element.style.position = 'absolute';\n element.style.display = 'none';\n body.appendChild(element);\n return element;\n}\n\n\nfunction showTip(tipNumber){\n if (tipNumber === 8 && !searchBarShowing) return; // Do nothing\n if (tipNumber === 4 && !searchBarShowing) return; // Do nothing\n\n let r = tipData[tipNumber].domTarget.getBoundingClientRect();\n//console.log(\"GGGGGGGGG: \",r);\n tips[tipNumber].style.top = (r.top + tipData[tipNumber].top + window.pageYOffset)+'px';\n tips[tipNumber].style.left = (r.left + tipData[tipNumber].left)+'px';\n tips[tipNumber].style.display = 'block';\n}\n\n// Init per user session\nfunction init(addBoxP, tableP, searchBoxP, propertiesTabsP){\n addBox = addBoxP;\n table = tableP;\n searchBox = searchBoxP;\n propertiesTabs = propertiesTabsP;\n\n independentTips[3] = localStorage.getItem('tip3');\n independentTips[4] = localStorage.getItem('tip4');\n independentTips[7] = localStorage.getItem('tip7');\n\n createUserTips();\n let currentTip = localStorage.getItem('currentTip');\n if (currentTip === null){ // First time\n updateCurrentTip(1);\n }else{\n currentTip = parseInt(currentTip);\n if (currentTip < 10){ // regular case\n updateCurrentTip(currentTip);\n }//else { currentTip === 10 the guidance has finished\n }\n}\n\n\nfunction createUserTips(){\n\n if (tips.length === 0){\n tips[1] = createUserMsg(1, 220);\n tipData[1] = {domTarget: addBox, top: -70, left: -240 };\n tips[2] = createUserMsg(2, 280);\n tipData[2] = {domTarget: addBox, top: -110, left: 80 };\n tips[3] = createUserMsg(3, 180);\n tipData[3] = {domTarget: table, top: 180, left: 720 };\n tips[4] = createUserMsg(4, 240);\n tipData[4] = {domTarget: searchBox, top: 45, left: -250 };\n tips[5] = createUserMsg(5, 210);\n tipData[5] = {domTarget: addBox, top: -130, left: 70};\n tips[6] = createUserMsg(6, 240);\n tipData[6] = {domTarget: addBox, top: -100, left: 370};\n tips[7] = createUserMsg(7, 220);\n tipData[7] = {domTarget: propertiesTabs, top: 160, left: -40};\n tips[8] = createUserMsg(8, 240);\n tipData[8] = {domTarget: searchBox, top: 30, left: 760};\n\n // Event\n tips[1].addEventListener( \"click\", closeAndShowNext);\n tips[2].addEventListener( \"click\", closeAndShowNext);\n tips[3].addEventListener( \"click\", e => { closeIndependentTip(3) });\n tips[4].addEventListener( \"click\", e => { closeIndependentTip(4) });\n tips[5].addEventListener( \"click\", closeAndShowNext);\n tips[6].addEventListener( \"click\", closeAndShowNext);\n tips[7].addEventListener( \"click\", e => { closeIndependentTip(7) });\n tips[8].addEventListener( \"click\", e => {\n e.target.style.display = 'none';\n updateCurrentTip(10);\n });\n }\n}\n\n\nfunction setFinal(){\n searchBarShowing = true;\n if (currentTip < 10){\n tips[currentTip].style.display = 'none';\n updateCurrentTip(8);//currentTip = 4;\n showTip(currentTip);\n }\n showIndependentTip(4, true);\n}\n\n\nfunction closeIndependentTip( tipNumber){\n //e.preventDefault();\n tips[tipNumber].style.display = 'none';\n localStorage.setItem('tip'+tipNumber, 'off');\n independentTips[tipNumber] = 'off';\n}\n\n\nfunction closeAndShowNext(e){\n e.preventDefault();\n //console.log(\"closeAndShowNext\",currentTip);\n e.target.style.display = 'none';\n\n switch (currentTip) {\n case 2: currentTip = 5; break;\n\n case 6: currentTip = 8; break;\n\n default: // 1 , 5\n currentTip++;\n }\n updateCurrentTip(currentTip);\n showTip(currentTip);\n}\n\n\nfunction showIndependentTip(tipNumber, value){\n //console.log(\"showIndependentTip\",tipNumber);\n if (independentTips[tipNumber] === null){ // Tip has not been removed (clicked)\n if (value) showTip(tipNumber);\n else tips[tipNumber].style.display = 'none';\n }\n}\n\n\n\n\nfunction show(value, tip3, tip7){ // Global show - the UserGuidance is shown or hidden at all\n if (currentTip < 10){ // sequential tips\n if (value) showTip(currentTip);\n else tips[currentTip].style.display = 'none';\n }\n // Independent tips\n showIndependentTip(3, value && tip3);\n showIndependentTip(4, value);\n showIndependentTip(7, value && tip7);\n}\n\n\nfunction updateCurrentTip(value){\n currentTip = value;\n localStorage.setItem('currentTip', value);\n //console.log('localStorage.currentTip:',localStorage.getItem('currentTip'));\n}\n\n\n\n\n// EXPORTS\nmodule.exports = {init, setFinal, show, showIndependentTip}\n\n\n//# sourceURL=webpack:///./src/common/UserGuidance.js?"); + +/***/ }), + +/***/ "./src/common/util.js": +/*!****************************!*\ + !*** ./src/common/util.js ***! + \****************************/ +/***/ ((module) => { + +"use strict"; +eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is an app-level utility JavaScript file. It holds:\n\n - Environments conf (API URL and user cookie domain configuration)\n * Maybe this conf info can be removed from here to a better place\n - global state variables and app-level constants\n - miscellaneous app-level function\n - local vars\n - app-level util functions\n\n * Maybe this file should be rethought\n */\n\n\n\n\n// global state vars\nlet materialId = null;\nlet searchResults = false;\n\n\n// app-level constants\n\nconst IMAGE_DIR = 'img/';\n\nconst MAT_VIEW = {\n 'structure' : 'structure',\n 'electronicstruct': 'electronicstruct',\n 'methodology': 'methodology',\n 'thermalprops': 'thermalprops',\n 'elasticconst': 'elasticconst'\n};\n\nlet ELEMENTS = [\n 'H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', // Si = 14\n 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', // Nin = 28\n 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', // Nb = 41\n 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', // Xe = 54\n 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', // Ho= 67\n 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', // Hg = 80\n 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', // Np = 93\n 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Ha', 'Sg', // sg = 106\n 'Ns', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og' // Mt = 109\n];\n\n// API URL and user cookie domain configuration\nconst API_BASE_URL = window.nomadEnv.apiRoot;\n\n// Mockup URLs\n//const FERMI_SURFACE_URL= HOST+'files/fermi/'+\n//'fed3fa9fbc68aa6c5e51845396889666ca37bb2e626e1da53.x3d';\n\n// Local variables\n\nlet userData = null;\n\n\n// app-level util functions\n\n// Groups calculations by their functional.\nfunction getCalcMapByFunctional(summaryCalcSet) {\n let functCalcMap = new Map();\n summaryCalcSet.forEach(calc => {\n if (functCalcMap.has(calc.functional_type)) {\n functCalcMap.get(calc.functional_type).add(calc.calc_id);\n\n } else { // New functional\n let newFunctionalArray = new Set();\n newFunctionalArray.add(calc.calc_id);\n functCalcMap.set(calc.functional_type, newFunctionalArray);\n }\n });\n return functCalcMap;\n}\n\nfunction getUserData(){\n return userData;\n}\n\n\nfunction getServerLocation(){\n return document.location.hostname;\n}\n\n\nfunction authServerReq(url, callback){\n var oReq = new XMLHttpRequest();\n oReq.addEventListener(\"load\", callback);\n console.log('util.authServerReq: ', API_BASE_URL+url);\n oReq.open(\"GET\", API_BASE_URL+url);\n //console.log('util.authServerReq oReq: ', oReq);\n oReq.send();\n return oReq;\n}\n\n/**\n * Used to create shortened codes from the full 28 character hash string.\n */\nfunction getShortCode(id) {\n if (id.startsWith(\"eos/\") || id.startsWith(\"par/\")) {\n if (id.length == 32) {\n return id.substring(0, 12);\n }\n }\n if (id.length == 28) {\n return id.substring(0, 8);\n }\n throw \"The given identifier could not be shortened as it does not have the right initial length.\";\n}\n\nfunction serverReq(url, callback){\n var oReq = new XMLHttpRequest();\n oReq.addEventListener(\"load\", callback);\n oReq.open(\"GET\", url);\n\n // Check for authentication\n var keycloak = window.keycloak;\n if (keycloak.authenticated) {\n oReq.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);\n }\n oReq.send();\n return oReq;\n}\n\n\nfunction serverReqPOST(url, data, callback){\n var oReq = new XMLHttpRequest();\n oReq.addEventListener('load', callback);\n oReq.open('POST', url);\n oReq.setRequestHeader('Content-Type', 'application/json');\n\n // Check for authentication\n var keycloak = window.keycloak;\n if (keycloak.authenticated) {\n oReq.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);\n }\n\n oReq.send(data);\n return oReq;\n}\n\n\nfunction getSubscriptedFormula(formula){\n let finalFormula= ''; // 'elementCode' : number\n for (let i = 0; i < formula.length; i++){\n if (formula.charCodeAt(i) >= 47 && formula.charCodeAt(i) < 58 )\n finalFormula += '<sub>'+formula[i]+'</sub>';\n else finalFormula += formula[i];\n //console.log(formula.charCodeAt(i) + \" \"+finalFormula);\n }\n return finalFormula;\n}\n\n\nfunction getSearchURL(){\n return API_BASE_URL+'materials/';\n}\n\nfunction getSuggestionURL(quantity){\n return API_BASE_URL+'suggestions?property='+quantity;\n}\n\nfunction getMaterialURL(matId) {\n return API_BASE_URL+'materials/'+matId;//'/materials/matid'; //\n}\n\nfunction getMaterialGroupURL(matId, groupType, groupId){\n return API_BASE_URL + \"materials/\" + matId + \"/groups/\" + groupType + \"/\" + groupId;\n}\n\nfunction getMaterialCalcURL(matId, calcId, property = ''){\n let propertyString = (property === '' ? '' : '?property='+property);\n return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+propertyString;\n}\n\nfunction getMaterialStatsURL(matId){\n return API_BASE_URL+'materials/'+matId+'/statistics';\n}\n\nfunction getMaterialXsURL(what, matId) {\n return API_BASE_URL+'materials/'+matId+'/'+what;\n}\n\nfunction getCalcEnergiesURL(matId,calcId){\n return API_BASE_URL+'materials/'+matId+'/calculations/'+calcId+'/energies';//'/materials/calculations';//\n}\n\nfunction getReportURL(matId) {\n return API_BASE_URL+'materials/'+matId+'/reports';\n}\n\n\n// Launch an app event\nfunction setBrowserHashPath(modulePath, finalPath){\n if (typeof finalPath === 'undefined') document.location= '#/'+modulePath;\n else document.location= '#/'+modulePath+'/'+finalPath;\n}\n\nfunction loadLib(url){\n let script = document.createElement('script');\n script.setAttribute('type', 'text/javascript');\n script.setAttribute('src', url);\n document.getElementsByTagName('head')[0].appendChild(script);\n}\n\nfunction getNumberArray(string){\n let sArray= string.substring(1,string.length-1).split(',');\n let fArray= [];\n for (var i = 0; i < sArray.length; i++) {\n fArray.push(parseFloat(sArray[i]));\n }\n //console.log('getNumberArray.SPLIT: '+fArray);\n return fArray;\n}\n\n/**\n * Used to convert a 2D array to another scale with the given factor.\n */\nfunction convert2d(data, factor) {\n let converted = [];\n for (var i = 0; i < data.length; i++) {\n let row = data[i];\n let row_converted = [];\n for (var j = 0; j < row.length; j++) {\n row_converted.push(row[j] * factor);\n }\n converted.push(row_converted);\n }\n\n return converted;\n}\n\nfunction J2eV(energy, decimals){\n let result= energy/1.602176565e-19;\n if (decimals === undefined){\n if (result < 0.01) return result.toFixed(6);\n else return result.toFixed(3);\n }else{\n return result.toFixed(decimals);\n }\n}\n\nfunction eV2J(energy){\n return energy*1.602176565e-19;\n}\n\n\n/*\nfunction getBandGapStatsValue(calcs){\n let bandGapSum= 0;\n let bandArray= [];\n let bandGapDirect= calcs[0].band_gap_direct;\n let bandGapType= (bandGapDirect ? \"direct\" : \"indirect\");\n\n for (var i = 0; i < calcs.length; i++) {\n //if (calcs[i].band_gap > 0){\n bandGapSum+= calcs[i].band_gap;\n bandArray.push(calcs[i].band_gap);\n if (calcs[i].band_gap_direct !== bandGapDirect)\n bandGapType= 'various results';\n //}\n //console.log(bandGapSum+' '+calcs[i].band_gap+' '+bandArray.length);\n }\n\n let html= '';//let html= ((bandGapSum / bandArray.length)/1.602176565e-19).toFixed(3)+' eV ('+bandGapType+')';;\n let min= (Math.min.apply(null, bandArray)/1.602176565e-19).toFixed(3);\n let max= (Math.max.apply(null, bandArray)/1.602176565e-19).toFixed(3);\n html+= ' ('+min+' ... '+max+' eV)';\n //html+= ' ['+bandArray.length+' / '+calcs.length+']';\n\n return html;\n}*/\n\nfunction m2Angstrom(dist){\n return (dist/1e-10).toFixed(3)+' Å';\n}\n\n\nfunction getLatticeAnglesValues(calcs, twoD, bulk){\n let lattParams= [0.0, 0.0, 0.0];\n calcs.forEach( (calc) => {\n if (calc.lattice_parameters !== undefined && calc.lattice_parameters !== null){\n let tempLattParams= getNumberArray(calc.lattice_parameters);\n lattParams[0] += tempLattParams[3];\n lattParams[1] += tempLattParams[4];\n lattParams[2] += tempLattParams[5];\n }\n });\n\n if (bulk)\n return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div>\n <div>β = ${rad2degree(lattParams[1] / calcs.size)}</div>\n <div>γ = ${rad2degree(lattParams[2] / calcs.size)}</div>`;\n else if (twoD)\n return `<div>α = ${rad2degree(lattParams[0] / calcs.size)}</div>`;\n else return ''; // 1D\n}\n\n\nfunction rad2degree(angle){\n return (angle * (180 / Math.PI)).toFixed(0)+'°';\n}\n\nfunction m3ToAngstrom3(vol){\n return (vol/1e-30).toFixed(3)+' Å<sup>3</sup>';\n}\n\n\nfunction getAverage(array){\n let sum = 0;\n for (var i = 0; i < array.length; i++) sum += array[i];\n return sum/array.length;\n}\n\n\n//function getQuantityStatsMap(calcs) {\n\n //// Determine which statistics to build based on system type\n //let quantities;\n //let materialType = \"\";\n //let labelMap = {\n //volume: 'Volume (ų)',\n //atomic_density: 'Atomic density (Å⁻³)',\n //mass_density: 'Mass density (kg/m³)',\n //lattice_a: 'a (Å)',\n //lattice_b: 'b (Å)',\n //lattice_c: 'c (Å)'\n //};\n //if (materialType == \"bulk\") {\n //quantities = ['volume', 'atomic_density', 'mass_density', 'lattice_a', 'lattice_b', 'lattice_c'];\n //} else {\n //quantities = ['lattice_a', 'lattice_b', 'lattice_c'];\n //}\n //let quantitiesMap = new Map();\n\n //// Request quantity statistics from the server. The statistics are calculated\n //// on the server to keep the GUI responsive in case of large number of\n //// calculations.\n //let matId = DataStore.getMaterialData().material_id;\n //let query = JSON.stringify({calculations: calcs});\n //serverReqPOST(getMaterialStatsURL(matId), query, e3 => {\n //let results = JSON.parse(e3.target.response);\n //});\n\n //return quantitiesMap;\n//}\n\n\nfunction toAngstromMinus3(density){\n return (density*1e-30).toFixed(3)+' Å<sup>-3</sup>';\n}\n\n\nfunction getMaterialTitle(data, html){\n let title;\n title = getSubscriptedFormula(data.formula_reduced);\n if (html !== undefined && html ===false) title = data.formula_reduced;\n\n if (data.space_group_number !== null && data.space_group_number !== undefined)\n title += ' - space group '+data.space_group_number;\n //return '<span style=\"font-size: 0.9em\">'+title+' </span>';\n return title;\n}\n\nfunction getMinMaxHTML(calcs,prop){\n let propArray= [];\n\n calcs.forEach( (calc) => {\n propArray.push(calc[prop]);\n });\n\n return '('+Math.min.apply(null, propArray)+' ... '+Math.max.apply(null, propArray)+')';\n}\n\nfunction numberWithCommas(x) {\n return x.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\nfunction generateDiagramSteps(maxVal, d=4){\n\n let exp = -Math.floor(Math.log(maxVal/d) * Math.LOG10E);\n\n let factor = Math.pow(10,exp);//100;\n //console.log('util.generateDiagramSteps ',exp, maxVal/d, factor);\n let ceil = Math.ceil(maxVal*factor/d);\n let stepArray = [];\n for (var i = 0; i <= d; i++) {\n stepArray[i] = ceil*i/factor;\n }\n //console.log('stepArray '+stepArray);\n exp = (exp < 0 ? 0 : exp);\n return [stepArray, exp];\n}\n\nfunction getDefault(value, fallback=\"unavailable\") {\n if (value === undefined || value === null) {\n return fallback;\n }\n return value;\n}\n\n/*\nfunction addBandGapData(calcJson, bsData){\n if (calcJson.band_gap > 0) {\n bsData.bandGapData = {};\n bsData.bandGapData.cbmEnergy = calcJson.band_gap_lower_energy;\n bsData.bandGapData.cbmKpt = getNumberArray(calcJson.band_gap_lower_kpt);\n bsData.bandGapData.vbmEnergy = calcJson.band_gap_upper_energy;\n bsData.bandGapData.vbmKpt = getNumberArray(calcJson.band_gap_upper_kpt);\n }\n}*/\n\nmodule.exports = {\n searchResults,\n materialId,\n MAT_VIEW: MAT_VIEW,\n IMAGE_DIR: IMAGE_DIR,\n ELEMENTS: ELEMENTS,\n getUserData,\n getServerLocation,\n authServerReq,\n serverReq,\n convert2d,\n serverReqPOST,\n getShortCode,\n getSearchURL: getSearchURL,\n getSuggestionURL,\n getMaterialURL: getMaterialURL,\n getMaterialCalcURL: getMaterialCalcURL,\n getMaterialGroupURL: getMaterialGroupURL,\n getMaterialStatsURL: getMaterialStatsURL,\n getMaterialXsURL: getMaterialXsURL,\n getCalcEnergiesURL: getCalcEnergiesURL,\n getReportURL,\n setBrowserHashPath: setBrowserHashPath,\n loadLib: loadLib,\n getNumberArray: getNumberArray,\n //FERMI_SURFACE_URL: FERMI_SURFACE_URL,\n J2eV: J2eV,\n eV2J,\n //getBandGapStatsValue: getBandGapStatsValue,\n m2Angstrom: m2Angstrom,\n getLatticeAnglesValues: getLatticeAnglesValues,\n rad2degree: rad2degree,\n m3ToAngstrom3: m3ToAngstrom3,\n toAngstromMinus3,\n getMaterialTitle,\n getMinMaxHTML: getMinMaxHTML,\n getSubscriptedFormula: getSubscriptedFormula,\n getAverage,\n numberWithCommas,\n generateDiagramSteps,\n getCalcMapByFunctional,\n getDefault,\n //is2DSystem_temporary_patch\n //addBandGapData\n};\n\n\n//# sourceURL=webpack:///./src/common/util.js?"); + +/***/ }), + +/***/ "./src/main.js": +/*!*********************!*\ + !*** ./src/main.js ***! + \*********************/ +/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file is the application entry point.\n It defines some app level components (Breadcrumb) and\n initializes several more (app level events, app routing, authentication)\n */\n\n\n\n\nlet util = __webpack_require__(/*! ./common/util.js */ \"./src/common/util.js\");\nlet LoadingPopup = __webpack_require__(/*! ./common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\nlet FlaggingFormPopup = __webpack_require__(/*! ./common/FlaggingFormPopup.js */ \"./src/common/FlaggingFormPopup.js\");\nlet PubSub = __webpack_require__(/*! ./common/PubSub.js */ \"./src/common/PubSub.js\");\nlet Router = __webpack_require__(/*! ./common/Router.js */ \"./src/common/Router.js\");\nlet MaterialMod = __webpack_require__(/*! ./material-mod/MaterialMod.js */ \"./src/material-mod/MaterialMod.js\");\nlet SearchModule = __webpack_require__(/*! ./search-mod/SearchMod.js */ \"./src/search-mod/SearchMod.js\");\nlet UserGuidance = __webpack_require__(/*! ./common/UserGuidance.js */ \"./src/common/UserGuidance.js\");\nlet DataStore = __webpack_require__(/*! ./material-mod/DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n\n// main DOM elements\nlet contentElement = document.getElementById('content');\nlet titleElement = document.querySelector('title');\nwindow.allowNewLoadPopup = true;\n\nvar getUrl = window.location;\nvar guiRoot = getUrl.protocol + \"//\" + getUrl.host + \"/\" + getUrl.pathname;\n\n// As of 0.8.3 nomad-FAIR is using KeyCloak 7.0.0, which does\n// not support the \"silentCheckSsoRedirectUri\" option. This option enables a\n// silent login check that does not enforce reloads. In order to do such silent\n// login, the Javascript adapter for KeyCloak 8.0.0 is used instead. This is\n// against the best practice of directly downloading the Javascript adapter\n// from the authentication server (window.nomadEnv.keycloakBase +\n// \"js/keycloak.min.js\"), but is in this case acceptable as it result is a much\n// smoother user experience.\nPubSub.subscribe('authenticated', data => {\n let hashPath = document.location.hash.substring(2);\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n if (hashPath.indexOf('/') > 0){\n let command = hashPath.split('/')[0];\n if (command === \"material\") {\n flaggingTab.style.visibility = 'visible';\n }\n }\n});\nvar keycloak = new Keycloak({\n url: window.nomadEnv.keycloakBase,\n realm: window.nomadEnv.keycloakRealm,\n clientId: window.nomadEnv.keycloakClientId\n});\nwindow.keycloak = keycloak;\nlet loginButton = document.querySelector('#login-button');\nlet logoutButton = document.querySelector('#logout-button');\nlet userName = document.querySelector('#user-name');\nkeycloak.init({\n onLoad: \"check-sso\",\n silentCheckSsoRedirectUri: `${guiRoot}silent-check-sso.html`,\n promiseType: \"native\",\n}).then((authenticated) => {\n if (authenticated) {\n keycloak.loadUserProfile()\n .then(function(profile) {\n userName.textContent = `${profile.firstName} ${profile.lastName}`;\n loginButton.style.display = 'none';\n logoutButton.style.display = 'inline';\n PubSub.publish('authenticated');\n }).catch(function() {\n console.log('Failed to load user profile.');\n });\n //util.setAuthRequestHeader(data.user, data.token.data);\n } else {\n loginButton.style.display = 'inline';\n logoutButton.style.display = 'none';\n userName.textContent = \"Guest\";\n }\n});\nloginButton.onclick = () => {\n keycloak.login({redirectUri: `${guiRoot}#/search`})\n .catch(() => {console.log(\"Authentication error.\");});\n};\nlogoutButton.onclick = () => {\n keycloak.logout();\n};\n\n\n/********* User flagging side tab ****************/\n\n/* This side vertical tab is hidden initially\n but it has to be set up when the app starts */\nlet flaggingTab = document.getElementById('calc-flagging-tab');\nflaggingTab.style.top = (window.innerHeight/2)+'px';\nflaggingTab.addEventListener('click', e => {\n FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus());\n});\n\n\n/*********** App Breadcrumb component definition ***************/\n\nclass Breadcrumb {\n\n constructor() {\n\n this.element = document.querySelector('#breadcrumb-placeholder');\n this.element.innerHTML = `\n <span class=\"goto-page Search\">Search</span>\n <span class=\"goto-page Results\"> > <span>Results</span></span>\n <span class=\"goto-page Overview\"> > <span>Overview</span></span>\n <span class=\"Details\">\n > \n <select class=\"details-dropdown\" >\n <option value=\"structure\">Structure</option>\n <option value=\"electronicstruct\">Electronic structure</option>\n <option value=\"methodology\">Methodology</option>\n <option value=\"thermalprops\">Thermal Properties</option>\n <!-- elasticconst-->\n </select>\n </span>\n `;\n this.resultsSel = this.element.querySelector('.Results');\n this.overviewSel = this.element.querySelector('.Overview');\n this.detailsSel = this.element.querySelector('.Details');\n this.detailsDropDown = this.element.querySelector('.details-dropdown');\n\n // Events\n this.element.querySelector('.Search').addEventListener( \"click\", e => {\n util.setBrowserHashPath('search');\n });\n this.resultsSel.addEventListener( \"click\", e => {\n util.setBrowserHashPath('search/results');\n });\n\n this.overviewSel.addEventListener('click', () => {\n util.setBrowserHashPath('material', util.materialId);\n });\n\n this.detailsDropDown.addEventListener('change', e => {\n util.setBrowserHashPath('material',\n DataStore.getMaterialData().material_id+'/'+e.target.value);\n });\n\n let self = this;\n function adjustDropdownOptions() {\n let esOption = self.detailsDropDown.querySelector('option[value=\"electronicstruct\"]');\n if (!DataStore.hasElecStructureData()) self.detailsDropDown.removeChild(esOption);\n\n let thOption = self.detailsDropDown.querySelector('option[value=\"thermalprops\"]');\n if (!DataStore.hasThermalData()) self.detailsDropDown.removeChild(thOption);\n // Remove because we want it's executed once\n self.detailsDropDown.removeEventListener('focus', adjustDropdownOptions);\n }\n\n this.detailsDropDown.addEventListener('focus', adjustDropdownOptions);\n }\n\n\n setState(appModule, param){\n let resultsSetLabel = this.resultsSel.querySelector('span');\n resultsSetLabel.style.fontWeight = 'normal';\n let overviewSelLabel = this.overviewSel.querySelector('span');\n overviewSelLabel.style.fontWeight = 'normal';\n\n if (appModule === 'search'){\n this.overviewSel.style.display = 'none';\n this.detailsSel.style.display = 'none';\n\n if (param === 'results'){\n this.resultsSel.style.display = 'inline';\n this.resultsSel.querySelector('span').style.fontWeight = 'bold';\n this.element.style.visibility = 'visible';\n }else\n this.element.style.visibility = 'hidden';\n\n }else if (appModule === 'material'){\n this.element.style.visibility = 'visible';\n this.resultsSel.style.display = (util.searchResults ? 'inline' : 'none');\n this.overviewSel.style.display = 'inline';\n\n if (param === undefined){ // Overview page\n this.detailsSel.style.display = 'none';\n overviewSelLabel.style.fontWeight = 'bold';\n }else{ // Details page\n this.detailsSel.style.display = 'inline';\n this.detailsDropDown.value = param;\n }\n }\n } // setState\n\n} // class Breadcrumb\n\n\n/***************************\n App setup\n***************************/\n\nlet breadcrumb = new Breadcrumb();\n\nlet searchMod;\nlet MaterialModule;\nlet materialModDOM;\nlet currentModule; // current module DOM being shown\n\n\nfunction showModuleDOM(module){\n if (currentModule) contentElement.removeChild(currentModule);\n currentModule = module;\n contentElement.appendChild(currentModule);\n}\n\n\n/****** App level events setup ********/\n\nPubSub.subscribe('show-material', data => {\n console.log('Handling event show-material: ' + data.material_id + ' view: '+data.view);\n\n //titleElement.innerHTML = 'NOMAD Encyclopedia - Material '+data.id;\n breadcrumb.setState('material', data.view);\n\n if (typeof materialModDOM === 'undefined'){\n MaterialModule = new MaterialMod();\n materialModDOM = MaterialModule.element;\n }\n showModuleDOM(materialModDOM);\n MaterialModule.setMaterialView(data);\n\n // In case the app comes from the search module through the url (back button)\n UserGuidance.show(false);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n if (keycloak.authenticated) {\n flaggingTab.style.visibility = 'visible';\n }\n});\n\nPubSub.subscribe('show-search', search => {\n console.log('Handling event show-search: '+search);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n flaggingTab.style.visibility = 'hidden';\n\n titleElement.innerHTML = 'NOMAD Encyclopedia - Search';\n breadcrumb.setState('search', search);\n\n if (search === undefined) {\n //LoadingPopup.reset();\n searchMod.showSearchPage();\n } else if (search === 'results') {\n searchMod.showResultsPage();\n }\n\n showModuleDOM(searchMod.element);\n});\n\n\n/****** App routing config ******/\nRouter.add('search', search => PubSub.publish('show-search', search));\nRouter.add('material', (matId, view) => PubSub.publish('show-material', {'material_id': matId, 'view': view}));\n\n/****** init ******/\nsearchMod = new SearchModule();\nif (document.location.hash === '') document.location += \"#/search\";\nRouter.route();\n\n/********* User authentication ***********/\n\nlet userNameElement = document.querySelector('#user-name');\n\nfunction getCookie(name) {\n let value = \"; \" + document.cookie;\n let parts = value.split(\"; \" + name + \"=\");\n if (parts.length === 2) return parts.pop().split(\";\").shift();\n}\n\nfunction parseCookie(userData) {\n return userData.substring(1, userData.length-1).replace(/\\\\054/g,',').replace(/\\\\/g,'');\n}\n\nlet userInfoCookie = getCookie('user_info');\n\nif (userInfoCookie !== undefined) {\n let userInfoData = JSON.parse(parseCookie(userInfoCookie));\n //console.log('userInfoData: ', userInfoData);\n setAppAuthenticated(userInfoData);\n}\n\n\n//# sourceURL=webpack:///./src/main.js?"); + +/***/ }), + +/***/ "./src/material-mod/BSDOSPlotter.js": +/*!******************************************!*\ + !*** ./src/material-mod/BSDOSPlotter.js ***! + \******************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Implements a graphical UI component composed of a DOS plotter and a\n Band Structure plotter\n */\n\n\nlet BSPlotter = __webpack_require__(/*! ./BSPlotter.js */ \"./src/material-mod/BSPlotter.js\");\nlet DOSPlotter = __webpack_require__(/*! ./DOSPlotter.js */ \"./src/material-mod/DOSPlotter.js\");\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\n\n\nclass BSDOSPlotter{\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute('style','margin: 0 auto');\n this.parentElement= null;\n this.bsPlotter= new BSPlotter();\n this.dosPlotter= new DOSPlotter({left: 4, right: 20, top: 0, bottom: 30});\n this.dosYAxisLabeled = false;\n }\n\n\n attach(element, width, height){\n element.appendChild(this.element);\n //this.bsPlotter.attach(this.element, element.clientWidth/2 + 200 -20/*padding*/, height);\n //this.dosPlotter.attach(this.element, element.clientWidth/2 - 200 -20/*padding*/, height);\n this.bsPlotter.attach(this.element, height, height);\n this.height = height;\n this.dosPlotter.attach(this.element, this.height/2+20, height);\n this.parentElement= element;\n }\n\n\n isAttached(){\n return this.parentElement !== null;\n }\n\n\n setUpAndData(dispData, dosData, codeName){\n\n this.hasDispData = (dispData !== undefined && dispData !== null);\n this.hasDosData = (dosData !== undefined && dosData !== null);\n\n // Create a new DOS graph with/without left axis and labels\n let newDosYAxisLabeled;\n if (this.hasDosData && !this.hasDispData) newDosYAxisLabeled = true;\n else newDosYAxisLabeled = false;\n\n if (this.dosYAxisLabeled !== newDosYAxisLabeled){\n this.element.removeChild(this.dosPlotter.svg);\n let newLeftMargin = (newDosYAxisLabeled ? 40 : 4);\n this.dosPlotter= new DOSPlotter({left: newLeftMargin, right: 20, top: 0, bottom: 30});\n let width = this.height/2 + newLeftMargin;\n this.dosPlotter.attach(this.element, width, this.height);\n }\n this.dosYAxisLabeled = newDosYAxisLabeled;\n\n if (this.hasDispData){\n this.bsPlotter.setBandStructureData(dispData);\n if (this.hasDosData)\n this.bsPlotter.setRepaintListener( (yZoom, yOffset) => {\n this.dosPlotter.setYZoomAndOffset(yZoom, yOffset);\n this.dosPlotter.repaint();\n });\n }else\n this.bsPlotter.setNoData();\n\n if (this.hasDosData){\n\n this.dosPlotter.setPoints(dosData, codeName);\n\n if (this.hasDispData){\n this.bsPlotter.setExternalYAxisMax(this.dosPlotter.getYAxisMax());\n\n this.dosPlotter.setRepaintListener( (yZoom, yOffset) => {\n this.bsPlotter.setYZoomAndOffset(yZoom, yOffset);\n this.bsPlotter.repaint();\n });\n // Remove y axis label\n this.dosPlotter.svg.removeChild(this.dosPlotter.yLabelText);\n this.dosPlotter.yLabelText = null;\n }\n\n }else\n this.dosPlotter.setNoData();\n\n this.dosPlotter.setYAxisLabelsVisibility(newDosYAxisLabeled);\n }\n\n\n setNoData(){\n this.bsPlotter.setNoData();\n this.dosPlotter.setNoData();\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = BSDOSPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/BSDOSPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/BSPlotter.js": +/*!***************************************!*\ + !*** ./src/material-mod/BSPlotter.js ***! + \***************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Implements the Band Structure plotter.\n It's used to show either the regular Band Structure data or\n the Phonon Dispersion.\n */\n\n\n\n\nlet InteractivePlotterBase = __webpack_require__(/*! ../common/InteractivePlotterBase.js */ \"./src/common/InteractivePlotterBase.js\");\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\nconst MULTIPLO1 = 1.602176565e-19;\nconst MULTIPLO2 = 1/5.034117012222e22;\n\n\nclass BSPlotter extends InteractivePlotterBase {\n\n constructor() {\n super({left: 55, right: 5, top: 0, bottom: 30});\n this.phononMode = false;\n this.factor = MULTIPLO1;\n }\n\n\n setPhononMode(){\n this.phononMode = true;\n this.factor = MULTIPLO2;\n this.outOfRangeColorActivated = false;\n }\n\n\n // detach if necessary and attach\n attach(element, width, height){\n super.attach(element, width, height);\n }\n\n getTopAndLowestPoints(bandStructData) {\n let bandMax = bandStructData[0].band_energies[0][0][0];\n let bandMin = bandStructData[0].band_energies[0][0][0];\n for (let i = 0; i < bandStructData.length; i++) { // Per segment\n // spin1 - per band energy loop\n for (let j = 0; j < bandStructData[i].band_energies[0].length; j++) {\n let tempValue= Math.max.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/);\n if (tempValue > bandMax) bandMax = tempValue;\n tempValue= Math.min.apply(null, bandStructData[i].band_energies[0]/*spin1*/[j]/*first_band*/);\n if (tempValue < bandMin) bandMin = tempValue;\n }\n\n if (bandStructData[i].band_energies.length === 2)\n // spin2 - per band energy loop\n for (let j = 0; j < bandStructData[i].band_energies[1].length; j++) { // Per segment\n let tempValue= Math.max.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/);\n if (tempValue > bandMax) bandMax = tempValue;\n tempValue= Math.min.apply(null, bandStructData[i].band_energies[1]/*spin1*/[j]/*first_band*/);\n if (tempValue < bandMin) bandMin = tempValue;\n }\n }\n return [(bandMin-this.energyOffset)/this.factor, (bandMax-this.energyOffset)/this.factor];\n }\n\n\n drawKPointLabel(x, label){\n svg.addText(this.axisGroup, x*this.xRel, 16, label, 'middle', 'steps');\n }\n\n setBandStructureData(data) {\n\n let bandStructData = data.section_k_band_segment;\n this.bandGapData = undefined;\n this.energyOffset = 0;\n if (data.section_band_gap !== undefined) {\n let minGap = Infinity;\n let minGapChannel = 0;\n for (let i=0; i < data.section_band_gap.length; ++i) {\n let iGap = data.section_band_gap[i].value;\n if (iGap < minGap) {\n minGap = iGap;\n minGapChannel = i;\n }\n }\n if (minGap != 0) {\n this.bandGapData = data.section_band_gap[minGapChannel];\n this.bandGapData.cbmDistances = [];\n this.bandGapData.vbmDistances = [];\n this.energyOffset = this.bandGapData.valence_band_max_energy;\n }\n }\n\n // Gather all the points per band (divided by spin) crossing the segments\n this.bandsDataSpin1= []; // [segment][band][kpoint]\n this.bandsDataSpin2= [];\n this.segmentLimitsX = [];\n this._reset();\n\n // Calculate energy range\n let topAndLowestPoints = this.getTopAndLowestPoints(bandStructData);\n let minEnergyVal = topAndLowestPoints[0];\n let maxEnergyVal = topAndLowestPoints[1];\n\n if (this.phononMode){\n this.setAxisRangeAndLabels('',0,1,'Frequency (cm⁻¹)',-50, 320,\n minEnergyVal, maxEnergyVal, 100);\n }else\n this.setAxisRangeAndLabels('',0,1,'Energy (eV)' ,-6 ,11 , minEnergyVal,\n maxEnergyVal, 5 );\n\n let currentDistance = 0;\n let prevLastLabel = null;\n let dataOverflow = false;\n let lastSegment = bandStructData[bandStructData.length -1];\n let lastPath = lastSegment.k_path_distances;\n let totalDistance = lastPath[lastPath.length-1];\n\n for (let k = 0; k < bandStructData.length; k++) { // For every segment\n let segment = bandStructData[k];\n let labels = segment.band_segm_labels;\n let energiesSpin1 = segment.band_energies[0];\n let energiesSpin2 = segment.band_energies[1];\n let kPathDistances = segment.k_path_distances;\n this.bandsDataSpin1.push([]); // Add a new array per segment\n this.bandsDataSpin2.push([]);\n let segmentDistance = kPathDistances[kPathDistances.length - 1] - kPathDistances[0];\n\n // keeping the segment limits (x coordinate) for after painting\n this.segmentLimitsX.push(currentDistance/totalDistance);\n\n if (labels !== null){\n // Set k-points labels\n if (prevLastLabel !== null && prevLastLabel !== labels[0])\n this.drawKPointLabel(currentDistance/totalDistance,\n getSymbol(prevLastLabel)+'|'+getSymbol(labels[0]));\n else\n this.drawKPointLabel(currentDistance/totalDistance, getSymbol(labels[0]));\n // The last label\n if (k === bandStructData.length -1)\n this.drawKPointLabel(1, getSymbol(labels[1]));\n\n prevLastLabel = labels[1];\n }\n\n // Mark the valence band maximu and conduction band minimum positions\n // if reported.\n //this.bandGapData.cbmDistances.push(tempDistance);\n //this.bandGapData.vbmDistances.push(tempDistance);\n\n // We assume that the sampling along segments is done uniformly. This\n // reduces the amount of data that is sent.\n //let nKPoints = energiesSpin1.length;\n //let delta = segmentDistance/ (nKPoints - 1);\n for (let i = 0; i < kPathDistances.length; i++) { // For every k-point\n let kPathDistance = kPathDistances[i] / totalDistance;\n\n // All bands spin1\n for (let j = 0; j < energiesSpin1[i].length; j++) {\n if (i === 0) {\n this.bandsDataSpin1[k][j] = [];\n }\n let e = energiesSpin1[i][j];\n let currentY = (e - this.energyOffset)/this.factor;\n this.bandsDataSpin1[k][j].push({x: kPathDistance, y: currentY});\n if (!dataOverflow && currentY > 10000) dataOverflow = true;\n }\n // All bands spin2\n if (energiesSpin2 !== undefined)\n for (let j = 0; j < energiesSpin2[i].length; j++) {\n if (i === 0) {\n this.bandsDataSpin2[k][j] = [];\n }\n let currentY = (energiesSpin2[i][j] - this.energyOffset)/this.factor;\n this.bandsDataSpin2[k][j].push({x: kPathDistance, y: currentY});\n if (!dataOverflow && currentY > 10000) dataOverflow = true;\n }\n }\n currentDistance += segmentDistance;\n }\n\n if (dataOverflow) throw 'Plotter Data Overflow: Probably the energy data is not in correct units'; //console.log('BSPlotter data overflow');\n else this.repaint();\n }\n\n\n repaintData(yMin, yMax){\n\n this.segmentLimitsX.forEach(x => {\n let yMinPx = this.transformY(yMin);\n let yMaxPx = this.transformY(yMax);\n if (this.phononMode) { yMinPx += 200; yMaxPx -= 200; }\n svg.addLine(this.plotContent, x*this.xRel, yMinPx,\n x*this.xRel, yMaxPx, 'segment');\n });\n\n // Drawing lines\n let polylinePoints;\n for (var i = 0; i < this.bandsDataSpin1.length; i++) // loop the segments\n\n for (var j = 0; j < this.bandsDataSpin1[i].length; j++) { // loop the bands\n polylinePoints = '';\n for (var k = 0; k < this.bandsDataSpin1[i][j].length; k++) { // loop the kpoints\n polylinePoints+= ' '+this.xRel*this.bandsDataSpin1[i][j][k].x+\n ' '+this.transformY(this.bandsDataSpin1[i][j][k].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1');\n }\n\n if (this.bandsDataSpin2.length > 0){\n for (var i = 0; i < this.bandsDataSpin2.length; i++) // loop the segments\n\n for (var j = 0; j < this.bandsDataSpin2[i].length; j++) { // loop the kpoints\n polylinePoints = '';\n for (var k = 0; k < this.bandsDataSpin2[i][j].length; k++) { // loop the bands\n polylinePoints+= ' '+this.xRel*this.bandsDataSpin2[i][j][k].x+\n ' '+this.transformY(this.bandsDataSpin2[i][j][k].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2');\n }\n }\n\n // Paint CBM and VBM\n if (this.bandGapData !== undefined) {\n this.bandGapData.cbmDistances.forEach( distance => {\n let x = this.xRel * distance;\n let y = this.transformY((this.bandGapData.conduction_band_min_energy-this.energyOffset)/this.factor);\n svg.addPoint(this.plotContent, x, y , 3, 'cbm-vbm-points');\n svg.addText(this.plotContent, x+4, y-6, 'CBM');\n });\n\n this.bandGapData.vbmDistances.forEach( distance => {\n let x = this.xRel*distance;\n let y = this.transformY((this.bandGapData.valence_band_max_energy-this.energyOffset)/this.factor);\n svg.addPoint(this.plotContent, x, y, 3, 'cbm-vbm-points');\n svg.addText(this.plotContent, x+4, y+14, 'VBM');\n });\n }\n }\n}\n\n\n//function kPointDistance(kPoints, position){\n //let p0 = kPoints[0];\n //let p1 = kPoints[position];\n //let deltaX = p1[0] - p0[0];\n //let deltaY = p1[1] - p0[1];\n //let deltaZ = p1[2] - p0[2];\n //return Math.sqrt(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ);\n//}\n\nfunction kPointDistance(p0, p1){\n let deltaX = p1[0] - p0[0];\n let deltaY = p1[1] - p0[1];\n let deltaZ = p1[2] - p0[2];\n return Math.sqrt(deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ);\n}\n\nfunction getSymbol(label){\n if (label === 'Gamma' || label === 'G') return 'Γ';\n else return label;\n}\n\n// EXPORTS\nmodule.exports = BSPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/BSPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/CalcSelectorBar.js": +/*!*********************************************!*\ + !*** ./src/material-mod/CalcSelectorBar.js ***! + \*********************************************/ +/***/ ((module) => { + +eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This class implements the bar to select calculations on a material\n */\n\n\nclass CalcSelectorBar {\n\n constructor(className, width){\n this.first = true;\n this.last = false;\n this.element = document.createElement('div');\n this.element.className = className;\n if (width !== undefined) this.element.style.width = width;\n this.element.innerHTML = `\n <div class=\"prev-sel-btn\" style=\"float: left; width: 20%;\">\n <div style=\"padding-left: 10%;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 10.069 11.872\" width=\"20px\">\n <path transform=\"scale(0.7) translate(-346.291 -664.481)\"\n 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\" />\n </svg>\n </div>\n </div>\n <div class=\"calc-sel-text\" style=\"float: left; width: 60%;\">\n NOT Calculation\n </div>\n <div class=\"next-sel-btn\" style=\"float: right; width: 20%;\">\n <div style=\"padding-right: 10%;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-10.069 -11.872 10.069 11.872\" width=\"20px\">\n <g transform=\"rotate(180) scale(0.7)\">\n <path d=\"M10.069,1.544,8.525,0,0,8.513,8.493,16.96l1.544-1.544-6.8-6.9Z\"/>\n </g>\n </svg>\n </div>\n </div>\n <div style=\"clear: both;\"></div>\n `;\n\n this.prevBtn = this.element.querySelector('.prev-sel-btn');\n this.prevIcon = this.element.querySelector('.prev-sel-btn path');\n this.calcSelectorTxt = this.element.querySelector('.calc-sel-text');\n this.nextBtn = this.element.querySelector('.next-sel-btn');\n this.nextIcon = this.element.querySelector('.next-sel-btn path');\n this._styleButtons();\n this._events();\n }\n\n\n _events() {\n this.prevBtn.addEventListener( \"click\", e => {\n e.preventDefault();\n if (this.first) return;\n /*** repensar esto es problematico porque necesita poder ser configurado desde fuera **/\n //if (this.last) this.last = false;\n this.first = this.prevListener();\n this.last = false;\n this._styleButtons();\n });\n\n this.nextBtn.addEventListener( \"click\", e => {\n e.preventDefault();\n if (this.last) return;\n //if (this.first) this.first = false;\n // this.last = this.nextListener();\n this.first = false;\n this.last = this.nextListener();\n this._styleButtons();\n });\n }\n\n\n _styleButtons(){\n this.prevIcon.setAttribute(\"class\",\n 'calc-selector-icon'+(this.first ? '-disabled' : ''));\n this.nextIcon.setAttribute(\"class\",\n 'calc-selector-icon'+(this.last ? '-disabled' : ''));\n }\n\n\n setPrevListener(listener){\n this.prevListener = listener;\n }\n\n\n setNextListener(listener){\n this.nextListener = listener;\n }\n\n setState(text, first, last){\n this.calcSelectorTxt.innerHTML = text;\n this.first = first;\n this.last = last;\n this._styleButtons();\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = CalcSelectorBar;\n\n\n//# sourceURL=webpack:///./src/material-mod/CalcSelectorBar.js?"); + +/***/ }), + +/***/ "./src/material-mod/DOSPlotter.js": +/*!****************************************!*\ + !*** ./src/material-mod/DOSPlotter.js ***! + \****************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Implements the DOS plotter.\n */\n\n\n\n\nlet InteractivePlotterBase = __webpack_require__(/*! ../common/InteractivePlotterBase.js */ \"./src/common/InteractivePlotterBase.js\");\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\n\nconst E_MIN = -6;\nconst E_MAX = 11;\nconst E_FACTOR = 1.602176565e-19;\nconst DOSVALUE_FACTOR = 1.602176565e-19;\n\n\nclass DOSPlotter extends InteractivePlotterBase {\n\n constructor(margins) {\n super(margins);\n }\n\n // detach if necessary and attach\n attach(element, width, height){\n super.attach(element, width, height);\n }\n\n setPoints(points, calcData) {\n this.pointsSpin1 = [];\n this.pointsSpin2 = [];\n this._reset();\n\n let pSpin1 = points.dos_values[0];\n let pSpin2 = null;\n if (points.dos_values.length === 2) pSpin2 = points.dos_values[1];\n let pointsY = points.dos_energies;\n let pointsXInPlotRange = [];\n let pointsYInPlotRange = [];\n\n for (var i = 0; i < pointsY.length; i++) {\n let energy = pointsY[i]/E_FACTOR;\n let dos_value_spin1 = pSpin1[i]*DOSVALUE_FACTOR;\n\n // Arrays to calculate the range to be represented\n pointsXInPlotRange.push(dos_value_spin1);\n pointsYInPlotRange.push(energy);\n\n this.pointsSpin1.push({x: dos_value_spin1, y: energy});\n if (pSpin2 !== null){\n let dos_value_spin2 = pSpin2[i]*DOSVALUE_FACTOR;\n this.pointsSpin2.push({x: dos_value_spin2, y: energy});\n pointsXInPlotRange.push(dos_value_spin2);\n }\n }\n\n let maxDosVal = Math.max.apply(null, pointsXInPlotRange);\n let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange);\n let minEnergyVal = Math.min.apply(null, pointsYInPlotRange);\n\n // x axis steps generation\n let t = util.generateDiagramSteps(maxDosVal, 3);\n let xSteps = t[0], exp = t[1];\n\n this.setAxisRangeAndLabels(null, 0, xSteps[xSteps.length-1], 'Energy (eV)',\n E_MIN, E_MAX, minEnergyVal, maxEnergyVal, 5);\n\n svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom,\n 'DOS (states/eV/cell)', 'middle', 'axis-steps-big');\n\n // draw x axis steps\n for (let i = 0; i < xSteps.length; i++) {\n let stepX = (this.plotRangeX*xSteps[i])/xSteps[xSteps.length-1];\n svg.addLine(this.axisGroup, stepX, 0, stepX, 3, 1);\n let x = xSteps[i];\n x = (Math.abs(x) >= 10000 || Math.abs(x) <= 0.0001) ? x.toExponential(1) : parseFloat(x.toFixed(3));\n svg.addText(this.axisGroup, stepX, 13,\n (i === 0 ? '0' : x), 'middle', 'axis-steps-smaller');\n }\n\n this.repaint();\n }\n\n\n repaintData(){\n\n let polylinePoints = '';\n for (var i = 0; i < this.pointsSpin1.length; i++) {\n polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+\n ' '+this.transformY(this.pointsSpin1[i].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1');\n\n polylinePoints = '';\n for (var i = 0; i < this.pointsSpin2.length; i++) {\n polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+\n ' '+this.transformY(this.pointsSpin2[i].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2');\n }\n\n\n setYAxisLabelsVisibility(value){\n if (this.yAxisLabelsGroup !== null)\n this.yAxisLabelsGroup.style.visibility = (value ? 'visible' : 'hidden');\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = DOSPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/DOSPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/DataStore.js": +/*!***************************************!*\ + !*** ./src/material-mod/DataStore.js ***! + \***************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n This file create and holds the application data models.\n It stores the data loaded from the backend (immutable).\n In addition it creates app life-time entities for convenience\n */\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\nlet materialData;\nlet groups;\nlet calcsInGroups;\nlet calcs;\nlet representatives;\nlet idealizedStructure;\nlet calcMap = new Map();\nlet ready = false;\nlet hasThermal;\nlet hasElecStructure;\nlet missing = false;\n\nfunction setMaterialData(dataFromAPI){\n materialData = dataFromAPI;\n}\n\nfunction getMaterialData(){\n return materialData;\n}\n\nfunction setMissing(value) {\n missing = value;\n}\n\nfunction getMissing() {\n return missing;\n}\n\nfunction setIdealizedStructure(structure){\n idealizedStructure = structure;\n}\n\nfunction getIdealizedStructure(){\n return idealizedStructure;\n}\n\nfunction setCalculations(calcsFromAPI){\n\n // Clean up null values already here.\n calcs = calcsFromAPI.results;\n calcs.forEach(function(calc, index) {\n let functional_type = util.getDefault(calc.functional_type);\n let core_electron_treatment = util.getDefault(calc.core_electron_treatment);\n this[index].functional_type = functional_type;\n this[index].core_electron_treatment = core_electron_treatment;\n }, calcs);\n\n representatives = calcsFromAPI.representatives;\n for (let i = 0; i < calcs.length; i++) {\n calcMap.set(calcs[i].calc_id, calcs[i]);\n }\n\n // Check what type of information is available\n let calcWithBS = representatives.electronic_band_structure;\n let calcWithDOS = representatives.electronic_dos;\n let calcWithHeat = representatives.thermodynamical_properties;\n hasElecStructure = (calcWithBS !== undefined || calcWithDOS !== undefined);\n hasThermal = calcWithHeat !== undefined;\n}\n\nfunction getRepresentatives() {\n return representatives;\n}\n\nfunction getCalculations(){\n return calcs;\n}\n\nfunction getCalc(calcId){\n return calcMap.get(calcId);\n}\n\n/**\n * Stores the group information from API into an easily accessible format.\n */\nfunction setGroups(groupsFromAPI) {\n let mapEos = new Map(Object.entries(groupsFromAPI.groups_eos));\n let mapPar = new Map(Object.entries(groupsFromAPI.groups_par));\n groups = new Map();\n groups.set(\"eos\", mapEos);\n groups.set(\"par\", mapPar);\n calcsInGroups = new Set();\n mapEos.forEach((calculations, group_id) => {\n calculations.forEach(calcsInGroups.add, calcsInGroups);\n });\n}\n\nfunction getGroups() {\n return groups;\n}\n\nfunction getGroupType(leafId) {\n return leafId.substring(0, 3);\n}\n\nfunction isGroup(leafId) {\n return (leafId.substring(0,4) === 'eos/' || leafId.substring(0,4) === 'par/');\n}\n\nfunction getReprCalc(leafId){\n // For groups, the first calculation is the representative.\n if (isGroup(leafId)) {\n let type = getGroupType(leafId);\n let groupId = getGroupId(leafId);\n return groups.get(type).get(groupId)[0];\n }\n else return leafId;\n}\n\nfunction getGroupId(leafId) {\n return leafId.substring(4);\n}\n\nfunction isReady(matId) {\n if (materialData !== undefined) {\n if (missing && matId == materialData.material_id) {\n return true;\n }\n if (idealizedStructure !== undefined) {\n if (calcs !== undefined) {\n if (groups !== undefined) {\n if (matId === materialData.material_id) {\n return true;\n }\n }\n }\n }\n }\n return false;\n}\n\nfunction clear() {\n materialData = undefined;\n calcs = undefined;\n groups = undefined;\n idealizedStructure = undefined;\n missing = false;\n}\n\nfunction isInAnyGroup(calcId){\n return calcsInGroups.has(calcId);\n}\n\nfunction isInAnyNotDisabledGroup(calcId) {\n return calcsInGroups.has(calcId);\n}\n\nfunction getGroupLeafId(calcId){\n let leafId = null;\n groups.forEach( (groupData, groupId) => {\n if (groupData.calculation_set.has(calcId)) leafId = groupId;//return true;\n });\n return leafId;\n}\n\nfunction hasThermalData(bool) {\n return hasThermal;\n}\n\nfunction hasElecStructureData(bool){\n return hasElecStructure;\n}\n\n\n// EXPORTS\nmodule.exports = {\n getRepresentatives,\n setMaterialData,\n getMaterialData,\n getCalculations,\n setCalculations,\n getCalc,\n getGroups,\n setGroups,\n getGroupId,\n isGroup,\n getGroupType,\n getReprCalc,\n isInAnyGroup,\n isInAnyNotDisabledGroup,\n getGroupLeafId,\n isReady,\n setMissing,\n getMissing,\n clear,\n setIdealizedStructure,\n getIdealizedStructure,\n hasThermalData,\n hasElecStructureData\n};\n\n\n//# sourceURL=webpack:///./src/material-mod/DataStore.js?"); + +/***/ }), + +/***/ "./src/material-mod/DetailsViewBase.js": +/*!*********************************************!*\ + !*** ./src/material-mod/DetailsViewBase.js ***! + \*********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Base class from which other main 'Details' view containers inherit.\n Every of these containers shows all the material info related to a type of info:\n Structure, Electronic Structure, etc\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n\nclass DetailsViewBase {\n\n constructor(domId) {\n this.element = document.createElement('div');\n this.element.setAttribute('id',domId);\n this.loadedMatId = null;\n this.gotoResultsListener= null;\n this.gotoOverviewListener= null;\n this.element.innerHTML= '<div class=\"material-title\"></div>';\n this.element.style.display = 'none';\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this.materialTitle= this.element.querySelector('.material-title');\n }\n\n\n attachNavTree(navTree){\n navTree.attach(this.navTreeWrapper);\n }\n\n\n setVisible() {\n this.element.style.display = 'block';\n }\n\n /**\n * Used to load material data into this view. Only loads the data if the\n * material has changed since last visit to this view.\n */\n load() {\n let data = DataStore.getMaterialData();\n let matId = data.material_id;\n if (this.loadedMatId === matId) {\n return;\n }\n this.loadedMatId = matId;\n this.setMaterialData();\n }\n\n setMaterialData() {\n this.materialTitle.innerHTML= util.getMaterialTitle(DataStore.getMaterialData());\n }\n\n\n updateCalcs(calcs){\n }\n\n\n updateMarkedCalc(calc){\n }\n\n}\n\n// EXPORTS\nmodule.exports = DetailsViewBase;\n\n\n//# sourceURL=webpack:///./src/material-mod/DetailsViewBase.js?"); + +/***/ }), + +/***/ "./src/material-mod/ElasticConstDetails.view.js": +/*!******************************************************!*\ + !*** ./src/material-mod/ElasticConstDetails.view.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n\n/*\nPending to implement\n*/\n\n\nlet DetailsViewBase = __webpack_require__(/*! ./DetailsViewBase.js */ \"./src/material-mod/DetailsViewBase.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\n//let DataStore = require('./DataStore.js');\n\n\n\nclass ElasticConstDetails extends DetailsViewBase {\n\n constructor() {\n super('Elastic constants');\n\n this.sortedLeafs = [];\n this.markedCalc = null;\n\n this.element.innerHTML+=`\n\n <div style=\"float: left; width: 27%;\">\n <div class=\"view-box\">\n <div class=\"title\">Calculation </div>\n <div class=\"navTreeWrapper\"></div>\n </div>\n </div>\n\n <div style=\"float: right; width: 73%;\">\n <div class=\"view-box\">\n <div class=\"title\">Elastic constants</div>\n\n <div>Parameters</div>\n <div ></div>\n\n\n </div>\n </div>\n\n <div style=\"clear: both;\"></div>\n `;\n\n this.navTreeWrapper =\n this.element.querySelector('.navTreeWrapper');\n\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n\n this._events();\n }\n\n\n _events() {\n //super._events();\n/*\n this.dataTableWrapper.addEventListener('click', (e) => {\n\n let rowElement = e.target.parentElement;\n if (rowElement.className.indexOf('data-row') < 0)\n rowElement = rowElement.parentElement;\n //console.log(\"TABLE EVENT \",rowElement);\n\n if (rowElement.className.indexOf('data-row') >= 0){\n let id= rowElement.getAttribute('data-calc-id');\n\n\n }\n\n });\n*/\n }\n\n\n\n updateSelection( leafIds /*Set*/){\n console.log('ElasticDetails updateSelection ',leafIds);\n\n }\n\n\n updateMarkedLeaf(leafId){\n\n/* Do nothing for now...\n this.markedCalc = leafId;\n let rowElement= this.element.querySelector('.data-row-marked');\n if (rowElement !== null) rowElement.className= 'data-row';\n\n if (this.markedCalc !== null){\n let rowElement1= document.querySelector('tr[data-calc-id=\"'+this.markedCalc+'\"]');\n if (rowElement1 !== null) rowElement1.className= 'data-row-marked';\n }\n */\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = ElasticConstDetails;\n\n\n//# sourceURL=webpack:///./src/material-mod/ElasticConstDetails.view.js?"); + +/***/ }), + +/***/ "./src/material-mod/ElectronicStructDetails.view.js": +/*!**********************************************************!*\ + !*** ./src/material-mod/ElectronicStructDetails.view.js ***! + \**********************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n 'Details' view container that shows all the material info related to\n its electronic structure.\n\n This container is quite complex.\n\n In the file there are two defined (classes) components used in the container:\n - SummaryByFunctionalsComponent: the component (left panel, below part)\n showing a band gap summary info by functional.\n - BZViewerWrapper: This component is a wrapper for the Brillouin zone viewer\n */\n\n\n\nlet DetailsViewBase = __webpack_require__(/*! ./DetailsViewBase.js */ \"./src/material-mod/DetailsViewBase.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet NavTree = __webpack_require__(/*! ./NavTree.js */ \"./src/material-mod/NavTree.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet CalcSelectorBar = __webpack_require__(/*! ./CalcSelectorBar.js */ \"./src/material-mod/CalcSelectorBar.js\");\nlet StatsViewer = __webpack_require__(/*! ./StatsViewer.js */ \"./src/material-mod/StatsViewer.js\");\nlet BSDOSPlotter = __webpack_require__(/*! ./BSDOSPlotter.js */ \"./src/material-mod/BSDOSPlotter.js\");\nlet PlotterBase = __webpack_require__(/*! ../common/PlotterBase.js */ \"./src/common/PlotterBase.js\");\nlet plotter= new PlotterBase();\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nfunction setFermiVizContent(fermiBox,url){\n let sceneContent;\n if (url === ''){\n fermiBox.innerHTML= '';//fermiBox.removeChild(plotter.canvas);\n plotter.attach(fermiBox,undefined,316);\n plotter.setNoData();\n }\n else{\n sceneContent= '<inline url=\"'+url+'\"> </inline>';\n fermiBox.innerHTML=\n `<x3d id=\"x3dframe\" width='290px' height='290px' >\n <scene>${sceneContent} </scene>\n </x3d>`;\n x3dom.reload();\n }\n\n}\n\nclass ElectronicStructDetails extends DetailsViewBase {\n\n constructor() {\n super('Electronic Structure');\n this.navTree = new NavTree();\n this.element.innerHTML+=`\n <div style=\"float: left; width: 30%;\">\n <div class=\"view-box\">\n <div class=\"title\">Calculations </div>\n <div class=\"navTreeWrapper\"></div>\n\n <div class=\"summary-title\">Summary</div>\n <div class=\"footer summary-box\" style=\"border-top: 0\"></div>\n </div>\n </div>\n\n <div style=\"float: right; width: 70%;\">\n <div class=\"view-box e-structure-box\">\n\n <div class=\"title\">Electronic structure</div>\n\n <div style=\"padding-top: 10px; \" >\n <div class=\"tree-leaf-title\"></div>\n </div>\n\n <div>\n\n <div style=\"padding: 30px 100px 20px 100px; \">\n <div class=\"info-fields-label\" style=\"float: left; width: 54%; \">\n <span info-sys-data=\"band-structure\">Band structure</span>\n </div>\n <div class=\"info-fields-label\" style=\"float: left;\">\n <span info-sys-data=\"DOS\">DOS</span>\n </div>\n <div style=\"clear: both;\"></div>\n\n <div class=\"calc-bs-dos-plotter\" >\n </div>\n\n <div>\n <div class=\"band-gap-field\" style=\"float: left; width: 56%; text-align: right\">\n <b><span info-sys-data=\"band-gap\">Band gap</span></b>:\n <span class=\"band-gap-value\" ></span>\n </div>\n <div style=\"clear: both;\"></div>\n\n </div>\n </div>\n\n <div class=\"spin-legend\" style=\"font-size: 0.9em; padding: 0 30px 10px; display: none\">\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin1\"/></svg>\n Spin <span style='font-size: 1.1em'>⇧</span> \n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin2\"/></svg>\n Spin <span style='font-size: 1.1em'>⇩</span>\n </div>\n\n </div>\n\n <div class=\"footer lower-section\">\n\n <div style=\"float: left\">\n <div style=\"padding: 16px; \">\n <div class=\"info-fields-label\" >\n <span info-sys-data=\"brillouin-zone-viewer\">Brillouin zone</span>\n </div>\n <div class=\"bz-viewer-wrapper\" style=\"width: 400px; height: 400px\">\n </div>\n </div>\n </div>\n\n <div class=\"band\" style=\"float: right; width: 40%;\">\n <div style=\"padding: 16px; \">\n <div class=\"info-fields-label\" >\n <!-- <span info-sys-data=\"fermi-surface\">Fermi surface </span> -->\n </div>\n <div class=\"fermi-box\" > </div>\n </div>\n </div>\n\n <div style=\"clear: both;\"></div>\n\n </div> <!-- footer -->\n\n </div>\n\n </div> <!-- view-box -->\n `;\n\n this.navTreeWrapper =\n this.element.getElementsByClassName(\"navTreeWrapper\")[0];\n\n this.leafTitle = this.element.querySelector('.tree-leaf-title');\n\n this.rightBox = this.element.querySelector('.e-structure-box');\n\n this.summaryByFunctionals = null;\n\n this.summaryBox = this.element.querySelector('.summary-box');\n this.summaryByFunctionals = new SummaryByFunctionalsComponent(this.summaryBox);\n\n this.bsDosPlotter = new BSDOSPlotter();\n\n this.bandGapField = this.element.querySelector('.band-gap-field');\n this.bandGapValue = this.element.querySelector('.band-gap-value');\n\n this.spinLegend = this.element.querySelector('.spin-legend');\n\n this.lowerSection = this.element.querySelector('.lower-section');\n\n this.fermiBox= this.element.getElementsByClassName('fermi-box')[0];\n // Load the x3dom library\n //util.loadLib(\"lib/x3dom.js\");\n\n this.bzViewerWrapper = new BZViewerWrapper(this.element.querySelector('.bz-viewer-wrapper'));\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n }\n\n setVisible() {\n this.element.style.display = 'block';\n this.bzViewerWrapper.resize();\n }\n\n setMaterialData() {\n let data = DataStore.getMaterialData();\n super.setMaterialData(data);\n\n // Build the navigation tree and attach listeners\n let name = (data.material_name === null ?\n data.formula : data.material_name);\n this.navTree.build(name, \"electronic\");\n this.navTree.selectAll();\n this.navTree.setHeight(400);\n this.navTree.setMarkedLeafIfNoneMarked(null);\n this.attachNavTree(this.navTree);\n this.updateSelection(this.navTree.getTreeSelectedCalcs());\n this.updateMarkedLeaf(this.navTree.getMarkedLeaf());\n this.navTree.setTreeSelectionListener( leafIds => {\n this.updateSelection(leafIds);\n });\n this.navTree.setLeafMarkedListener( leafId => {\n this.updateMarkedLeaf(leafId);\n });\n }\n\n updateSelection(leafIds) {\n if (leafIds.size > 0) {\n\n // Create a list of calculation ids for which statistics are fetched. For\n // calculation groups, the first calculation is included in the\n // statistics, as it corresponds to the lowest energy calculation.\n let calcs = [];\n leafIds.forEach(leafId => {\n calcs.push(DataStore.getCalc(leafId));\n });\n\n // Groups calculation by their functional\n let calcMapByFunctional = util.getCalcMapByFunctional(calcs);\n\n // Request new summary\n this.summaryByFunctionals.build(calcMapByFunctional);\n\n // Hide statistics box if no selections are made\n } else {\n this.summaryBox.style.visibility = 'hidden';\n }\n }\n\n /**\n * Called whenever individual calculations are selected in the navigation\n * tree.\n */\n updateMarkedLeaf(leafId) {\n if (leafId === null) {\n this.leafTitle.innerHTML = 'NO SELECTION';\n } else {\n this.leafTitle.innerHTML = util.getShortCode(leafId);\n this._loadGraphData(leafId);\n }\n }\n\n _updateSelectorState(leafId) {\n this.leafTitle.innerHTML = util.getShortCode(leafId);\n }\n\n _loadGraphData(calcId) {\n let calc = DataStore.getCalc(calcId);\n if (!this.bsDosPlotter.isAttached())\n this.bsDosPlotter.attach(this.element.querySelector('.calc-bs-dos-plotter')\n ,undefined,360);\n\n if (calc === null || (!calc.has_band_structure && !calc.has_dos)){\n this.bsDosPlotter.setNoData();\n this.bzViewerWrapper.setNoData();\n this.bandGapField.style.display = 'none';\n this.lowerSection.style.display = 'none';\n\n } else {\n LoadingPopup.show(\"electronic_structure\");\n let matId = DataStore.getMaterialData().material_id;\n\n let url = util.getMaterialCalcURL(matId, calc.calc_id);\n let query = JSON.stringify({properties: [\"electronic_band_structure\", \"electronic_dos\", \"band_gap\"]});\n util.serverReqPOST(url, query, e => {\n let response = JSON.parse(e.target.response);\n let dosData = response.electronic_dos;\n let bsData = response.electronic_band_structure;\n let bandGap = response.band_gap;\n if (bothSpins(bsData, dosData)) {\n this.spinLegend.style.display = 'block';\n }\n this.bsDosPlotter.setUpAndData(bsData, dosData, calc);\n\n // Hide or show band gap\n if (bandGap !== undefined) {\n this.bandGapValue.textContent = util.J2eV(bandGap, 2) + ' eV ';\n this.bandGapField.style.display = 'block';\n } else {\n this.bandGapValue.textContent = \"\";\n this.bandGapField.style.display = 'none';\n }\n\n // Show Brillouin zone viewer and band gap for calculation with BS\n if (bsData !== undefined) {\n if (bsData.brillouin_zone !== undefined) {\n this.lowerSection.style.display = 'block';\n this.bzViewerWrapper.setCalcData(bsData);\n } else {\n this.lowerSection.style.display = 'none';\n this.bzViewerWrapper.setNoData();\n }\n } else {\n this.lowerSection.style.display = 'none';\n this.bzViewerWrapper.setNoData();\n }\n LoadingPopup.hide(\"electronic_structure\");\n });\n }\n\n function bothSpins(bsData, dosData){\n if (bsData !== undefined){\n if (bsData.section_k_band_segment[0].band_energies.length === 2) return true;\n }\n if (dosData !== undefined) {\n if (dosData.dos_values.length === 2) return true;\n }\n return false;\n }\n }\n\n setPrevCalcListener(listener){\n this.prevCalcListener = listener;\n }\n\n setNextCalcListener(listener){\n this.nextCalcListener = listener;\n }\n}\n\n\n\nclass SummaryByFunctionalsComponent{\n\n constructor(hostElement){\n this.calcMapByFunctional = null;\n this.hostElement = hostElement;\n this.viewType = 'text';\n this.functional = null;\n this.nBins = 15;\n this.hostElement.innerHTML+=`\n <div style=\"float: left\" >\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"chart-tab\"\n viewBox=\"0 0 15 15\" width=\"15\" height=\"15\" style=\"fill: #c7c7c7;\">\n <rect x=\"0\" y=\"0\" width=\"2\" height=\"15\" />\n <rect x=\"3\" y=\"5\" width=\"1.8\" height=\"7\" />\n <rect x=\"6\" y=\"3\" width=\"1.8\" height=\"9\" />\n <rect x=\"9\" y=\"6\" width=\"1.8\" height=\"6\" />\n <rect x=\"12\" y=\"2\" width=\"1.8\" height=\"10\" />\n <rect x=\"2\" y=\"13\" width=\"13\" height=\"2\" />\n </svg>\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"text-tab\"\n viewBox=\"0 0 15 15\" width=\"15\" height=\"15\" style=\"fill: #777;\">\n <rect x=\"0\" y=\"1\" width=\"15\" height=\"2.5\" />\n <rect x=\"0\" y=\"6\" width=\"15\" height=\"2.5\" />\n <rect x=\"0\" y=\"11\" width=\"15\" height=\"2.5\" />\n </svg>\n </div>\n\n <div class=\"functional-tabs\" style=\"float: right\">\n </div>\n\n <div style=\"clear: both;\"></div>\n\n <div class=\"content-placeholder\" >\n\n <div style=\"display: block\" class=\"text-panel\" >\n\n <div><b><span info-sys-data=\"band gap\">Band gap</span></b> (eV):\n <div class=\"stats-fields summary-bandgap-field\" > </div>\n </div>\n </div>\n\n <div style=\"display:none\" class=\"chart-panel\" >\n <div class=\"charts-placeholder\" > </div>\n </div>\n\n </div>\n `;\n this.chartTab = this.hostElement.querySelector('.chart-tab');\n this.textTab = this.hostElement.querySelector('.text-tab');\n this.functionalTabs = this.hostElement.querySelector('.functional-tabs');\n this.chartPanel = this.hostElement.querySelector('.chart-panel');\n this.textPanel = this.hostElement.querySelector('.text-panel');\n this.bandgapField = this.hostElement.querySelector('.summary-bandgap-field');\n this.statsViewer = new StatsViewer();\n\n this.chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder');\n\n this.chartTab.addEventListener( \"click\", e => {\n this.chartTab.style.fill = '#777';\n this.viewType = 'chart';\n this.textTab.style.fill = '#c7c7c7';\n this.chartPanel.style.display = 'block';\n this.textPanel.style.display = 'none';\n });\n\n this.textTab.addEventListener( \"click\", e => {\n this.textTab.style.fill = '#777';\n this.viewType = 'text';\n this.chartTab.style.fill = '#c7c7c7';\n this.textPanel.style.display = 'block';\n this.chartPanel.style.display = 'none';\n });\n\n this.functionalTabs.addEventListener( \"click\", e => {\n if (e.target.className === 'tab') {\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]')\n .className = 'tab';\n this.functional = e.target.getAttribute('data-tab');\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]')\n .className = 'tab-selected';\n this._setData();\n }\n });\n\n }\n\n _setData() {\n // Get calculations with selected functional\n let functional = this.functional;\n let calcs = Array.from(this.calcMapByFunctional.get(this.functional));\n let matId = DataStore.getMaterialData().material_id;\n\n // Query band gap statistics from the API\n let query = JSON.stringify({\n calculations: calcs,\n properties: [\"band_gap\"],\n n_histogram_bins: this.nBins,\n });\n util.serverReqPOST(util.getMaterialStatsURL(matId), query, e3 => {\n let results = JSON.parse(e3.target.response);\n let stats = results.band_gap;\n if (stats === undefined) {\n this.bandgapField.innerHTML = \"No band gap data available\";\n this.statsViewer.clear();\n this.chartsPlaceholder.textContent = \"No band gap data available\";\n return;\n } else {\n this.chartsPlaceholder.textContent = \"\";\n this.statsViewer.attach(this.chartsPlaceholder, 250, 150);\n }\n\n // Unit conversion\n stats.min *= 6.241509e18;\n stats.avg *= 6.241509e18;\n stats.max *= 6.241509e18;\n stats.histogram.values = stats.histogram.values.map(x => x*6.241509e18);\n\n // Set statistics text\n let decimals = 3;\n let html = stats.avg.toFixed(decimals)+\n ' <span style=\"font-size: 0.9em\">['+stats.min.toFixed(decimals)\n +' , '+stats.max.toFixed(decimals)+']</span>';\n this.bandgapField.innerHTML = html;\n\n // Set histogram data\n this.statsViewer.clear();\n this.statsViewer.drawPoints(stats.histogram, \"Band gap (eV)\", this.nBins);\n });\n }\n\n build(calcMapByFunctional) {\n this.calcMapByFunctional = calcMapByFunctional;\n this.graphTrigger = null;\n this.statsViewer.clear();\n this.unfoldedElement = null;\n this.functionalQuantityMap = new Map();\n\n // Create tabs for each functional\n this.functionalTabs.innerHTML = \"\";\n this.calcMapByFunctional.forEach((calcs, functionalName) => {\n this.functionalTabs.innerHTML +=\n '<span class=\"tab\" data-tab=\"'+functionalName+'\">'+functionalName+'</span>';\n });\n\n // Preselect the first functional\n let functionals = Array.from(this.calcMapByFunctional.keys());\n if (this.functional === null || functionals.indexOf(this.functional) < 0) {\n this.functional = functionals[0]; // the first one is selected\n }\n this._setData();\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]').className = 'tab-selected';\n\n InfoSys.addToInfoSystem(this.hostElement);\n }\n}\n\nclass BZViewerWrapper {\n\n constructor(hostElement) {\n this.hostElement = hostElement;\n this.loaded = false;\n let options = {\n view: {\n autoResize: false,\n autoFit: true,\n fitMargin: 0.018,\n }\n };\n this.bzViewer = new matviewer.BrillouinZoneViewer(this.hostElement, options);\n }\n\n setCalcData(bsData) {\n this.bzViewer.load(this._getBZDataForViewer(bsData));\n this.hostElement.style.visibility = 'visible';\n this.loaded = true;\n this.resize();\n }\n\n setNoData() {\n this.hostElement.style.visibility = 'hidden';\n }\n\n resize() {\n this.bzViewer.resizeCanvasToHostElement();\n if (this.loaded) {\n this.bzViewer.fitToCanvas();\n this.bzViewer.render();\n }\n }\n\n _getBZDataForViewer(bsData){\n let zone = bsData.brillouin_zone; // The BZ data is stored as string for now.\n let labels = [];\n let kPoints = [];\n bsData.section_k_band_segment.forEach( segment => {\n labels.push(segment.band_segm_labels);\n kPoints.push(segment.band_segm_start_end);\n });\n\n let data = {\n vertices: zone.vertices,\n faces: zone.faces,\n basis: bsData.reciprocal_cell,\n labels: labels,\n segments: kPoints\n };\n return data;\n }\n}\n\n// EXPORTS\nmodule.exports = ElectronicStructDetails;\n\n\n//# sourceURL=webpack:///./src/material-mod/ElectronicStructDetails.view.js?"); + +/***/ }), + +/***/ "./src/material-mod/EquationOfStateViewer.js": +/*!***************************************************!*\ + !*** ./src/material-mod/EquationOfStateViewer.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n Equation of State plotter implementation\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet PlotterBase = __webpack_require__(/*! ../common/PlotterBase.js */ \"./src/common/PlotterBase.js\");\n\n\nclass EquationOfStateViewer extends PlotterBase{\n\n constructor() {\n super({left: 60, right: 20, top: 30, bottom: 40});\n this.tooltip;\n this.calcPointMap= new Map();\n this.pointSelected = null;\n }\n\n\n draw(pointsX, pointsY, calcIds, eZero){\n\n for (let i = 0; i < pointsY.length; i++) {\n if (pointsY[i] !== 555) pointsY[i] -= eZero;\n }\n\n function remove555(points){\n let goodPoints = [];\n points.forEach( p => {\n if (p !== 555) goodPoints.push(p);\n });\n return goodPoints;\n }\n let goodPointsY = remove555(pointsY);\n\n let xMin = Math.min.apply(null,pointsX);\n let xMax = Math.max.apply(null,pointsX);\n let yMin = 0;\n let yMax = Math.max.apply(null,goodPointsY);\n\n if (xMin === xMax) { xMin -= 1; xMax += 1; }\n else{\n let gap = xMax - xMin;\n xMin -= gap*0.1; xMax += gap*0.1;\n }\n if (yMin === yMax) { yMin -= 1; yMax += 1; }\n else{\n let gap = yMax - yMin;\n yMin -= gap*0.15; yMax += gap*0.1;\n }\n\n this.setRangeAndLabels('Volume (ų)', xMin, xMax,'E - Eₘᵢₙ (eV)', yMin, yMax);\n this.drawAxis(2, 2);\n\n // Draw reference line\n svg.addLine(this.plotArea, 0, this.y(0), this.plotRangeX, this.y(0), 'zeroline');\n\n svg.addText(this.plotArea, this.x(xMax),\n this.y(0)+12, 'Eₘᵢₙ: '+eZero.toFixed(3)+' eV', 'end', 'axis-steps');\n\n // Draw points\n for (let i = 0; i < pointsX.length; i++) {\n\n let styleClass = (i === 0 ? 'eos-viewer-sel' : 'eos-viewer');\n let r = (i === 0 ? 6 : 3);\n let yVal = (pointsY[i] === 555 ? 20 : this.y(pointsY[i])); // Trick\n\t let pointElement =\n svg.addPoint(this.plotArea,this.x(pointsX[i]), yVal, r\n , styleClass);\n if (i === 0) this.pointSelected = pointElement;\n\n\t pointElement.addEventListener('mouseover', e => {\n\t\t this.tooltip = svg.addText(this.plotArea, e.target.getBBox().x+10,\n e.target.getBBox().y-10, 'Calc ' + util.getShortCode(calcIds[i]), 'middle', 'tooltip');\n });\n\t pointElement.addEventListener('mouseout', e => {\n\t\t svg.removeElement(this.tooltip);\n\t });\n pointElement.addEventListener('click', e => {\n\t\t //console.log('ID',calcIds[i]);\n this.clickPointListener(calcIds[i]);\n\t });\n this.calcPointMap.set(calcIds[i], pointElement);\n\t }\n }\n\n\n selectCalc(calcId){\n this.pointSelected.setAttribute('class', 'eos-viewer');\n this.pointSelected.setAttribute('r', 3);\n this.pointSelected = this.calcPointMap.get(calcId);\n this.pointSelected.setAttribute('class', 'eos-viewer-sel');\n this.pointSelected.setAttribute('r', 6);\n }\n\n\n setClickPointListener(listener){\n this.clickPointListener = listener;\n }\n\n\n x(x){\n return this.xRel*(x - this.xMin);\n }\n\n\n y(y){\n return -this.yRel*(y - this.yMin);\n }\n\n}\n\n\n\n// EXPORTS\nmodule.exports = EquationOfStateViewer;\n\n\n//# sourceURL=webpack:///./src/material-mod/EquationOfStateViewer.js?"); + +/***/ }), + +/***/ "./src/material-mod/HeatCapPlotter.js": +/*!********************************************!*\ + !*** ./src/material-mod/HeatCapPlotter.js ***! + \********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n Specific Heat plotter implementation\n */\n\n\n\n\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet PlotterBase = __webpack_require__(/*! ../common/PlotterBase.js */ \"./src/common/PlotterBase.js\");\n\n\nclass HeatCapPlotter extends PlotterBase{\n\n constructor() {\n super({left: 50, right: 16, top: 10, bottom: 32});\n this.tooltip;\n }\n\n\n setData(x, y){\n this.clear();\n\n // Null values are ignored.\n let xClean = [];\n let yClean = [];\n for (let i=0; i<x.length; ++i) {\n let xi = x[i];\n let yi = y[i];\n if (xi !== null & yi !== null) {\n xClean.push(xi);\n yClean.push(yi);\n }\n }\n\n // We only display values up to 600K\n let maxIndex;\n for (let i=0; i < x.length; ++i) {\n let ival = x[i];\n if (ival > 600) {\n maxIndex = i-1;\n break\n }\n }\n if (maxIndex === undefined) {\n maxIndex = x.length - 1;\n }\n x = x.slice(0, maxIndex+1);\n y = y.slice(0, maxIndex+1);\n\n let yMaxValue = Math.max.apply(null, y);\n let yAxisMaxValue = Math.ceil(yMaxValue/200)*200;\n let xMaxValue = x[maxIndex];\n let xAxisMaxValue = Math.ceil(xMaxValue/50)*50;\n this.setRangeAndLabels('T (K)', 0, xAxisMaxValue, 'Cv (J/K/kg)', 0, yAxisMaxValue);\n this.drawAxis(4, 4, 0);\n\n let polylinePoints = '';\n x.forEach( (t, i) => {\n let iy = y[i];\n polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(iy - this.yMin);\n });\n svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1');\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = HeatCapPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/HeatCapPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/HelmholtzPlotter.js": +/*!**********************************************!*\ + !*** ./src/material-mod/HelmholtzPlotter.js ***! + \**********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n Helmholtz free energy plotter implementation\n */\n\n\n\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet PlotterBase = __webpack_require__(/*! ../common/PlotterBase.js */ \"./src/common/PlotterBase.js\");\n\n\nclass HelmholtzPlotter extends PlotterBase {\n\n constructor() {\n super({left: 70, right: 16, top: 10, bottom: 32});\n this.tooltip;\n }\n\n\n setData(x, y) {\n this.clear();\n\n // Null values are ignored.\n let xClean = [];\n let yClean = [];\n for (let i=0; i<x.length; ++i) {\n let xi = x[i];\n let yi = y[i];\n if (xi !== null & yi !== null) {\n xClean.push(xi);\n yClean.push(yi);\n }\n }\n\n // We only display values up to 600K\n let maxIndex;\n for (let i=0; i < x.length; ++i) {\n let ival = x[i];\n if (ival > 600) {\n maxIndex = i-1;\n break\n }\n }\n if (maxIndex === undefined) {\n maxIndex = x.length - 1;\n }\n\n x = x.slice(0, maxIndex+1);\n y = y.slice(0, maxIndex+1);\n\n let yMaxValue = Math.max.apply(null, y);\n let yMinValue = Math.min.apply(null, y);\n let yAxisMaxValue = Math.ceil(yMaxValue/5000)*5000;\n let yAxisMinValue = Math.floor(yMinValue/5000)*5000;\n let xMaxValue = x[maxIndex];\n let xAxisMaxValue = Math.floor(xMaxValue/50)*50;\n\n this.setRangeAndLabels('T (K)', 0, xAxisMaxValue, 'F (J/kg)', yAxisMinValue, yAxisMaxValue);\n this.drawAxis(4, 4, 2);\n\n let polylinePoints = '';\n x.forEach( (t, i) => {\n let iy = y[i];;\n polylinePoints += ' ' + this.xRel*t + ' -' + this.yRel * (iy - this.yMin);\n });\n svg.addPolyline(this.plotArea, polylinePoints, 'plotSpin1');\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = HelmholtzPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/HelmholtzPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/MaterialMod.js": +/*!*****************************************!*\ + !*** ./src/material-mod/MaterialMod.js ***! + \*****************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the Material Module of the application.\n It's a UI component container that displays the selected material information.\n It's complex because of the amount and diversity of material info available.\n\n In this file other two inner components of the module are implemented\n (by convenience):\n StructureViewerWrapper and DropDown (used exclusively in StructureViewerWrapper)\n */\n\n\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet NavTree = __webpack_require__(/*! ./NavTree.js */ \"./src/material-mod/NavTree.js\");\nlet Overview = __webpack_require__(/*! ./Overview.view.js */ \"./src/material-mod/Overview.view.js\");\nlet StructureDetails = __webpack_require__(/*! ./StructureDetails.view.js */ \"./src/material-mod/StructureDetails.view.js\");\nlet ElectronicStructDetails = __webpack_require__(/*! ./ElectronicStructDetails.view.js */ \"./src/material-mod/ElectronicStructDetails.view.js\");\nlet MethodologyDetails = __webpack_require__(/*! ./MethodologyDetails.view.js */ \"./src/material-mod/MethodologyDetails.view.js\");\nlet ThermalPropsDetails = __webpack_require__(/*! ./ThermalPropsDetails.view.js */ \"./src/material-mod/ThermalPropsDetails.view.js\");\nlet ElasticConstDetails = __webpack_require__(/*! ./ElasticConstDetails.view.js */ \"./src/material-mod/ElasticConstDetails.view.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\n\n// Store the default marked tree leafs\nlet markedTreeLeafs = { eStruct: null, thermalProps: null };\n\nclass MaterialMod {\n\n constructor(){\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'material-module');\n this.missingPrompt = document.createElement('div');\n this.missingPrompt.id = \"missing-prompt\";\n this.element.appendChild(this.missingPrompt)\n\n this.overview = new Overview();\n this.overview.attachAndSetEvents(this.element);\n\n this.structureViewer = new StructureViewerWrapper(this.overview.vizBox);\n\n this.structureDetails = new StructureDetails();\n this.structureDetails.attachAndSetEvents(this.element);\n\n this.electronicStructDetails = new ElectronicStructDetails();\n this.electronicStructDetails.attachAndSetEvents(this.element);\n\n this.methodologyDetails = new MethodologyDetails();\n this.methodologyDetails.attachAndSetEvents(this.element);\n\n this.thermalDetails = new ThermalPropsDetails();\n this.thermalDetails.attachAndSetEvents(this.element);\n\n this.elasticDetails = new ElasticConstDetails();\n this.elasticDetails.attachAndSetEvents(this.element);\n\n this.currentDetailView = null;\n }\n\n setMaterialView(data) {\n this._loadMaterial(data.material_id, data.view);\n }\n\n getCurrentPageStatus(){\n\n let tempeStructCalcs = null;\n if (this.currentDetailView === null){\n tempeStructCalcs = this.overview.getEStructChosenCalcs();\n }\n\n return {\n pageId: this.currentDetailViewId,\n eStructCalcs: tempeStructCalcs\n };\n }\n\n _setView(view) {\n // Hide the current view\n if (this.currentDetailView === null) {\n this.overview.element.style.display = 'none';\n } else {\n this.currentDetailView.element.style.display = 'none';\n }\n\n if (typeof view === 'undefined') { // Overview view\n this.currentDetailView = null;\n this.currentDetailViewId = null;\n this.overview.setVisible();\n } else {\n this.currentDetailViewId = view;\n this._setDetailView(util.MAT_VIEW[view]);\n }\n }\n\n _setCellViewer(hostElement) {\n this.structureViewer.changeHostElement(hostElement);\n }\n\n _setDetailView(view) {\n\n // Set active view\n if (view === util.MAT_VIEW.structure){\n this.currentDetailView = this.structureDetails;\n } else if (view === util.MAT_VIEW.electronicstruct) {\n this.currentDetailView = this.electronicStructDetails;\n } else if (view === util.MAT_VIEW.methodology) {\n this.currentDetailView = this.methodologyDetails;\n } else if (view === util.MAT_VIEW.thermalprops) {\n this.currentDetailView = this.thermalDetails;\n } /* To be implemented\n else{ // Elastic constants\n this.currentDetailView = this.elasticDetails;\n this.navTree.showCalcsGraphDataAvalability(false);\n this.navTree.setHeight(600);\n this.navTree.setMarkedLeafIfNoneMarked(null);\n }*/\n \n // Show the view\n this.currentDetailView.setVisible();\n }\n\n /**\n * Called upon loading the overview page for a specific material.\n */\n _loadMaterial(matId, view) {\n let show = () => {\n // Show error message if resource not found\n const missingPrompt = document.getElementById(\"missing-prompt\");\n if (DataStore.getMissing()) {\n // Hide the current view\n if (this.currentDetailView === null) {\n this.overview.element.style.display = 'none';\n } else {\n this.currentDetailView.element.style.display = 'none';\n }\n const msg = `\n Could not find information for material with identifier ${matId}.\n Either the material does not exist or is not yet visible in the Encyclopedia.\n `;\n missingPrompt.textContent = msg;\n missingPrompt.style.display = \"block\";\n } else {\n missingPrompt.style.display = \"none\";\n this._setView(view);\n let materialData = DataStore.getMaterialData();\n let idealizedStructure = DataStore.getIdealizedStructure();\n // Cell viewer needs to be set only after page is visible so that it is\n // resized correctly.\n if (this.currentDetailView !== null) {\n this.currentDetailView.load();\n if (view === util.MAT_VIEW.structure) {\n this._setCellViewer(this.structureDetails.vizBox);\n }\n if (view === util.MAT_VIEW.methodology) {\n this.methodologyDetails.updateSelection();\n }\n } else {\n document.querySelector('title').innerHTML =\n 'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false);\n this.overview.setMaterialData();\n let name = (materialData.material_name === null ? materialData.formula : materialData.material_name);\n this.overview.setCalcsData(markedTreeLeafs);\n this._setCellViewer(this.overview.vizBox);\n }\n }\n };\n let isReady = () => {\n let ready = DataStore.isReady(matId);\n if (ready) {\n show();\n }\n return ready;\n };\n\n // If material is already loaded, nothing fetched.\n if (!isReady()) {\n DataStore.clear();\n this.overview.clearCalcsData();\n LoadingPopup.reset();\n this.structureViewer.axisCheckbox.checked = true;\n this.structureViewer.bondsCheckbox.checked = true;\n document.getElementById('methodology-ov').style.visibility = 'hidden';\n document.getElementById('structure-ov').style.visibility = 'hidden';\n document.getElementById('e-structure-ov').style.display = 'none';\n document.getElementById('e-structure-ov').style.visibility = 'hidden';\n document.getElementById('thermal-props-ov').style.visibility = 'hidden';\n\n // Request basic material data\n LoadingPopup.show(\"load_basic\");\n util.serverReq(util.getMaterialURL(matId), e1 => {\n // Check for error\n let stat = e1.target.status;\n if (stat >= 400 && stat < 500) {\n DataStore.setMissing(true);\n DataStore.setMaterialData({material_id: matId});\n } else {\n let materialData = JSON.parse(e1.target.response);\n DataStore.setMaterialData(materialData);\n util.materialId = materialData.material_id;\n }\n isReady();\n LoadingPopup.hide(\"load_basic\");\n });\n\n // Request basic details for all calculations related to this material\n LoadingPopup.show(\"load_calculations\");\n util.serverReq(util.getMaterialXsURL('calculations', matId), e4 => {\n // Check for error\n let stat = e4.target.status;\n if (stat >= 400 && stat < 500) {\n DataStore.setMissing(true);\n } else {\n let calculations = JSON.parse(e4.target.response);\n let representatives = calculations.representatives;\n let idealId = representatives.idealized_structure;\n DataStore.setCalculations(calculations);\n\n // Get the idealized structure\n //util.serverReq(util.getMaterialXsURL('idealized_structure', matId), e2 => {\n let query = JSON.stringify({properties: [\"idealized_structure\"]});\n LoadingPopup.show(\"load_idealized\");\n util.serverReqPOST(util.getMaterialCalcURL(matId, idealId), query, e2 => {\n // Check for error\n let stat = e2.target.status;\n if (stat >= 400 && stat < 500) {\n DataStore.setMissing(true);\n } else {\n let struct = JSON.parse(e2.target.response).idealized_structure;\n DataStore.setIdealizedStructure(struct);\n this.structureViewer.load(struct);\n document.getElementById('structure-ov').style.visibility = 'visible';\n document.getElementById('methodology-ov').style.visibility = 'visible';\n }\n isReady();\n LoadingPopup.hide(\"load_idealized\");\n });\n }\n LoadingPopup.hide(\"load_calculations\");\n });\n\n // Request groups\n LoadingPopup.show(\"load_groups\");\n util.serverReq(util.getMaterialXsURL('groups', matId), e5 => {\n // Check for error\n let stat = e5.target.status;\n if (stat >= 400 && stat < 500) {\n DataStore.setMissing(true);\n } else {\n let groups = JSON.parse(e5.target.response);\n DataStore.setGroups(groups);\n }\n isReady();\n LoadingPopup.hide(\"load_groups\");\n });\n }\n\n };\n} // class MaterialMod\n\n\n// Wrapper the structure viewer to be properly integrated\n// on the UI components showing it\nclass StructureViewerWrapper {\n\n constructor(hostElement){\n this.hostElement = hostElement;\n\n let options = {\n view: {\n autoResize: false,\n autoFit: true,\n fitMargin: 0.5,\n },\n structure: {\n createLegend: false,\n showLegend: false,\n radiusScale: 0.7,\n bondScale: 1.5,\n showCopies: true,\n viewCenter: \"COP\",\n }\n };\n this.viewer = new matviewer.StructureViewer(hostElement, options);\n\n this.legendElement = document.createElement('div');\n this.legendElement.setAttribute('class', 'element-labels');\n this.legendElement.setAttribute('style', 'position: absolute; bottom: 50px; right: 0');\n this.hostElement.appendChild(this.legendElement);\n\n this.footerElement = document.createElement('div');\n this.footerElement.setAttribute('class', 'structure-viewer-legend');\n this.hostElement.appendChild(this.footerElement);\n this.footerElement.innerHTML = `\n\n <div style=\"float: left; padding-right: 12px\" >\n <input type=\"checkbox\" class=\"show-axis\" checked> Show axis\n </div>\n\n <div style=\"float: left; padding-right: 18px\" >\n <input type=\"checkbox\" class=\"show-bonds\" checked> Show bonds\n </div>\n\n <div style=\"float: left; position:relative;\" >\n <img class=\"view-reset\" style=\"cursor: pointer;\" height=\"18px\"\n src=\"${util.IMAGE_DIR}reset.svg\" />\n <div class=\"view-reset-tooltip\" > Reset view </div>\n </div>\n\n <div class=\"vr-download\" style=\"float: right\"> </div>\n\n <div style=\"clear: both;\"></div>\n `;\n\n this.axisCheckbox = this.footerElement.querySelector('.show-axis');\n this.axisCheckbox.addEventListener('click', e => {\n this.viewer.toggleLatticeParameters(this.axisCheckbox.checked);\n });\n\n this.bondsCheckbox = this.footerElement.querySelector('.show-bonds');\n this.bondsCheckbox.addEventListener('click', e => {\n this.viewer.toggleBonds(this.bondsCheckbox.checked);\n });\n\n this.labelsContainer = this.hostElement.querySelector('.element-labels');\n\n //this.vrLinksContainer = this.footerElement.querySelector('.vr-download');\n //this.vrDropDown = new DropDown();\n //this.vrLinksContainer.appendChild(this.vrDropDown.element);\n\n let resetButton = this.hostElement.querySelector('.view-reset');\n resetButton.addEventListener('click', e => this.viewer.controls.reset() );\n\n resetButton.addEventListener('mouseover', e => {\n this.hostElement.querySelector('.view-reset-tooltip').style.display = 'block';\n });\n resetButton.addEventListener('mouseout', e => {\n this.hostElement.querySelector('.view-reset-tooltip').style.display = 'none';\n });\n }\n\n load(data) {\n data = this.getCellDataForViewer(data);\n this.viewer.load(data);\n this.createElementLegend();\n }\n\n resizeCanvasToHostElement() {\n this.viewer.resizeCanvasToHostElement();\n }\n\n setMaterialId(id){\n //this.vrDropDown.setMaterialId(id);\n }\n\n changeHostElement(hostElement) {\n if (this.hostElement !== hostElement){\n this.hostElement.removeChild(this.legendElement);\n this.hostElement.removeChild(this.footerElement);\n this.hostElement = hostElement;\n this.viewer.changeHostElement(hostElement);\n this.hostElement.appendChild(this.legendElement);\n this.hostElement.appendChild(this.footerElement);\n }\n this.viewer.resizeCanvasToHostElement();\n this.viewer.fitToCanvas();\n this.viewer.render();\n }\n\n getCellDataForViewer(struct){\n let cellData = {};\n cellData.pbc = struct.periodicity;\n cellData.chemicalSymbols = struct.atom_labels;\n\n // Convert to angstrom\n cellData.cell = util.convert2d(struct.lattice_vectors, 1e10);\n cellData.scaledPositions = struct.atom_positions;\n\n return cellData;\n }\n\n\n createElementLegend() {\n // Empty the old legend\n this.labelsContainer.innerHTML = '';\n\n // Create a list of elements\n let elements = this.viewer.elements;\n let elementArray = [];\n for (let property in elements) {\n if (elements.hasOwnProperty(property))\n elementArray.push([property, elements[property][0], elements[property][1]]);\n }\n\n // Sort by name\n elementArray.sort(function (a, b) {\n if (a[0] < b[0]) return -1;\n if (a[0] > b[0]) return 1;\n return 0;\n });\n\n let svgElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svgElement.setAttribute(\"width\", 50);\n svgElement.setAttribute(\"height\", elementArray.length*25);\n\n this.labelsContainer.appendChild(svgElement);\n\n for (let i = 0; i < elementArray.length; ++i) {\n let elementName = elementArray[i][0];\n let elementColor = elementArray[i][1].toString(16);\n let nZeros = 6 - elementColor.length;\n let prefix = \"#\" + Array(nZeros + 1).join(\"0\");\n elementColor = prefix + elementColor;\n svg.addCircle(svgElement, 10, 25*i+12, 8, elementColor, \"#777\", 1);\n svg.addText(svgElement, 24, 25*i+18, elementName, 'start', 'structure-viewer-legend-labels');\n }\n }\n} // class StructureViewerWrapper\n\n\n// used exclusively in StructureViewerWrapper\nclass DropDown {\n\n constructor(materialId){\n this.folded = true;\n this.element = document.createElement('div');\n //this.element.className = className;\n\n this.element.innerHTML+=`\n <div >\n <span style=\" vertical-align: 30%; \">Virtual Reality files</span>\n <img style=\"cursor: pointer\" src=\"${util.IMAGE_DIR}folded.png\" />\n </div>\n\n <div class=\"vr-download-panel\" style=\"position: relative; display: none\">\n\n </div>\n `;\n\n // Focus related properties (in order to hide the box when the user click out)\n this.element.tabIndex = '0'; // enabled the support of focusing\n this.element.style.outline = 'none'; // The outline is not shown when it gains the focus\n\n this.foldingPanel = this.element.querySelector('.vr-download-panel');\n this.foldBtn = this.element.querySelector('img');\n\n this.foldBtn.addEventListener('click', e => {\n this.folded = !this.folded;\n this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' :\n util.IMAGE_DIR+'unfolded.png');\n //this.foldBtn.className = (this.folded ? 'on' : 'off');\n this.foldingPanel.style.display = (this.folded ? 'none' : 'block');\n });\n\n this.element.addEventListener('blur' , e => {\n setTimeout(() => {\n this.folded = true;\n this.foldBtn.src = util.IMAGE_DIR+'folded.png';\n this.foldingPanel.style.display = 'none';\n }, 300);\n });\n\n //this.cellViewer.toggleLatticeParameters(false);\n }\n\n setMaterialId(id){\n this.foldingPanel.innerHTML = `\n <div class=\"vr-download-panel-unfolded\" style=\"width: 210px;\">\n <div style=\"padding: 5px; \">\n <a href=\"http://nomad.srv.lrz.de/cgi-bin/NOMAD/material?${id}\">Get VR file</a>\n </div>\n <br>\n <div style=\"padding-bottom: 5px; \">Visualization tools for specific devices:</div>\n\n <div style=\"padding: 5px; \">\n <a href=\"http://nomad.srv.lrz.de/NOMAD/NOMADViveT-Setup.exe\">HTC Vive</a>\n </div>\n <!--\n <div style=\"padding: 5px; \">\n <a href=\"http://nomad.srv.lrz.de/NOMAD/NOMADGearvrT.apk\">Samsung GearVR</a>\n </div>\n -->\n <div style=\"padding: 5px; \">\n <a target=\"_blank\" href=\"https://play.google.com/store/apps/details?id=com.lrz.nomadvr\">Google Cardboard</a>\n </div>\n\n </div>\n `;\n }\n\n} // class DropDown\n\n\n// EXPORTS\nmodule.exports = MaterialMod;\n\n\n//# sourceURL=webpack:///./src/material-mod/MaterialMod.js?"); + +/***/ }), + +/***/ "./src/material-mod/MethodologyDetails.view.js": +/*!*****************************************************!*\ + !*** ./src/material-mod/MethodologyDetails.view.js ***! + \*****************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n 'Details' view container that shows a table with the methodology info used\n to get the material calculations.\n\n In the file there is a defined (class) component used as a filter widget for\n the table: FilterInGroupsComponent.\n */\n\n\n\nlet DetailsViewBase = __webpack_require__(/*! ./DetailsViewBase.js */ \"./src/material-mod/DetailsViewBase.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet SortingButton = __webpack_require__(/*! ./SortingButton.js */ \"./src/material-mod/SortingButton.js\");\n\nvar getUrl = window.location;\nvar guiRoot = getUrl.protocol + \"//\" + getUrl.host + \"/\" + getUrl.pathname;\nlet REPOSITORY_LINK = guiRoot.slice(0, guiRoot.lastIndexOf('/', guiRoot.lastIndexOf('/')-1))+\"/gui/entry/id\"\n\nconst calcTypesMap = new Map([\n ['single point', 'Single point'],\n ['GW calculation', 'GW'],\n ['geometry optimization', 'Geometry optimization'],\n ['molecular dynamics', 'Molecular dynamics'],\n ['phonon calculation', 'Phonon'],\n ['equation of state', 'Equation of state'],\n ['parameter variation', 'Parameter variation'],//['Convergence', 'convergence'],\n ['QHA calculation', 'QHA']\n]);\n\n\n\nclass FilterInGroupsComponent {\n\n constructor(className) {\n this.element = document.createElement('div');\n this.element.className = className;\n this.filtersOn = [];\n this.folded = true;\n this.element.innerHTML+=`\n <div style=\"padding: 0rem 2.5rem\">\n <div style=\"display: flex; justify-content: flex-end;\">\n <div class=\"filter-groups-c-folded\" >\n <span style=\"vertical-align: top;\">Filtering </span>\n </div>\n <div class=\"filter-c-btn\" >\n <img src=\"${util.IMAGE_DIR}folded.png\" />\n <!--<button class=\"on\">filter</button> -->\n </div>\n </div>\n <div class=\"filter-groups-c-unfolded\" style=\"display: none; padding-bottom: 1rem\">\n <table style=\"width: 100%;\">\n <thead>\n <tr>\n <th style=\"width: 20%;\">\n <span>Type</span>\n </th>\n <th style=\"width: 20%;\">\n <span info-sys-data=\"functional-type\">Density functional</span>\n </th>\n <th style=\"width: 20%;\">\n <span info-sys-data=\"code-name\">Code</span>\n </th>\n <th style=\"width: 20%;\">\n <span info-sys-data=\"pseudopotential-type\">Potential</span>\n </th>\n <th style=\"width: 20%;\">\n <span info-sys-data=\"basis-set-type\">Basis set</span>\n </th>\n </tr>\n </thead>\n <tbody>\n <tr id=\"filter-items-row\"></tr>\n </tbody>\n </table>\n </div>\n </div>\n `;\n this.unfoldedPanel = this.element.querySelector('.filter-groups-c-unfolded');\n this.filterItemsRow = this.element.querySelector('#filter-items-row');\n\n this.foldBtn = this.element.querySelector('img');\n\n this.foldBtn.addEventListener('click', e => {\n this.folded = !this.folded;\n this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' :\n util.IMAGE_DIR+'unfolded.png');\n this.unfoldedPanel.style.display = (this.folded ? 'none' : 'block');\n });\n\n // Add listener for checkboxes events\n this.element.addEventListener('click', (e) => {\n\n if (e.target.tagName === 'INPUT'){\n let index = this.filtersOn.indexOf(e.target.value);\n if (index >= 0) this.filtersOn.splice( index, 1 );\n else this.filtersOn.push(e.target.value);\n this.itemListener(this.filtersOn);\n //console.log('this.filtersOn',this.filtersOn);\n }\n });\n }\n\n\n addGroupsItems(calcs){\n let lCalcTypesMap = new Map();\n let lDensityFunctionalMap = new Map();\n let lCodeMap = new Map();\n let lPotentialMap = new Map();\n let lBasicSetMap = new Map();\n calcs.forEach( c => {\n if (!lCalcTypesMap.has(c.type))\n lCalcTypesMap.set(c.type, calcTypesMap.get(c.type));\n if (!lDensityFunctionalMap.has(c.functional_type))\n lDensityFunctionalMap.set(c.functional_type, c.functional_type);\n if (!lCodeMap.has(c.code))\n lCodeMap.set(c.code, c.code);\n if (!lPotentialMap.has(c.core_electron_treatment))\n lPotentialMap.set(c.core_electron_treatment, c.core_electron_treatment);\n if (!lBasicSetMap.has(c.basis_set_type))\n lBasicSetMap.set(c.basis_set_type, c.basis_set_type);\n });\n this.filterItemsRow.innerHTML = '';\n this.filtersOn = [];\n this.addGroupItems(lCalcTypesMap);\n this.addGroupItems(lDensityFunctionalMap);\n this.addGroupItems(lCodeMap);\n this.addGroupItems(lPotentialMap);\n this.addGroupItems(lBasicSetMap);\n //this.filterItemsRow.innerHTML += '<td></td>'; // link column\n }\n\n\n addGroupItems(groupItemsMap){\n let html = '<td> ';\n groupItemsMap.forEach( (itemName, itemId) => {\n this.filtersOn.push(itemId);\n html += '<input type=\"checkbox\" value=\"'+itemId+'\" checked>'+\n '<span style=\"vertical-align: 20%\">'+itemName+'</span> <br> ';\n });\n this.filterItemsRow.innerHTML += html+ '</td>';\n }\n\n\n setItemListener(listener){\n this.itemListener = listener;\n }\n}\n\n\n\nclass MethodologyDetails extends DetailsViewBase {\n\n constructor() {\n super('Methodology');\n\n this.sortedCalcs = [];\n this.markedCalc = null;\n\n this.element.innerHTML+=`\n\n <div>\n <div class=\"view-box\">\n <div class=\"title\">Methodology</div>\n\n <div class=\"filter-placeholder\"></div>\n\n <div class=\"dataTableWrapper\"></div>\n </div>\n </div>\n `;\n\n // There is no this.navTreeWrapper = this.element.querySelector('.navTreeWrapper');\n\n this.dataTableWrapper =\n this.element.querySelector('.dataTableWrapper');\n\n this.dataTableWrapper.innerHTML+=`\n <table id=\"methodology-data\">\n <thead>\n <tr>\n <th style=\"width: 12%;\">\n <span>Calculation ID</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 16%;\">\n <span>Type</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 18%;\">\n <span info-sys-data=\"functional-type\">Density functional</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 12%;\">\n <span info-sys-data=\"code-name\">Code</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 16%;\">\n <span info-sys-data=\"pseudopotential-type\">Potential</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 11%;\">\n <span info-sys-data=\"basis-set-type\">Basis set</span>\n <span class=\"sorting-button\"></span>\n </th>\n <th style=\"width: 4%;\">\n <span info-sys-data=\"basis-set-type\">Link</span>\n </th>\n </tr>\n </thead>\n <tbody>\n </tbody>\n </table>\n `;\n\n this.tbody = this.dataTableWrapper.querySelector(\"tbody\");\n this.moreInfoRow = document.createElement('tr'); //\n this.moreInfoRow.className= 'moreinfo';\n this.moreInfoCalcId = null;\n\n // Filtering feature initialization\n this.filterComponent = new FilterInGroupsComponent('meth-filter-component');\n this.element.querySelector('.filter-placeholder').\n appendChild(this.filterComponent.element);\n\n this.filterComponent.setItemListener( propsSel => {\n this.sortedCalcs.forEach( rowCalcData => { //leafId => {\n let calcProps = rowCalcData.dataCalcProps.split(',');\n let propsPresent = true;\n calcProps.forEach( e => {\n if (propsSel.indexOf(e) < 0) propsPresent = false;\n });\n if (propsPresent) rowCalcData.visible = true;\n else rowCalcData.visible = false;\n });\n this._render();\n });\n\n // Row sorting feature initialization\n this.sortingButtonWrappers =\n this.dataTableWrapper.querySelectorAll('.sorting-button');\n\n this.sortingButtons = [];\n\n let sortingButtonsMap = new Map([\n [ 'calc_id', undefined ],\n [ 'type', undefined ],\n [ 'functional_type', undefined ],\n [ 'code', undefined ],\n [ 'core_electron_treatment', undefined ],\n [ 'basis_set_type', undefined ] ]);\n\n let keysIter = sortingButtonsMap.keys();\n this.sortingButtonWrappers.forEach( e => {\n let field = keysIter.next().value;\n let component = new SortingButton(field);\n e.appendChild(component.element);\n this.sortingButtons.push(component);\n\n component.setListener( (descendingOrder, field) => {\n this.sortingButtons.forEach( el => {\n if (el !== component) el.init();\n });\n this._sortRowsCalcDataBy(descendingOrder, field);\n this._render();\n });\n });\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n\n //this._events();\n }\n\n/*\n _events() {\n //super._events();\n\n\n this.dataTableWrapper.addEventListener('click', (e) => {\n\n let rowElement = e.target.parentElement;\n if (rowElement.className.indexOf('data-row') < 0)\n rowElement = rowElement.parentElement;\n\n if (rowElement.className.indexOf('data-row') >= 0){\n let id= rowElement.getAttribute('data-calc-id');\n\n if (this.moreInfoCalcId !== null){ // If more-info panel unfolded\n this.moreInfoRow.parentElement.removeChild(this.moreInfoRow);\n if (this.moreInfoCalcId === id){\n this.moreInfoCalcId = null;\n return;\n }\n }\n this.moreInfoCalcId = id;\n let moreInfoCalc, calcGroupType;\n this.sortedCalcs.forEach( leafId => { //console.log(leafId, id);\n if (leafId === id){\n moreInfoCalc = DataStore.getCalc(DataStore.getCalcReprIntId(leafId));\n calcGroupType = DataStore.getGroupType(leafId);\n\n }\n });\n this.moreInfoRow.innerHTML= ' <td></td><td colspan=\"7\"> '+\n getHTMLCalcType(calcGroupType, moreInfoCalc)+' </td>';\n rowElement.parentElement.insertBefore(this.moreInfoRow, rowElement.nextElementSibling);\n }\n\n });\n\n\n\n function getHTMLCalcType(calcGroupType, calc){\n\n let result = '';\n let calcType = calcGroupType;\n if (calcGroupType === null ) calcType = calc.run_type;\n //console.log('getHTMLCalcType',calcType);\n switch (calcType){\n\n case calcTypesMap.get('Single point'):\n\n case calcTypesMap.get('Geometry optimization'):\n //console.log(calc.pseudopotential_type, calc.scf_threshold, calc.basis_set_short_name);\n result = getValueHTML('pseudopotential type',calc.pseudopotential_type) +\n getValueHTML('scf threshold',calc.scf_threshold) +\n getValueHTML('basis set short name',calc.basis_set_short_name) +\n getSmearingHTML(calc.smearing);\n break;\n\n case calcTypesMap.get('GW'):\n result = getValueHTML('gw starting point',calc.gw_starting_point) +\n getValueHTML('gw type',calc.gw_type);\n break;\n\n case calcTypesMap.get('Equation of state'):\n result = getValueHTML('pseudopotential type',calc.pseudopotential_type) +\n getValueHTML('basis set short name',calc.basis_set_short_name) +\n getValueHTML('scf threshold',calc.scf_threshold) +\n getSmearingHTML(calc.smearing) +\n getValueHTML('k point grid description',calc.k_point_grid_description);\n break;\n\n case calcTypesMap.get('Parameter variation'):\n result = getValueHTML('pseudopotential type',calc.pseudopotential_type) +\n getValueHTML('scf threshold',calc.scf_threshold);\n break;\n\n case calcTypesMap.get('Phonon'):\n result = getValueHTML('type', 'finite differences');\n break;\n\n }\n if (result.trim() === '') return 'NO ADDITIONAL DATA';\n else return result;\n }\n\n }\n*/\n updateSelection(leafIds) {\n this.sortedCalcs = [];\n\n DataStore.getCalculations().forEach( c => {\n\n let calcType = c.run_type;\n if (DataStore.getGroups().has(c.calc_id)){\n calcType = DataStore.getGroupType(c.calc_id);\n }\n\n this.sortedCalcs.push({\n calc_id: c.calc_id,\n type: calcType,\n functional_type: c.functional_type,\n code: c.code_name,\n core_electron_treatment: c.core_electron_treatment,\n basis_set_type: c.basis_set_type,\n dataCalcProps: calcType+','+c.functional_type+','+c.code_name+\n ','+c.core_electron_treatment+','+c.basis_set_type,\n visible: true\n });\n });\n\n this.filterComponent.addGroupsItems(this.sortedCalcs);\n\n this._sortRowsCalcDataBy(true, 'calc_id');\n\n this._render();\n }\n\n\n _sortRowsCalcDataBy(descendingOrder, field){\n\n this.sortedCalcs.sort( (a, b) => {\n let order = descendingOrder ? -1 : 1;\n return a[field] < b[field] ? order : -order;\n });\n }\n\n updateMarkedLeaf(leafId){ }\n\n _render() {\n\n let html = '';\n this.sortedCalcs.forEach( rowCalcData => { //leafId => {\n if (rowCalcData.visible) html+= getRowHtml(rowCalcData);\n });\n this.tbody.innerHTML = html;\n\n InfoSys.addToInfoSystem(this.tbody);\n\n function getRowHtml(rowCalcData/*leafId, calc, calcType*/) {\n\n let calc = DataStore.getCalc( /*DataStore.getCalcReprIntId(*/rowCalcData.calc_id);\n let calcType = rowCalcData.type;\n\n let repositoryLinkHtml =\n `<a href=\"${REPOSITORY_LINK}/${calc.upload_id}/${calc.calc_id}\" target=\"blank\">`\n +'<img src=\"img/download.svg\" height=\"20px\" /> </a>';\n\n let materialId = DataStore.getMaterialData().material_id;\n let calcId = calc.calc_id;\n let vrLinkHtml = `<a href=\"https://labdev-nomad.esc.rzg.mpg.de/remotevis/cM/start/${materialId}+${calcId}\" `\n + 'target=\"blank\"><img src=\"img/monitor_icon.png\" height=\"20px\" /> </a>';\n\n return `\n <tr data-calc-id=\"${calc.calc_id}\" class=\"data-row\">\n <td>${util.getShortCode(calc.calc_id)}</td>\n <td>\n <span info-sys-data=\"calculation-type.value:${calcType}\">\n ${calcType}</span>\n </td>\n <td>\n <span info-sys-data=\"functional-type.value:${rowCalcData.functional_type}\">\n ${rowCalcData.functional_type}</span>\n ${getOptValue(calc.functional_long_name)}\n </td>\n <td>\n <span info-sys-data=\"code-name.value:${calc.code_name}\">\n ${calc.code_name}</span>\n ${getOptValue(calc.code_version)}\n </td>\n\n <td>\n <span info-sys-data=\"core-electron-treatment.value:${rowCalcData.core_electron_treatment}\">\n ${rowCalcData.core_electron_treatment}</span>\n </td>\n <td>\n <span info-sys-data=\"basis-set-type.value:${calc.basis_set_type}\">\n ${calc.basis_set_type}</span>\n ${getOptValue(calc.basis_set_quality_quantifier)}\n </td>\n\n <td style=\"padding-top: 8px;padding-bottom: 4px;\">${repositoryLinkHtml}\n </td>\n\n <!--\n <td style=\"padding-top: 8px;padding-bottom: 4px;\">${vrLinkHtml}\n </td>-->\n\n </tr>`;\n }\n }\n\n}\n\nfunction getValueHTML(text,value){\n if (value === undefined || value === null) return '';\n else return '<b>'+text+'</b>: '+value+'<br>';\n}\n\nfunction getSmearingHTML(value){\n let values = value.substring(1,value.length-1).split(',');\n return (values[0] === 'none' ? '' : '<b>smearing kind</b>: '+values[0]+' , ')+\n (values[1] === '0' ? '' : '<b>smearing width</b>: '+values[1]);\n}\n\nfunction getOptValue(value){\n if (value === undefined || value === null) return '';\n else return '('+value+')';\n}\n\n// EXPORTS\nmodule.exports = MethodologyDetails;\n\n\n//# sourceURL=webpack:///./src/material-mod/MethodologyDetails.view.js?"); + +/***/ }), + +/***/ "./src/material-mod/NavTree.js": +/*!*************************************!*\ + !*** ./src/material-mod/NavTree.js ***! + \*************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n The NavTree class define the UI tree component showing the material calculations.\n There is only one instance that is shared by several 'Details' views.\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n\n\nfunction buildCalcTree(calcs, calcsInGroups, view) {\n\n // Add groups\n let calcTree = new Map();\n function addGroupToTree(groups, prefix) {\n groups.forEach((calculations, groupId) => {\n let representative = DataStore.getCalc(calculations[0]);\n let codeNameTrimed = representative.code_name.trim();\n let functionalType = representative.functional_type;\n\n // Grouping enabled only for exciting\n if (codeNameTrimed === 'exciting') {\n if (calcTree.has(functionalType)){\n let functionalMap= calcTree.get(functionalType);\n\n if (functionalMap.has(codeNameTrimed)) {\n // Get an array and push a new calc\n functionalMap.get(codeNameTrimed).push(prefix+groupId);\n } else // New code name\n functionalMap.set(codeNameTrimed, [prefix+groupId]);\n\n } else { // New functional, not build if value is unavailable\n if (functionalType !== \"unavailable\") {\n let newFunctionalMap = new Map();\n newFunctionalMap.set(codeNameTrimed, [prefix+groupId]);\n calcTree.set(functionalType, newFunctionalMap);\n }\n }\n }\n });\n }\n if (view === \"structure\") {\n addGroupToTree(calcsInGroups.get(\"eos\"), \"eos/\");\n addGroupToTree(calcsInGroups.get(\"par\"), \"par/\");\n }\n\n // Add individual calculations to tree if they are not already in a group.\n function addToCalcTree(calc, id) {\n let codeNameTrimed= calc.code_name.trim();\n let functionalType = calc.functional_type;\n if (calcTree.has(functionalType)){\n let functionalMap= calcTree.get(functionalType);\n\n if (functionalMap.has(codeNameTrimed)){\n // Get an array and push a new calc\n functionalMap.get(codeNameTrimed).push(id);\n } else // New code name\n functionalMap.set(codeNameTrimed, [id]);\n\n } else { // New functional\n let newFunctionalMap= new Map();\n newFunctionalMap.set(codeNameTrimed, [id]);\n calcTree.set(functionalType, newFunctionalMap);\n }\n }\n calcs.forEach(calc => {\n if (!DataStore.isInAnyNotDisabledGroup(calc.calc_id)) {\n if (view === \"electronic\") {\n if (calc.has_dos || calc.has_band_structure) {\n addToCalcTree(calc, calc.calc_id);\n }\n } else if (view === \"thermal\") {\n if (calc.has_thermal_properties) {\n addToCalcTree(calc, calc.calc_id);\n }\n } else {\n addToCalcTree(calc, calc.calc_id);\n }\n }\n });\n return calcTree;\n}\n\n\n/* Maybe these functions (getNextNode, getPreviousNode) can be removed\nsearching by node class calc-l and node-selected. Think about this */\n\nfunction getNextNode(nodeBox){\n let nextCalcNodeBox= nodeBox.nextElementSibling.nextElementSibling;\n if (nextCalcNodeBox === null){\n\n if (nodeBox.parentElement.nextElementSibling !== null)\n nextCalcNodeBox= nodeBox.parentElement.nextElementSibling/*nextCodeNodeBox*/\n .nextElementSibling/*nextCodeBox*/.children[0]/*nextCalc*/;\n\n else if (nodeBox.parentElement.parentElement.nextElementSibling !== null)\n nextCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/\n .nextElementSibling.nextElementSibling/*nextFunctionalBox*/\n .children[1]/*nextCodeBox*/.children[0]/*nextCalc*/;\n else //reaching the final node\n nextCalcNodeBox= null;\n }\n return nextCalcNodeBox;\n}\n\n\nfunction getPreviousNode(nodeBox){\n let prevCalcNodeBox;\n if (nodeBox.previousElementSibling !== null)\n prevCalcNodeBox= nodeBox.previousElementSibling.previousElementSibling;\n else{\n\n if (nodeBox.parentElement.previousElementSibling.previousElementSibling !== null)\n prevCalcNodeBox= nodeBox.parentElement.previousElementSibling/*prevCodeNodeBox*/\n .previousElementSibling/*prevCodeBox*/.children[0]/*prevCalc*/;\n else if (nodeBox.parentElement.parentElement.previousElementSibling.previousElementSibling !== null)\n prevCalcNodeBox= nodeBox.parentElement.parentElement/*FunctionalBox*/\n .previousElementSibling.previousElementSibling/*prevFunctionalBox*/\n .lastElementChild/*prevCodeBox*/.lastElementChild.previousElementSibling/*prevCalc*/;\n else //reaching the final node\n prevCalcNodeBox= null;\n }\n return prevCalcNodeBox;\n}\n\n\n\nclass NavTree {\n\n constructor() {\n this.selectedCalcs= new Set();\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','navigation-tree');\n this.parentElement= null;\n this.markedNode = null;\n this._events();\n }\n\n // detach if necessary and attach\n attach(element){\n if (this.parentElement !== null)\n this.parentElement.removeChild(this.element);\n this.parentElement= element;\n this.parentElement.appendChild(this.element);\n }\n\n /**\n * Used to construct the navigation tree. Use the view parameter to define\n * the type of view. Options are:\n *\n * - view=\"structure\": Shows all calculations without tooltips. Calculations\n * are grouped if groups are available.\n * - view=\"electronic\": Shows only calculations with electronic dos or band\n * structure. Grouping is disabled.\n * - view=\"thermal\": Shows only calculations with thermal properties.\n * Grouping is disabled.\n */\n build (materialName, view) {\n // Reset\n this.selectedCalcs.clear();\n this.markedNode = null;\n this.element.innerHTML= '';\n this.calcsInGroups = DataStore.getGroups();\n\n //function getNodeHTML(level, label, unfolded, counter = 0){\n //let foldingValue= 'node-'+(unfolded ? 'unfolded' : 'folded');\n //let counterTag= (counter === 0 ? '' :\n //'<span class=\"node-counter\">('+counter+')</span>');\n //return `\n //<div class=\"${level}\">\n //<span class=\"${foldingValue}\"></span>\n //<span class=\"node-checkbox\"></span>\n //<span class=\"node-label\" >${label}</span>\n //${counterTag}\n //</div>\n //`;\n //}\n\n function getNodeHTML(level, label, unfolded, counter = 0) {\n let foldingValue= 'node-' + (unfolded ? 'unfolded' : 'folded');\n let container = document.createElement(\"div\");\n container.className = level;\n let span1 = document.createElement(\"span\");\n span1.className = foldingValue;\n container.appendChild(span1);\n let span2 = document.createElement(\"span\");\n span2.className = \"node-checkbox\";\n container.appendChild(span2);\n let span3 = document.createElement(\"span\");\n span3.className = \"node-label\";\n span3.textContent = label;\n container.appendChild(span3);\n if (counter !== 0) {\n let span0 = document.createElement(\"span\");\n span0.className = \"node-counter\";\n span0.textContent = \"(\" + counter + \")\";\n container.appendChild(span0);\n }\n\n return container;\n }\n\n function getCalcHTML(leafId, leafAbbr, calcNumber, isGroup, graphInfoAvailabilityHTML) {\n\n let container = document.createElement(\"div\");\n container.className = \"calc-l\";\n container.setAttribute(\"data-calc-id\", leafId);\n let span1 = document.createElement(\"span\");\n container.appendChild(span1);\n let span2 = document.createElement(\"span\");\n span2.className = \"node-checkbox\";\n container.appendChild(span2);\n let span3 = document.createElement(\"span\");\n span3.className = \"node-label\";\n if (isGroup) {\n span3.appendChild(calcIcon);\n }\n span3.textContent = leafAbbr + calcNumber + \"\\u00A0\";\n span3.appendChild(graphInfoAvailabilityHTML);\n container.appendChild(span3);\n let div1 = document.createElement(\"div\");\n div1.id = \"icon-container\";\n div1.style.float = \"right\";\n div1.style.padding = \"1px 10px 0 0\";\n div1.style.display = \"none\";\n let img = document.createElement(\"img\");\n img.setAttribute(\"src\", util.IMAGE_DIR + \"next.png\");\n div1.appendChild(img);\n container.appendChild(div1);\n\n return container;\n }\n\n function getCalcGraphInfoAvalabilityHTML(calc) {\n let fragment = document.createElement(\"div\");\n fragment.className = \"calc-graph-aval\";\n let container = document.createElement(\"span\");\n if (calc.has_band_structure) {\n let span1 = document.createElement(\"span\");\n span1.className = \"tooltip\";\n span1.textContent = \"B\";\n let span2 = document.createElement(\"span\");\n span2.className = \"tooltiptext\";\n span2.textContent = \"Band structure\";\n span1.appendChild(span2);\n container.appendChild(span1);\n }\n if (calc.has_dos) {\n let span1 = document.createElement(\"span\");\n span1.className = \"tooltip\";\n span1.textContent = \"D\";\n let span2 = document.createElement(\"span\");\n span2.className = \"tooltiptext\";\n span2.textContent = \"Density of states\";\n span1.appendChild(span2);\n container.appendChild(span1);\n }\n if (calc.has_fermi_surface) {\n let span1 = document.createElement(\"span\");\n span1.className = \"tooltip\";\n span1.textContent = \"F\";\n let span2 = document.createElement(\"span\");\n span2.className = \"tooltiptext\";\n span2.textContent = \"Fermi surface\";\n span1.appendChild(span2);\n container.appendChild(span1);\n }\n if (calc.has_thermal_properties) {\n let span1 = document.createElement(\"span\");\n span1.className = \"tooltip\";\n span1.textContent = \"T\";\n let span2 = document.createElement(\"span\");\n span2.className = \"tooltiptext\";\n span2.textContent = \"Phonons\";\n span1.appendChild(span2);\n container.appendChild(span1);\n }\n fragment.appendChild(container);\n return fragment;\n }\n\n // Init map to store calculations data\n let calcs = DataStore.getCalculations();\n let calcIcon = document.createElement(\"img\");\n calcIcon.className = \"folder-icon\";\n calcIcon.src = util.IMAGE_DIR+\"folder.png\";\n\n // For the structure view we group calculations\n let groups = null;\n let tree = buildCalcTree(calcs, this.calcsInGroups, view);\n let rootElement = document.createElement('div');\n rootElement.appendChild(getNodeHTML('material-l', materialName, true));\n\n let functionalLevelBox = document.createElement('div');\n rootElement.appendChild(functionalLevelBox);\n\n tree.forEach((codeMap, functionalName) => {\n functionalLevelBox.appendChild(getNodeHTML('functional-l', functionalName, true));\n\n let codeLevelBox= document.createElement('div');\n functionalLevelBox.appendChild(codeLevelBox);\n\n codeMap.forEach( (calcArray, codeName) => {\n codeLevelBox.appendChild(getNodeHTML('code-l', codeName, false, calcArray.length));\n\n let calcLevelBox= document.createElement('div');\n codeLevelBox.appendChild(calcLevelBox);\n calcLevelBox.style.display = 'none';\n\n for (var i = 0; i < calcArray.length; i++) {\n let leafId = calcArray[i];\n let calcNumber = '';\n let leafAbbr = util.getShortCode(leafId);\n let isGroup = DataStore.isGroup(leafId);\n if (isGroup) {\n let groupType = DataStore.getGroupType(leafId);\n let groupId = leafId.substring(4);\n let calculations;\n calculations = this.calcsInGroups.get(groupType).get(groupId);\n calcNumber = '('+calculations.length+')';\n } \n\n let calc_id = DataStore.getReprCalc(leafId);\n let calc = DataStore.getCalc(calc_id);\n let graphInfoAvalabilityHTML = getCalcGraphInfoAvalabilityHTML(calc);\n let calcDiv = getCalcHTML(leafId, leafAbbr, calcNumber, isGroup, graphInfoAvalabilityHTML);\n calcLevelBox.appendChild(calcDiv);\n calcLevelBox.appendChild(document.createElement(\"div\"));\n }\n });\n });\n this.element.appendChild(rootElement);\n this.showCalcsGraphDataAvailability(view !== \"structure\");\n } // build method\n\n selectAll(initMarkedLeafId){\n let materialNodeBox= this.element.children[0].children[0];\n this._recursiveNodeSelection(materialNodeBox, true);\n keepTreeIntegrity(materialNodeBox, true);\n }\n\n getMarkedLeaf(){\n if (this.markedNode === null) return null;\n else return this.markedNode.getAttribute('data-calc-id');\n }\n\n setMarkedLeafIfNoneMarked(leafId) { // If leafId === null first node selected\n if (this.getMarkedLeaf() === null) { // If none marked\n if (leafId === null) this._markFirstSelectedNode();\n else{\n let nodeBox = this.element.querySelector('div[data-calc-id=\"'+leafId+'\"]');\n this._setMarkedCalc(nodeBox);\n }\n }\n }\n\n _events() {\n this.element.addEventListener('click',(e) => {\n let classString = e.target.className;\n\n // drop down/up event\n if (classString.indexOf('folded') >= 0){\n this._foldTreeNode(e.target);\n\n // descendant selection/deselection event\n }else if ((classString.indexOf('node-checkbox') >= 0)){\n\n let selectMode= (e.target.parentElement.className.indexOf('selected') < 0);\n this._recursiveNodeSelection(e.target.parentElement, selectMode);\n keepTreeIntegrity(e.target.parentElement, selectMode);\n this.treeSelectionListener(this.selectedCalcs);\n this._keepCalcMarked(selectMode); //if (this.calcMarked)\n\n }else if (/*this.calcMarked && */(classString.indexOf('node-label') >= 0 )\n && (e.target.parentElement.className === 'calc-l node-selected')){\n this._setMarkedCalc(e.target.parentElement/*nodeBox*/);\n }\n });\n\n }\n\n _foldTreeNode(dropDowmElement){\n let siblingElement= dropDowmElement.parentElement.nextElementSibling;\n let classString = dropDowmElement.className;\n\n if (classString.indexOf('-folded') >= 0) {\n dropDowmElement.className= dropDowmElement.className.replace('folded','unfolded');\n siblingElement.style.display= 'block';\n } else {\n dropDowmElement.className= dropDowmElement.className.replace('unfolded','folded');\n siblingElement.style.display = 'none';\n }\n }\n\n _recursiveNodeSelection(nodeBox, select){\n\n let nodeCheckBox = nodeBox.children[1];\n if (select){\n nodeBox.className += ' node-selected';\n }else{ // deselect\n let index= nodeBox.className.indexOf(' node-selected');\n nodeBox.className= nodeBox.className.substring(0,index);\n }\n\n if (nodeBox.className.indexOf('calc-l') >= 0) { // leaf node\n let id= nodeBox.getAttribute('data-calc-id');//let id= parseInt(nodeBox.getAttribute('data-calc-id'));\n if (select) this.selectedCalcs.add(id);\n else this.selectedCalcs.delete(id);\n\n }else { // Not leaf node\n\n let nextLevelBox = nodeBox.nextElementSibling; // next levelBox\n // Two children per banch: the first one is the node label box\n // and the second one the next level with the descendants\n for (let i = 0; i < nextLevelBox.children.length; i++ ) {\n this._recursiveNodeSelection(nextLevelBox.children[i++], select);\n }\n }\n }\n\n setTreeSelectionListener(listener){\n this.treeSelectionListener= listener;\n }\n\n setLeafMarkedListener(listener){\n this.leafMarkedListener = listener;\n }\n\n getTreeSelectedCalcs(){\n return this.selectedCalcs;\n }\n\n _keepCalcMarked(select){\n\n if (select && (this.markedNode === null)){\n this._markFirstSelectedNode();\n\n }else if (!select){\n let id= parseInt(this.markedNode.getAttribute('data-calc-id'));\n if (this.selectedCalcs.size === 0){\n this.markedNode.className= this.markedNode.className.replace('-marked','');\n this.markedNode= null;\n this.leafMarkedListener(null);\n }else if (!this.selectedCalcs.has(id)){\n this._markFirstSelectedNode();\n }\n }\n } // _keepCalcMarked\n\n _markFirstSelectedNode(){\n let calcNodeBoxes = this.element.getElementsByClassName('calc-l');\n for (var i = 0; i < calcNodeBoxes.length; i++)\n if (calcNodeBoxes[i].className.indexOf('node-selected') >= 0){\n this._setMarkedCalc(calcNodeBoxes[i]);\n return;\n }\n }\n\n _setMarkedCalc(nodeBox){\n if (this.markedNode !== null){\n this.markedNode.className= this.markedNode.className.replace('-marked','');\n this.markedNode.querySelector('#icon-container').style.display = 'none';\n let folderIcon = this.markedNode.querySelector('.folder-icon');\n if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder.png';\n }\n\n nodeBox.className += '-marked';\n let folderIcon = nodeBox.querySelector('.folder-icon');\n if (folderIcon !== null ) folderIcon.src = util.IMAGE_DIR+'folder-sel.png';\n nodeBox.querySelector('#icon-container').style.display = 'block';\n this.markedNode= nodeBox;\n\n // The parent tree node is unfolded in order to show the leaf selected\n let foldingElement = nodeBox.parentElement.previousElementSibling.firstElementChild;\n if (foldingElement.className === 'node-folded'){\n foldingElement.className = 'node-unfolded';\n foldingElement.parentElement.nextElementSibling.style.display= 'block';\n }\n\n if (this.leafMarkedListener !== undefined)\n this.leafMarkedListener(nodeBox.getAttribute('data-calc-id'));\n }\n\n showCalcsGraphDataAvailability(bool){\n let elements= this.element.getElementsByClassName('calc-graph-aval');\n for (var i = 0; i < elements.length; i++)\n elements[i].style.display = (bool ? 'inline' : 'none');\n }\n\n setHeight(heightPx){\n this.element.style.height = heightPx+'px';\n }\n\n} // class NavTree\n\n\n\nfunction keepTreeIntegrity(nodeBox, select){\n\n if (nodeBox.className.indexOf('material-l') >= 0) return;\n let levelBox= nodeBox.parentElement;\n\n for (let i = 0; i < levelBox.children.length; i++ ) {\n let siblingNodeBox = levelBox.children[i++];\n if (siblingNodeBox !== nodeBox &&\n siblingNodeBox/*.children[1]*/.className.indexOf('selected') < 0)\n return;\n }\n let parentNodeBox= levelBox.previousElementSibling;\n if (select){\n parentNodeBox.className += ' node-selected';\n }else{\n let index= parentNodeBox.className.indexOf(' node-selected');\n parentNodeBox.className= parentNodeBox.className.substring(0,index);\n }\n keepTreeIntegrity(parentNodeBox, select);\n}\n\n\n\n\n// EXPORTS\nmodule.exports = NavTree;\n\n\n//# sourceURL=webpack:///./src/material-mod/NavTree.js?"); + +/***/ }), + +/***/ "./src/material-mod/Overview.view.js": +/*!*******************************************!*\ + !*** ./src/material-mod/Overview.view.js ***! + \*******************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the Overview view component in the Material Module.\n */\n\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet BSPlotter = __webpack_require__(/*! ./BSPlotter.js */ \"./src/material-mod/BSPlotter.js\");\nlet DOSPlotter = __webpack_require__(/*! ./DOSPlotter.js */ \"./src/material-mod/DOSPlotter.js\");\nlet HeatCapPlotter = __webpack_require__(/*! ./HeatCapPlotter.js */ \"./src/material-mod/HeatCapPlotter.js\");\nlet MaterialMod = __webpack_require__(/*! ./MaterialMod.js */ \"./src/material-mod/MaterialMod.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet SimilarityFinder = __webpack_require__(/*! ./Similarity.js */ \"./src/material-mod/Similarity.js\").SimilarityFinder;\n\nconst ELEMENT_INCLUDED_MSG = 'ELEMENT ALREADY INCLUDED';\n\n\nclass Overview {\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','overview');\n this.materialId;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n this.calcMaterialId = null;\n this.element.innerHTML=`\n\n <div class=\"material-title\">\n </div>\n\n <div style=\"float: left; width: 40%;\">\n\n <div id=\"structure-ov\" class=\"view-box\">\n <div class=\"title\">Structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"viz-box-container\" style=\"height: 260px; position: relative\">\n <div class=\"viz-box\" style=\"height: 90%\"></div>\n </div>\n\n <div class=\"footer\">\n <div ><b><span>Material type</span></b>:\n <span class=\"material-type-field\" ></span>\n </div>\n <div class=\"space-group-field\" style=\"display: none\">\n <b><span info-sys-data=\"space-group\">Space group</span></b>:\n <span class=\"space-group-value\" ></span>\n </div>\n <div class=\"structure-type-field\" style=\"display: none\">\n <b><span info-sys-data=\"structure-type\">Structure type</span></b>:\n <span class=\"structure-type-value\" ></span>\n </div>\n </div>\n </div>\n\n\n<!-- ***** Elastic Constants Box\n\n <div id=\"elastic-ov\" class=\"view-box\">\n <div class=\"title\">Elastic constants\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n Not analyzed yet\n </div>\n\n </div>\n-->\n\n\n <div id=\"methodology-ov\" class=\"view-box\">\n <div class=\"title\">Methodology\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n\n <div class=\"info-fields-label\" > Available calculations </div>\n\n <div style=\"float: left; width: 45%\" >\n <b><span info-sys-data=\"functional-type\">Functional</span></b>\n <div class=\"functional-field\" > </div>\n </div>\n <div style=\"float: right; width: 45%\" >\n <b><span info-sys-data=\"code-name\">Code</span></b>\n <div class=\"code-field\"> </div>\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"float: right; width: 60%;\">\n\n <div id=\"e-structure-ov\" class=\"view-box\" style=\"display: block\">\n <div class=\"title\">Electronic structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div > <!-- style=\"margin: 12% 0; \" -->\n\n <div style=\"float: left; width: 60%; \">\n <div style=\"padding: 20px 0 20px 30px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"band-structure\">Band structure</span>\n </div>\n <div>\n <div id=\"band-plotter\" > </div>\n </div>\n\n <div class=\"footer-bs-calc\"></div>\n </div>\n </div>\n\n <div style=\"float: left; width: 40%; \">\n <div style=\"padding: 20px 30px 20px 40px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"DOS\">DOS</span>\n </div>\n\n <div>\n <div id=\"dos-plotter\" > </div>\n </div>\n <div class=\"footer-dos-calc\"></div>\n </div>\n </div>\n\n\n\n <div style=\"clear: both;\"></div>\n <table style=\"width: 100%\">\n <tr>\n <td class=\"spin-legend\" style=\"font-size: 0.9em; padding: 6px 30px 10px; display: none;\">\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin1\"/></svg>\n Spin <span style='font-size: 1.1em'>⇧</span> \n\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin2\"/></svg>\n Spin <span style='font-size: 1.1em'>⇩</span>\n </td>\n <td class = \"similarity-data-field\">\n <div style=\"float: right; padding: 0px 50px 10px 0px; margin-top: 1px;\" class=\"similarity-finder\">\n </div>\n </td>\n </tr>\n </table>\n </div>\n <div style=\"clear: both;\"></div>\n <!--\n <div class=\"footer\">\n\n <b>Band gap</b>: <span class=\"e-struct-field\" ></span>\n </div>\n -->\n </div>\n\n <div id=\"thermal-props-ov\" class=\"view-box\" style=\"visibility: hidden\">\n <div class=\"title\">Vibrational and thermal properties\n <img style=\"float: right\" class=\"to-detail thermal-props\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div style=\"padding: 36px; \">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"heat-capacity-cv\">Specific heat</span>\n </div>\n\n\n <div>\n <div id=\"heat-plotter\" > </div>\n </div>\n <div class=\"footer-heat-calc\" style=\"text-align: center\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"clear: both;\"></div>\n `;\n\n this.materialTitle= this.element.getElementsByClassName('material-title')[0];\n\n this.systemType= this.element.querySelector('.material-type-field');\n this.spaceGroupField = this.element.querySelector('.space-group-field');\n this.spaceGroupValue = this.element.querySelector('.space-group-value');\n this.structTypeField= this.element.querySelector('.structure-type-field');\n this.structTypeValue= this.element.querySelector('.structure-type-value');\n //this.band_gap = this.element.getElementsByClassName('e-struct-field')[0];\n //fields= this.element.getElementsByClassName('method-field');\n this.functional= this.element.querySelector('.functional-field');//fields[0];\n this.code= this.element.querySelector('.code-field');//fields[1];\n\n let fields= this.element.getElementsByClassName('to-detail');\n this.structureDetailBtn= fields[0];\n this.electronicStructDetailBtn= fields[2];\n this.methodologyDetailBtn= fields[1];\n this.thermalDetailBtn= fields[3];\n/*\n this.elasticDetailBtn= fields[1];\n this.methodologyDetailBtn= fields[2];\n this.electronicStructDetailBtn= fields[3];\n this.thermalDetailBtn= fields[4];\n */\n\n this.vizBox = this.element.getElementsByClassName('viz-box')[0];\n this.bandPlotter= null;\n this.bsCalcIdBox = this.element.getElementsByClassName('footer-bs-calc')[0];\n this.dosPlotter= null;\n this.dosCalcIdBox = this.element.getElementsByClassName('footer-dos-calc')[0];\n this.heatPlotter= null;\n this.heatCalcIdBox = this.element.querySelector('.footer-heat-calc');\n\n this.spinLegend = this.element.querySelector('.spin-legend');\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n\n // Store the state of the calcs chosen on the Elec. Structure box\n this.eStructCalcs = { bs: null, dos: null};\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._events();\n }\n\n\n _events() {\n\n this.structureDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.structure);\n });\n\n this.electronicStructDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.electronicstruct);\n });\n\n this.methodologyDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.methodology);\n });\n\n this.thermalDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.thermalprops);\n });\n\n/*\n this.elasticDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.elasticconst);\n });\n*/\n\n\n //******* Optimize, genralize:\n //this.element.querySelectorAll('.to-detail+.'+detailsId).addEventListener( \"click\", (e) => {\n // util.setBrowserHashPath('material', this.materialId+'/'+detailsId);\n }\n\n\n getEStructChosenCalcs(){\n return this.eStructCalcs;\n }\n\n\n setDetailViewsListener(listener){\n this.detailViewsListener= listener;\n }\n\n setVisible() {\n this.element.style.display = 'block';\n }\n\n\n setMaterialData() {\n\n let data = DataStore.getMaterialData();\n this.materialTitle.innerHTML= util.getMaterialTitle(data);\n this.materialId = data.material_id;\n\n let isBulk = (data.material_type === 'bulk');\n this.systemType.textContent= data.material_type;\n this.structTypeField.style.display =\n (isBulk && data.structure_type !== null ? 'block' : 'none');\n this.spaceGroupField.style.display = (isBulk ? 'block' : 'none');\n\n if (isBulk){\n this.structTypeValue.textContent= data.structure_type;\n this.spaceGroupValue.textContent = data.space_group_number+\n ' ('+data.space_group_international_short_symbol+')';\n InfoSys.addElementToInfoSystem(this.spaceGroupValue,\n 'space-group.value:'+data.space_group_number);\n }\n\n if (this.similarityFinder) {\n this.similarityFinder.element.remove();\n }\n\n if (data.similarity) {\n this.similarityFinder = new SimilarityFinder({left: 40, right: 16, top: 0, bottom: 30});\n\n const similarityFinderContainer = this.element.querySelector('.similarity-finder');\n this.similarityFinder.setSimilarityData(data.similarity);\n similarityFinderContainer.appendChild(this.similarityFinder.element);\n this.similartyDataField = this.element.querySelector('.similarity-data-field');\n InfoSys.addToInfoSystem(this.similartyDataField);\n }\n }\n\n isLoaded(hasBs, hasDOS, hasPhonon) {\n let materialData = DataStore.getMaterialData();\n let material_id = materialData.material_id;\n if (this.calcMaterialId === material_id) {\n if (hasBs && !this.bsLoaded) {\n return false;\n }\n if (hasDOS && !this.dosLoaded) {\n return false;\n }\n if (hasPhonon && !this.phononLoaded) {\n return false;\n }\n return true;\n }\n return false;\n }\n\n clearCalcsData() {\n this.materialId = null;\n this.calcMaterialId = null;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n }\n\n setCalcsData(markedTreeLeafs) {\n let matData = DataStore.getMaterialData();\n let calcs = DataStore.getCalculations();\n let functionalMap = new Map();\n let codeMap = new Map();\n\n // Get the representative calculations from the material data returned\n let calcWithBS = DataStore.getCalc(DataStore.getRepresentatives().electronic_band_structure);\n let calcWithDOS = DataStore.getCalc(DataStore.getRepresentatives().electronic_dos);\n let calcWithHeat = DataStore.getCalc(DataStore.getRepresentatives().thermodynamical_properties);\n\n if (this.isLoaded(calcWithBS !== undefined, calcWithDOS !== undefined, calcWithHeat !== undefined)) {\n return;\n }\n this.calcMaterialId = matData.material_id;\n this.bsLoaded = false;\n this.dosLoaded = false;\n this.phononLoaded = false;\n\n // Gather how many calculations there are for each functional and code\n for (let i = 0; i < calcs.length; i++) {\n if (functionalMap.has(calcs[i].functional_type)) {\n let num = functionalMap.get(calcs[i].functional_type);\n functionalMap.set(calcs[i].functional_type, ++num);\n } else {\n functionalMap.set(calcs[i].functional_type, 1);\n }\n\n let codeNameTrimed= calcs[i].code_name.trim();\n if (codeMap.has(codeNameTrimed)) {\n let num= codeMap.get(codeNameTrimed);\n codeMap.set(codeNameTrimed, ++num);\n } else {\n codeMap.set(codeNameTrimed, 1);\n }\n }\n\n if (calcWithBS !== undefined) this.eStructCalcs.bs = calcWithBS.calc_id;\n if (calcWithDOS !== undefined) this.eStructCalcs.dos = calcWithDOS.calc_id;\n\n let tempCalcId = null;\n if (calcWithBS !== undefined) tempCalcId = calcWithBS.calc_id;\n else if (calcWithDOS !== undefined) tempCalcId = calcWithDOS.calc_id;\n\n if (tempCalcId === null) {\n markedTreeLeafs.eStruct = null; // no graph data\n } else\n markedTreeLeafs.eStruct = +tempCalcId;\n\n if (calcWithHeat === undefined) {\n markedTreeLeafs.thermalProps = null;\n } else {\n markedTreeLeafs.thermalProps = +calcWithHeat.calc_id;\n }\n\n // Add list of functionals\n this.functional.textContent = \"\";\n let container = document.createElement(\"div\");\n functionalMap.forEach((number, functional) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"functional-type.value:\" + util.getDefault(functional));\n span.textContent = number + ' ' + util.getDefault(functional);\n container.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container.appendChild(linebreak);\n });\n this.functional.append(container);\n InfoSys.addToInfoSystem(this.functional);\n\n // Add list of codes\n this.code.textContent = \"\";\n let container2 = document.createElement(\"div\");\n codeMap.forEach((number, codeName) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"code-name.value:\" + codeName);\n span.textContent = number + ' ' + codeName;\n container2.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container2.appendChild(linebreak);\n });\n this.code.append(container2);\n InfoSys.addToInfoSystem(this.code);\n\n if (DataStore.hasElecStructureData()) {\n document.getElementById('e-structure-ov').style.display = 'block';\n let isReady = () => {\n if (this.dosLoaded && this.bsLoaded) {\n document.getElementById('e-structure-ov').style.visibility = 'visible';\n }\n };\n\n if (this.bandPlotter === null){\n this.bandPlotter= new BSPlotter();\n this.bandPlotter.attach(document.getElementById('band-plotter'),undefined,316);\n }\n if (this.dosPlotter === null){\n this.dosPlotter= new DOSPlotter({left: 40, right: 20, top: 0, bottom: 30});\n this.dosPlotter.attach(document.getElementById('dos-plotter'),undefined,317);\n }\n\n if (calcWithBS === undefined){\n this.bandPlotter.setNoData();\n this.bsCalcIdBox.innerHTML = '';\n this.bsLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithBS.calc_id);\n LoadingPopup.show(\"overview_electronic_band_structure\");\n let query = JSON.stringify({properties: [\"electronic_band_structure\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let bandStructData = JSON.parse(e.target.response).electronic_band_structure;\n this.bandPlotter.setBandStructureData(bandStructData);\n this.bsCalcIdBox.innerHTML = 'From calculation <b>' + util.getShortCode(calcWithBS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithBS.functional_type+' - '+calcWithBS.code_name+')</span>';\n if (bandStructData.section_k_band_segment[0].band_energies.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.bandPlotter.setNoData();\n }\n this.bsLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_band_structure\");\n });\n }\n\n if (calcWithDOS === undefined){\n this.dosPlotter.setNoData();\n this.dosCalcIdBox.innerHTML = '';\n this.dosLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithDOS.calc_id);\n LoadingPopup.show(\"overview_electronic_dos\");\n let query = JSON.stringify({properties: [\"electronic_dos\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let dosData= JSON.parse(e.target.response).electronic_dos;\n this.dosPlotter.setPoints(dosData, calcWithDOS);\n this.dosCalcIdBox.innerHTML = 'From calculation <b>'+util.getShortCode(calcWithDOS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithDOS.functional_type+' - '+calcWithDOS.code_name+')</span>';\n if (dosData.dos_values.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.dosPlotter.setNoData();\n }\n this.dosLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_dos\");\n });\n }\n }\n\n if (!DataStore.hasThermalData()) {\n document.getElementById('thermal-props-ov').style.display = 'none';\n } else {\n document.getElementById('thermal-props-ov').style.display = 'block';\n\n if (this.heatPlotter === null){\n this.heatPlotter= new HeatCapPlotter();\n this.heatPlotter.attach(document.getElementById('heat-plotter'),undefined,317);\n }\n\n if (calcWithHeat === undefined){\n this.heatPlotter.setNoData();\n this.heatCalcIdBox.innerHTML = '';\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithHeat.calc_id);\n LoadingPopup.show();\n let query = JSON.stringify({properties: [\"thermodynamical_properties\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200) {\n let response = JSON.parse(e.target.response);\n let thermoProp = response.thermodynamical_properties;\n let t = thermoProp.thermodynamical_property_temperature;\n this.heatPlotter.setData(t, thermoProp.specific_heat_capacity);\n this.heatCalcIdBox.innerHTML = 'From calculation <b>'+util.getShortCode(calcWithHeat.calc_id)+'</b>'+\n '</b> <span style=\"font-size: 0.8em\">('+calcWithHeat.functional_type+' - '+calcWithHeat.code_name+')</span>';\n this.phononLoaded = true;\n document.getElementById('thermal-props-ov').style.visibility = 'visible';\n }\n LoadingPopup.hide();\n });\n }\n }\n }\n}\n\n// EXPORTS\nmodule.exports = Overview;\n\n\n//# sourceURL=webpack:///./src/material-mod/Overview.view.js?"); + +/***/ }), + +/***/ "./src/material-mod/PhononDispDOSPlotter.js": +/*!**************************************************!*\ + !*** ./src/material-mod/PhononDispDOSPlotter.js ***! + \**************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Implements a graphical UI component composed of\n a phonon DOS plotter (PhononDOSPlotter class, implemented in thsi file) and a\n BSPlotter (showing Phonon Dispersion)\n */\n\n\nlet BSPlotter = __webpack_require__(/*! ./BSPlotter.js */ \"./src/material-mod/BSPlotter.js\");\nlet InteractivePlotterBase = __webpack_require__(/*! ../common/InteractivePlotterBase.js */ \"./src/common/InteractivePlotterBase.js\");\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\n\nclass PhononDispDOSPlotter{\n\n constructor() {\n this.element = document.createElement('div');\n //this.element.setAttribute('id','elementable');\n this.parentElement= null;\n this.dispPlotter= new BSPlotter();\n this.dispPlotter.setPhononMode();\n\n this.dosPlotter= new PhononDOSPlotter();\n }\n\n\n attach(element, width, height){\n element.appendChild(this.element);\n this.dispPlotter.attach(this.element, height, height);\n this.dosPlotter.attach(this.element, undefined, height);\n this.parentElement= element;\n }\n\n\n isAttached(){\n return this.parentElement !== null;\n }\n\n\n setUpAndData(dispData, dosData){\n\n this.hasDispData = (dispData !== undefined && dispData !== null);\n this.hasDosData = (dosData !== undefined && dosData !== null);\n\n if (this.hasDispData){\n this.dispPlotter.setBandStructureData(dispData);\n if (this.hasDosData)\n this.dispPlotter.setRepaintListener( (yZoom, yOffset) => {\n this.dosPlotter.setYZoomAndOffset(yZoom, yOffset);\n this.dosPlotter.repaint();\n });\n }else\n this.dispPlotter.setNoData();\n\n if (this.hasDosData){\n this.dosPlotter.setPoints(dosData);\n if (this.hasDispData)\n this.dosPlotter.setRepaintListener( (yZoom, yOffset) => {\n this.dispPlotter.setYZoomAndOffset(yZoom, yOffset);\n this.dispPlotter.repaint();\n });\n }else\n this.dosPlotter.setNoData();\n }\n\n setNoData(){\n this.dispPlotter.setNoData();\n this.dosPlotter.setNoData();\n }\n\n}\n\n\n\nclass PhononDOSPlotter extends InteractivePlotterBase{\n\n constructor() {\n super({left: 4, right: 16, top: 0, bottom: 30});\n this.outOfRangeColorActivated = false;\n }\n\n // detach if necessary and attach\n attach(element, width, height){\n super.attach(element, height/2+this.margins.left, height);\n }\n\n\n setPoints(points){\n\n this.pointsSpin1 = [];\n this.pointsSpin2 = [];\n this._reset();\n\n let pSpin1= points.dos_values[0];\n let pSpin2 = null;\n if (points.dos_values.length === 2) pSpin2 = points.dos_values[1];\n let pointsY= points.dos_energies;\n let pointsXInPlotRange = [];\n let pointsYInPlotRange = [];\n\n for (var i = 0; i < pointsY.length; i++) {\n let frecuency = pointsY[i]*5.034117012222e22;\n let dosSpin1 = pSpin1[i]*0.029979;\n let dosSpin2;\n if (pSpin2 !== null) dosSpin2 = pSpin2[i]*0.029979246;\n //console.log('POINTS : ',frecuency);\n pointsXInPlotRange.push(dosSpin1);\n if (pSpin2 !== null) pointsXInPlotRange.push(dosSpin2);\n pointsYInPlotRange.push(frecuency);\n //console.log('POINTS : ',pointsX[i], energy);\n this.pointsSpin1.push({x: dosSpin1, y: frecuency});\n if (pSpin2 !== null) this.pointsSpin2.push({x: dosSpin2, y: frecuency});\n }\n\n let maxDosVal = Math.max.apply(null, pointsXInPlotRange);\n let maxEnergyVal = Math.max.apply(null, pointsYInPlotRange);\n let minEnergyVal = Math.min.apply(null, pointsYInPlotRange);\n\n // x axis steps generation\n let t = util.generateDiagramSteps(maxDosVal);\n let xSteps = t[0], exp = t[1];\n\n //console.log('formattedPoints paintPointsLine : ', this.formattedPoints.length);\n this.setAxisRangeAndLabels(null,0,xSteps[xSteps.length-1]/*maxDosVal*1.2*/,\n null,-50,320,minEnergyVal, maxEnergyVal, 100);\n\n svg.addText(this.axisGroup, this.plotRangeX/2, this.margins.bottom,\n 'DOS (states/cm⁻¹)', 'middle', 'axis-steps-big');\n\n for (let i = 0; i < xSteps.length; i++) {\n let stepX = (this.plotRangeX*xSteps[i])/xSteps[xSteps.length-1];\n svg.addLine(this.axisGroup, stepX, 0, stepX, 3, 1);\n //console.log('step ',xSteps[i], stepX);\n svg.addText(this.axisGroup, stepX, 13,\n (i === 0 ? '0' : xSteps[i].toFixed(exp)), 'middle', 'axis-steps-smaller');\n }\n\n this.repaint();\n }\n\n\n repaintData(){\n let polylinePoints = '';\n for (var i = 0; i < this.pointsSpin1.length; i++) {\n polylinePoints+= ' '+this.xRel*this.pointsSpin1[i].x+\n ' '+this.transformY(this.pointsSpin1[i].y);\n //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin1');\n\n polylinePoints = '';\n for (var i = 0; i < this.pointsSpin2.length; i++) {\n polylinePoints+= ' '+this.xRel*this.pointsSpin2[i].x+\n ' '+this.transformY(this.pointsSpin2[i].y);\n //console.log('POINTS LAT : ',this.formattedPoints[i].x, this.formattedPoints[i].y);\n }\n svg.addPolyline(this.plotContent, polylinePoints, 'plotSpin2');\n\n }\n}\n\n// EXPORTS\nmodule.exports = PhononDispDOSPlotter;\n\n\n//# sourceURL=webpack:///./src/material-mod/PhononDispDOSPlotter.js?"); + +/***/ }), + +/***/ "./src/material-mod/Similarity.js": +/*!****************************************!*\ + !*** ./src/material-mod/Similarity.js ***! + \****************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("let util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\nclass SimilarityFinder {\n\n constructor() {\n this.folded = true;\n this.element = document.createElement('span');\n this.element.style = \"float : right; border: 2px solid #DDD; padding: 2px 2px 0px 3px;\"\n this.element.innerHTML+=`\n <span info-sys-data=\"similar-materials\" style=\"vertical-align: 30%;\">Similar materials</span>\n <img style=\"cursor: pointer\" src=\"${util.IMAGE_DIR}folded.png\" />\n\n <div class=\"vr-download-panel\" style=\"position: relative; display: none;\">\n\n </div>\n `;\n\n // Focus related properties (in order to hide the box when the user click out)\n this.element.tabIndex = '0'; // enabled the support of focusing\n this.element.style.outline = 'none'; // The outline is not shown when it gains the focus\n\n this.foldingPanel = this.element.querySelector('.vr-download-panel');\n this.foldBtn = this.element.querySelector('img');\n\n this.foldBtn.addEventListener('click', e => {\n this.folded = !this.folded;\n this.foldBtn.src = (this.folded ? util.IMAGE_DIR+'folded.png' :\n util.IMAGE_DIR+'unfolded.png');\n //this.foldBtn.className = (this.folded ? 'on' : 'off');\n this.foldingPanel.style.display = (this.folded ? 'none' : 'block');\n });\n\n this.element.addEventListener('blur' , e => {\n setTimeout(() => {\n this.folded = true;\n this.foldBtn.src = util.IMAGE_DIR+'folded.png';\n this.foldingPanel.style.display = 'none';\n }, 300);\n });\n\n //this.cellViewer.toggleLatticeParameters(false);\n }\n\n setSimilarityData(similarityData) {\n similarityData.sort(function(a, b) {\n if (a.value < b.value) {\n return 1\n } else if (a.value > b.value) {\n return -1\n } else {\n return 0\n }\n })\n\n const slicedData = similarityData.slice(0, 5)\n\n const container = document.createElement('table')\n container.setAttribute('class','similar-materials-panel-unfolded')\n container.style='width: 230px; padding-left: 5px;'\n this.foldingPanel.appendChild(container)\n const sim_table_header = document.createElement('tr')\n sim_table_header.style = \"padding: 5px; \"\n sim_table_header.innerHTML = `<th style = \"text-align: left;\">Formula (space group)</th><th>:</th><th style = \"text-align: left;\">Tc</th>`\n container.appendChild(sim_table_header)\n\n slicedData.forEach(function(item) {\n const element = document.createElement('tr')\n element.style = \"padding: 5px; font-family: 'Arimo', sans-serif; font-size: 10pt; \"\n const url = `${window.location.toString().replace(/#.*$/, \"\")}#/material/${item.material_id}`\n element.innerHTML = `<td><a href=\"${url}\" target=\"_${item.material_id}\" style=\"color:#777; font-family: 'Arimo', sans-serif; font-size: 10pt;\">${util.getSubscriptedFormula(item.formula)} (${item.space_group_number})</a></td><td>:</td> <td>${item.value.toPrecision(3)}</td>`\n container.appendChild(element)\n })\n\n }\n\n} // class DropDown\n\nmodule.exports = { SimilarityFinder };\n\n\n//# sourceURL=webpack:///./src/material-mod/Similarity.js?"); + +/***/ }), + +/***/ "./src/material-mod/SortingButton.js": +/*!*******************************************!*\ + !*** ./src/material-mod/SortingButton.js ***! + \*******************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n Sorting button component implementation\n */\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\nclass SortingButton {\n\n constructor(id) {\n\n this.id = id;\n this.ascending = true;\n\n this.element = document.createElement('span');\n this.element.innerHTML+=`\n <img src=\"img/sorting_init.png\" width=\"12px\"\n style=\"margin-bottom: -1px; cursor: pointer\"/>\n `;\n this.image = this.element.querySelector('img');\n\n if (id === 'id')\n this.image.setAttribute('src','img/sorting_ascending.png');\n\n this.element.addEventListener('click', e => {\n this.ascending = !this.ascending;\n this.image.setAttribute('src',\n 'img/sorting_'+(this.ascending ? 'ascending' : 'descending')+'.png');\n this.listener(this.ascending, this.id);\n });\n }\n\n\n init(){\n this.image.setAttribute('src','img/sorting_init.png');\n }\n\n\n setListener(listener){\n this.listener = listener;\n }\n\n}\n\n// EXPORTS\nmodule.exports = SortingButton;\n\n\n//# sourceURL=webpack:///./src/material-mod/SortingButton.js?"); + +/***/ }), + +/***/ "./src/material-mod/StatsViewer.js": +/*!*****************************************!*\ + !*** ./src/material-mod/StatsViewer.js ***! + \*****************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n The StatsViewer class implements a graphical UI component composed of\n a histogram plot.\n */\n\n\n\n\nlet svg = __webpack_require__(/*! ../common/SVG.js */ \"./src/common/SVG.js\");\nlet PlotterBase = __webpack_require__(/*! ../common/PlotterBase.js */ \"./src/common/PlotterBase.js\");\n\n\nclass FrequencyGraph extends PlotterBase {\n\n constructor() {\n super({left: 54, right: 20, top: 20, bottom: 30});\n }\n\n /**\n * Used to draw bar histogram.\n */\n drawBars(occurrences, values, xMin, xMax, interval, nBins) {\n\t\tfor (let i=0; i < occurrences.length; ++i) {\n let xi = (values[i]-xMin) * this.xRel;\n let width;\n\t\t let yi = occurrences[i] * this.yRel;\n\t\t if (interval == 0) {\n width = (xMax-xMin)/nBins*this.xRel;\n\t\t } else {\n width = interval*this.xRel;\n\t\t }\n svg.addRect(this.plotArea, xi, -yi, width, yi, \"bar\");\n };\n }\n}\n\n\nclass StatsViewer{\n\n constructor() {\n this.freqGraph = new FrequencyGraph();\n }\n\n attach(element, width, height) {\n\t this.freqGraph.attach(element, width, height);\n }\n\n drawPoints(histogramData, xLabel, nBins){\n let occurrences = histogramData.occurrences;\n let values = histogramData.values;\n let xMin;\n let xMax;\n let yMin = 0;\n let yMax = Math.max(...occurrences);\n let interval;\n if (values.length == 1) {\n let value = values[0];\n // If the single value is nonzero, the boundaries are determined through scaling.\n if (value !== 0) {\n xMin = 0.9*value;\n xMax = 1.1*value+0.2*value/nBins;\n // If the single value is zero, the boundaries are determined by adding a constant padding.\n } else {\n xMin = 0;\n xMax = value + 1;\n }\n interval = 0;\n } else {\n xMin = values[0];\n interval = values[1]-values[0];\n xMax = values[values.length-1]+interval;\n }\n\n this.freqGraph.setRangeAndLabels(xLabel, xMin, xMax, 'Occurrence', yMin, yMax);\n this.freqGraph.drawAxis((xMax === xMin ? null : 5), 2, (xMin > 1000 ? 0 : 2));\n this.freqGraph.drawBars(occurrences, values, xMin, xMax, interval, nBins);\n }\n\n clear() {\n this.freqGraph.clear();\n }\n}\n\n// EXPORTS\nmodule.exports = StatsViewer;\n\n\n//# sourceURL=webpack:///./src/material-mod/StatsViewer.js?"); + +/***/ }), + +/***/ "./src/material-mod/StructureDetails.view.js": +/*!***************************************************!*\ + !*** ./src/material-mod/StructureDetails.view.js ***! + \***************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n 'Details' view container that shows all the material info related to\n its structure.\n\n This container is extremely complex.\n\n In the file there are two defined (classes) components used in the container:\n - TreeLeafViewer: The panel on the right showing the data of the tree leaf marked\n - SummaryByFunctionalsComponent: the component (central panel, below part)\n showing a summary for the selected item on the tree (by functional)\n */\n\n\n\nlet DetailsViewBase = __webpack_require__(/*! ./DetailsViewBase.js */ \"./src/material-mod/DetailsViewBase.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet NavTree = __webpack_require__(/*! ./NavTree.js */ \"./src/material-mod/NavTree.js\");\nlet CalcSelectorBar = __webpack_require__(/*! ./CalcSelectorBar.js */ \"./src/material-mod/CalcSelectorBar.js\");\nlet StatsViewer = __webpack_require__(/*! ./StatsViewer.js */ \"./src/material-mod/StatsViewer.js\");\nlet EquationOfStateViewer = __webpack_require__(/*! ./EquationOfStateViewer.js */ \"./src/material-mod/EquationOfStateViewer.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n\nclass StructureDetails extends DetailsViewBase {\n\n constructor() {\n super('Structure');\n this.navTree = new NavTree();\n this.groupsData;\n this.element.innerHTML+=`\n\n <div style=\"float: left; width: 36%;\">\n <div class=\"view-box\">\n <div class=\"title\">Structure </div>\n <div class=\"viz-box-container\" style=\"height: 400px; position: relative\">\n <div class=\"viz-box\" style=\"height: 90%\"></div>\n </div>\n\n <div class=\"footer-flex-wrapper\">\n\n <div class=\"fields-container\">\n <div><b><span>System type</span></b>:\n <span class=\"struct-field\" ></span>\n </div>\n <div class=\"structure-type-field\" style=\"display: none\">\n <b><span info-sys-data=\"structure-type\">Structure type</span></b>:\n <span class=\"structure-type-value\" ></span>\n </div>\n <div class=\"structure-prototype-field\" style=\"display: none\">\n <b><span info-sys-data=\"structure-prototype\">Structure prototype</span></b>:\n <span class=\"structure-prototype-value\" ></span>\n </div>\n <div class=\"strukturbericht-field\" style=\"display: none\">\n <b><span info-sys-data=\"strukturbericht\">Strukturbericht designation</span></b>:\n <span class=\"strukturbericht-value\" ></span>\n </div>\n </div>\n\n <div class=\"footer-flex\" style=\"display: none\">\n\n <div class=\"fields-container\"\n style=\"flex-basis: 70%; border-right: 1px solid #E4E4E4; \">\n\n <div>\n <b><span info-sys-data=\"crystal-system\">Lattice</span></b>:\n <span class=\"lattice-value\" ></span>\n </div>\n <div>\n <b><span info-sys-data=\"space-group\">Space group</span></b>:\n <span class=\"space-group-value\" ></span>\n </div>\n <div>\n <b><span info-sys-data=\"point-group\">Point group</span></b>:\n <span class=\"point-group-value\" ></span>\n </div>\n </div>\n\n <div style=\"flex-basis: 30%; margin-left: 30px;\">\n <div class=\"fields-container\">\n <div><b><span info-sys-data=\"wyckoff-position-population\">Wyckoff sites</span></b></div>\n <div class=\"wyckoff-sites-value\"> </div>\n </div>\n </div>\n\n </div>\n\n </div>\n\n </div>\n </div>\n\n <div style=\"float: left; width: 36%;\">\n <div class=\"view-box\">\n <div class=\"title\">Calculations</div>\n <div class=\"navTreeWrapper\"></div>\n\n <div class=\"summary-title\">Summary </div>\n <div style=\"font-size: 0.85em; text-align: center; padding: 4px;\">Based on the calculations selected above</div>\n\n <div class=\"info-fields summary-box\">\n <!-- Lattice constants Cell volume, Density panel dynamically generated\n -->\n </div>\n </div>\n </div>\n\n <div style=\"float: right; width: 28%;\">\n <div class=\"calc-specifics-box\">\n <div style=\"padding-top: 10px; \" >\n <div class=\"tree-leaf-title\"></div>\n </div>\n <div class=\"tree-leaf-viewer-host\"></div>\n </div>\n </div>\n </div>\n </div>\n `;\n\n this.navTreeWrapper =\n this.element.getElementsByClassName(\"navTreeWrapper\")[0];\n\n let fields= this.element.getElementsByClassName('struct-field');\n this.systemTypeField= fields[0];\n this.structTypeField= this.element.querySelector('.structure-type-field');\n this.structTypeValue= this.element.querySelector('.structure-type-value');\n this.structPrototypeField= this.element.querySelector('.structure-prototype-field');\n this.structPrototypeValue= this.element.querySelector('.structure-prototype-value');\n this.strukturberichtField= this.element.querySelector('.strukturbericht-field');\n this.strukturberichtValue= this.element.querySelector('.strukturbericht-value');\n\n this.lowerBox = this.element.querySelector('.footer-flex');\n this.latticeValue= this.element.querySelector('.lattice-value');\n this.spaceGroupValue= this.element.querySelector('.space-group-value');\n this.pointGroupValue= this.element.querySelector('.point-group-value');\n this.wyckoffValue= this.element.querySelector('.wyckoff-sites-value');\n\n this.summaryByFunctionals = null;\n\n this.leafTitle = this.element.querySelector('.tree-leaf-title');\n\n this.summaryBox = this.element.querySelector('.summary-box');\n this.summaryByFunctionals = new SummaryByFunctionalsComponent(this.summaryBox);\n\n this.calcSpecificsBox = this.element.querySelector('.calc-specifics-box');\n\n this.treeLeafViewer = new TreeLeafViewer();\n this.element.querySelector('.tree-leaf-viewer-host').\n appendChild(this.treeLeafViewer.element);\n\n this.vizBox = this.element.querySelector('.viz-box');\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n }\n\n setMaterialData() {\n let data = DataStore.getMaterialData();\n let idealizedStructure = DataStore.getIdealizedStructure();\n super.setMaterialData(data);\n\n // Build the navigation tree and attach listeners\n let name = (data.material_name === null ?\n data.formula : data.material_name);\n this.navTree.build(name, \"structure\");\n this.navTree.selectAll();\n this.navTree.setHeight(250);\n this.navTree.setMarkedLeafIfNoneMarked(null); // Set the first leaf marked\n this.attachNavTree(this.navTree);\n this.updateSelection(this.navTree.getTreeSelectedCalcs());\n this.updateMarkedLeaf(this.navTree.getMarkedLeaf());\n this.navTree.setTreeSelectionListener( leafIds => {\n this.updateSelection(leafIds);\n });\n this.navTree.setLeafMarkedListener( leafId => {\n this.updateMarkedLeaf(leafId);\n });\n\n this.isBulk = (data.material_type === 'bulk');\n\n this.systemTypeField.textContent= data.material_type;\n this.structTypeField.style.display =\n (this.isBulk && data.structure_type !== null ? 'block' : 'none');\n this.structPrototypeField.style.display =\n (this.isBulk && data.structure_prototype !== null ? 'block' : 'none');\n this.strukturberichtField.style.display =\n (this.isBulk && data.strukturbericht_designation !== null ? 'block' : 'none');\n\n this.lowerBox.style.display = (this.isBulk ? 'flex' : 'none');\n\n if (this.isBulk){\n this.structTypeValue.textContent= data.structure_type;\n this.structPrototypeValue.textContent= data.structure_prototype;\n this.strukturberichtValue.textContent= data.strukturbericht_designation;\n this.spaceGroupValue.textContent = data.space_group_number\n +' ('+data.space_group_international_short_symbol+')';\n this.pointGroupValue.textContent = data.point_group;\n this.latticeValue.textContent = data.crystal_system;\n\n // Wyckoff processing\n let wyckoffMap = new Map();\n let valueSet= new Set();\n\n for (var i = 0; i < idealizedStructure.wyckoff_sets.length; i++) {\n let wset = idealizedStructure.wyckoff_sets[i];\n let element = wset.element;\n if (wyckoffMap.has(element)){\n wyckoffMap.get(element).add(wset.wyckoff_letter);\n } else {\n let newSet = new Set();\n newSet.add(wset.wyckoff_letter);\n wyckoffMap.set(element, newSet);\n }\n }\n\n let wyckoffHTML= '';\n wyckoffMap.forEach((posSet, element) => {\n let firstPos = true;\n wyckoffHTML += '<tr> <td>'+element+': </td>';\n posSet.forEach( pos => {\n if (firstPos){\n firstPos = false;\n wyckoffHTML += '<td>'+pos+'</td></tr>';\n }else\n wyckoffHTML += '<tr><td> </td><td>'+pos+'</td></tr>';\n });\n });\n\n this.wyckoffValue.innerHTML = '<table>' + wyckoffHTML+'</table>';\n }\n\n InfoSys.addElementToInfoSystem(this.spaceGroupValue,\n 'space-group.value:'+data.space_group_number);\n InfoSys.addElementToInfoSystem(this.latticeValue,\n 'crystal-system.value:'+data.crystal_system);\n InfoSys.addElementToInfoSystem(this.pointGroupValue,\n 'point-group.value:'+data.point_group);\n //InfoSys.addElementToInfoSystem(this.wyckoffValue, 'wyckoff-position-population.value:'+);\n }\n\n /**\n * Called whenever selections (checkboxes) in the navigation tree are\n * modified.\n */\n updateSelection(leafIds) {\n\n if (leafIds.size > 0) {\n\n // Create a list of calculation ids for which statistics are fetched. For\n // calculation groups, the first calculation is included in the\n // statistics, as it corresponds to the lowest energy calculation.\n let calcs = [];\n leafIds.forEach(leafId => {\n let isGroup = DataStore.isGroup(leafId);\n if (isGroup) {\n let groupType = DataStore.getGroupType(leafId);\n let groupId = DataStore.getGroupId(leafId);\n let calculations = DataStore.getGroups().get(groupType).get(groupId);\n // Calculations are sorted by energy, first has lowest energy.\n calcs.push(DataStore.getCalc(calculations[0]));\n } else {\n calcs.push(DataStore.getCalc(leafId));\n }\n });\n\n this.summaryBox.style.visibility = 'visible';\n\n // Groups calculation by their functional\n let calcMapByFunctional = util.getCalcMapByFunctional(calcs);\n\n // Build new summary based on the selection\n this.summaryByFunctionals.build(calcMapByFunctional);\n\n // Hide statistics box if no selections are made\n } else {\n this.summaryBox.style.visibility = 'hidden';\n }\n }\n\n /**\n * Called whenever individual calculations are selected in the navigation\n * tree.\n */\n updateMarkedLeaf(leafId) {\n let isGroup = false;\n if (leafId !== null){\n\n // Update leaf component title\n this.calcSpecificsBox.style.visibility = 'visible';\n isGroup = DataStore.isGroup(leafId);\n if (isGroup) {\n let groupType = DataStore.getGroupType(leafId);\n let groupId = DataStore.getGroupId(leafId);\n let calculations;\n calculations = DataStore.getGroups().get(groupType).get(groupId);\n this.leafTitle.innerHTML = util.getShortCode(leafId) +\n ' ('+calculations.length+')';\n } else {\n this.leafTitle.innerHTML = util.getShortCode(leafId);\n }\n } else {\n this.calcSpecificsBox.style.visibility = 'hidden';\n }\n this.treeLeafViewer.update(leafId, isGroup);\n }\n}\n\n\nclass TreeLeafViewer {\n constructor(hostClass) {\n this.groupCalcs = null;\n this.element = document.createElement('div');\n this.element.innerHTML = `\n <div>\n\n <div class=\"group-components\" style=\"display: none\">\n <div style=\"padding: 10px 0 30px 10px; \" class=\"eos-host\">\n </div>\n\n <div style=\"padding-top: 10px; \" class=\"calc-selector-host\">\n </div>\n </div>\n\n <div class=\"info-fields\">\n <div><b>Lattice constants</b></div>\n <div class=\"latt-constants\"></div>\n <div class=\"volume-field\"><b><span info-sys-data=\"cell-volume\">Volume</span></b>:\n <span class=\"volume-value\" ></span>\n </div>\n <!-- <div><b>Pressure</b>: <span class=\"\" ></span> </div>-->\n <div class=\"density-field\"><b>Density</b>:\n <div class=\"stats-fields\" >\n <span info-sys-data=\"mass-density\">Mass density</span> =\n <span class=\"mass-density-value\" ></span>\n </div>\n <div class=\"stats-fields\" >\n <span info-sys-data=\"atomic-density\">Atomic density</span> =\n <span class=\"atomic-density-value\" ></span>\n </div>\n </div>\n\n <div class=\"energy-field\"><b><span info-sys-data=\"energies\">Energies</span></b> (code-specific)</div>\n <div class=\"energy-descomp\"> </div>\n\n <div class=\"wyckoff-pos-calc-field\" >\n <b><span info-sys-data=\"free-wyckoff-parameters\">Wyckoff sites</span></b>\n (fractional coordinates)\n <div class=\"wyckoff-pos-calc-table\"> </div>\n </div>\n\n </div>\n\n </div>\n `;\n\n this.groupComponents = this.element.querySelector('.group-components');\n\n this.calcSelector = new CalcSelectorBar('calc-selector-bar','60%');\n this.element.querySelector('.calc-selector-host').\n appendChild(this.calcSelector.element);\n\n this.lattConstantsField = this.element.querySelector('.latt-constants');\n this.volumeField = this.element.querySelector('.volume-field');\n this.volumeValue = this.element.querySelector('.volume-value');\n this.densityField = this.element.querySelector('.density-field');\n this.massDensityValue = this.element.querySelector('.mass-density-value');\n this.atomicDensityValue = this.element.querySelector('.atomic-density-value');\n\n this.energyField= this.element.querySelector('.energy-field');\n this.energyDescompValue= this.element.querySelector('.energy-descomp');\n\n this.wyckoffPosField = this.element.querySelector('.wyckoff-pos-calc-field');\n this.wyckoffPosTable = this.element.querySelector('.wyckoff-pos-calc-table');\n\n this.eosViewer = new EquationOfStateViewer();\n this.eosViewer.attach(this.element.querySelector('.eos-host'),320, 280);\n\n this.eosViewer.setClickPointListener( calc => {\n this.groupCalcUpdate(calc+'');\n });\n\n InfoSys.addToInfoSystem(this.element);\n\n this._events();\n }\n\n _events() {\n this.calcSelector.setPrevListener(e => {\n if (this.groupIndex > 0){\n this.groupCalcUpdate(this.groupCalcs[--this.groupIndex]+'');\n return this.groupIndex === 0; // the first\n }\n });\n\n this.calcSelector.setNextListener( e => {\n if (this.groupIndex < this.groupCalcs.length-1){\n this.groupCalcUpdate(this.groupCalcs[++this.groupIndex]+'');\n return this.groupIndex === this.groupCalcs.length-1; // the last\n }\n });\n }\n\n /**\n * Called when a specific calculation or group is selected in the navigation tree.\n */\n update(leafId, isGroup) {\n this.representative = leafId;\n\n // If a group is selected, the EOS data is fetched and displayed.\n this.isGroup = isGroup;\n if (isGroup){\n let groupType = DataStore.getGroupType(leafId);\n let groupId = DataStore.getGroupId(leafId);\n let materialData = DataStore.getMaterialData();\n let materialId = materialData.material_id;\n let groupR = util.serverReq(util.getMaterialGroupURL(materialId, groupType, groupId), () => {\n if (groupR.status === 200) {\n let groupData = JSON.parse(groupR.response);\n let volumes = groupData.volumes.map(x => x/1e-30);\n let energies = groupData.energies.map(x => x/1.602176565e-19);\n let calculations = groupData.calculations;\n let eZero = energies[0];\n this.groupCalcs = calculations;\n this.groupIndex = 0;\n this.groupComponents.style.display = 'block';\n this.eosViewer.clear();\n this.eosViewer.draw(volumes, energies, this.groupCalcs, eZero);\n this.groupCalcUpdate(calculations[0]);\n }\n });\n } else {\n this.groupComponents.style.display = 'none';\n this.groupCalcUpdate(leafId);\n }\n }\n\n /**\n * Updates the information that is displayed for a single calculation.\n */\n groupCalcUpdate(calcId) {\n if (calcId !== null){\n\n // Updates the selected calculation in the Equation of State viewer.\n if (this.groupCalcs !== null){\n this.groupIndex = this.groupCalcs.indexOf(calcId);\n if (this.groupIndex >= 0){\n let t = util.getShortCode(calcId)+' ('+(this.groupIndex+1)+'/'+this.groupCalcs.length+')';\n this.calcSelector.setState(t, this.groupIndex === 0, this.groupIndex === this.groupCalcs.length-1);\n this.eosViewer.selectCalc(calcId);\n }\n }\n\n // Load data that is available already through the pre-fetched material\n // and calculation data.\n let materialData = DataStore.getMaterialData();\n let materialId = materialData.material_id;\n let is2Dsystem = (materialData.material_type === '2D');\n let isBulk = (materialData.material_type === 'bulk');\n let calc = DataStore.getCalc(calcId);\n let thereIsWyckoffData = DataStore.getMaterialData().has_free_wyckoff_parameters;\n\n // Make an API call to retrieve larger, calculation specific data.\n let properties = [\"energies\", \"lattice_parameters\", \"mass_density\", \"atomic_density\", \"cell_volume\"];\n if (thereIsWyckoffData) {\n properties.push(\"wyckoff_sets\");\n };\n let query = JSON.stringify({properties: properties});\n let calcR = util.serverReqPOST(util.getMaterialCalcURL(materialId, calcId), query, () => {\n if (calcR.status === 200) {\n let response = JSON.parse(calcR.response);\n\n // Lattice parameters\n let lattParams = response.lattice_parameters;\n let lattCFieldHTML = ((is2Dsystem || isBulk) ?\n `<div>b = ${util.m2Angstrom(lattParams.b)}</div>` : '');\n lattCFieldHTML += (isBulk ?\n `<div>c = ${util.m2Angstrom(lattParams.c)}</div>` : '');\n let lattBetaGammaFieldHTML = (isBulk ?\n `<div>α = ${util.rad2degree(lattParams.alpha)}</div>\n <div>β = ${util.rad2degree(lattParams.beta)}</div>` : '');\n lattBetaGammaFieldHTML += ((is2Dsystem || isBulk) ?\n `<div>γ = ${util.rad2degree(lattParams.gamma)}</div>` : '');\n this.lattConstantsField.innerHTML= `\n <div style=\"float: left; \">\n <div>a = ${util.m2Angstrom(lattParams.a)}</div>\n ${lattCFieldHTML}\n </div>\n <div style=\"float: left; padding-left: 40px;\">\n ${lattBetaGammaFieldHTML}\n </div>\n <div style=\"clear: both;padding: 0\"></div>\n `;\n\n // Volume, densities\n this.densityField.style.display = (isBulk ? 'block' : 'none');\n this.volumeField.style.display = (isBulk ? 'block' : 'none');\n if (isBulk) {\n this.volumeValue.innerHTML= util.m3ToAngstrom3(response.cell_volume);\n this.atomicDensityValue.innerHTML= util.toAngstromMinus3(response.atomic_density);\n this.massDensityValue.innerHTML = response.mass_density.toFixed(1) + ' kg/m<sup>3</sup>';\n }\n\n // Energies\n let energyFound = false;\n let energies = response.energies;\n if (energies !== undefined) {\n let energy_total = energies.energy_total;\n if (energy_total !== undefined) {\n energyFound = true;\n this.energyDescompValue.innerHTML =\n '<div>Total E = ' + util.J2eV(energy_total) + ' eV</div>';\n }\n }\n this.energyField.style.display = (energyFound ? 'block' : 'none');\n this.energyDescompValue.style.display = (energyFound ? 'block' : 'none');\n\n // Wyckoff position parameters\n this.wyckoffPosField.style.display = (thereIsWyckoffData ? 'block' : 'none');\n\n if (thereIsWyckoffData) {\n let wyckoffMap = new Map(); // Map(element, Array of pairArray[w-pos, coor])\n response.wyckoff_sets.forEach(d => {\n // Only entries having items in .variables are included\n let varsHtml = '';\n let hasVariables = false;\n let variables = d.variables;\n if (variables !== undefined) {\n hasVariables = true;\n [\"x\", \"y\", \"z\"].forEach(variable => {\n if (variable in variables) {\n let v = variables[variable];\n varsHtml += '' + variable + ' = ' + v.toFixed(2) + '<br>';\n }\n });\n }\n\n\n if (hasVariables) {\n let wyckoffVarsPair = [];\n wyckoffVarsPair.push(d.wyckoff_letter);\n wyckoffVarsPair.push(varsHtml);\n\n if (wyckoffMap.has(d.element)) {\n wyckoffMap.get(d.element).push(wyckoffVarsPair);\n } else {\n wyckoffMap.set(d.element, [wyckoffVarsPair]);\n }\n }\n\n });\n\n // Update Wyckoff information in the GUI\n let wyckoffHTML= '';\n wyckoffMap.forEach((posSet, element) => {\n posSet.sort( (a, b) => {\n return (a[0] > b[0] ? 1 : -1);\n });\n let firstPos = true;\n wyckoffHTML += '<tr > <td style=\"width: 30%;\">'+element+' </td>';\n posSet.forEach( pos => {\n if (firstPos){\n firstPos = false;\n wyckoffHTML += '<td style=\"width: 30%; \">'+pos[0]+'</td><td style=\"width: 40%;\">'+pos[1]+'</td></tr>';\n }else\n wyckoffHTML += '<tr><td> </td><td>'+pos[0]+'</td><td>'+pos[1]+'</td></tr>';\n });\n });\n this.wyckoffPosTable.innerHTML = '<table id=\"calc-wyckoff\">' + wyckoffHTML+'</table>';\n }\n }\n });\n }\n }\n}\n\n\nclass SummaryByFunctionalsComponent {\n\n constructor(hostElement) {\n this.calcMapByFunctional = null;\n this.quantitiesMap = null;\n this.hostElement = hostElement;\n this.graphTrigger = null;\n this.viewType = 'text';\n this.functional = null;\n this.nBins = 15;\n this.hostElement.innerHTML+=`\n <div style=\"float: left\" >\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"chart-tab\"\n viewBox=\"0 0 15 15\" width=\"15\" height=\"15\" style=\"fill: #c7c7c7;\">\n <rect x=\"0\" y=\"0\" width=\"2\" height=\"15\" />\n <rect x=\"3\" y=\"5\" width=\"1.8\" height=\"7\" />\n <rect x=\"6\" y=\"3\" width=\"1.8\" height=\"9\" />\n <rect x=\"9\" y=\"6\" width=\"1.8\" height=\"6\" />\n <rect x=\"12\" y=\"2\" width=\"1.8\" height=\"10\" />\n <rect x=\"2\" y=\"13\" width=\"13\" height=\"2\" />\n </svg>\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"text-tab\"\n viewBox=\"0 0 15 15\" width=\"15\" height=\"15\" style=\"fill: #777;\">\n <rect x=\"0\" y=\"1\" width=\"15\" height=\"2.5\" />\n <rect x=\"0\" y=\"6\" width=\"15\" height=\"2.5\" />\n <rect x=\"0\" y=\"11\" width=\"15\" height=\"2.5\" />\n </svg>\n </div>\n\n <div class=\"functional-tabs\" style=\"float: right\">\n </div>\n\n <div style=\"clear: both;\"></div>\n\n <div class=\"content-placeholder\" >\n\n <div style=\"display: block\" class=\"text-panel\" >\n <div><b>Lattice constants</b>:\n <div class=\"stats-fields latt-constants-field\" >\n </div>\n </div>\n <div class=\"volume-field\"><b><span info-sys-data=\"cell-volume\">Volume</span></b> (Å<sup>3</sup>):\n <div class=\"stats-fields volume-value\" > </div>\n </div>\n <div class=\"density-field\"><b>Density</b> :\n <div >\n <div class=\"stats-fields\" >\n <span info-sys-data=\"mass-density\">Mass density</span> (kg/m<sup>3</sup>) =\n <span class=\"mass-density-value\" ></span>\n </div>\n <div class=\"stats-fields\" >\n <span info-sys-data=\"atomic-density\">Atomic density</span> (Å<sup>-3</sup>) =\n <span class=\"atomic-density-value\" ></span>\n </div>\n </div>\n </div>\n </div>\n\n <div style=\"display:none\" class=\"chart-panel\" >\n <div class=\"charts-placeholder\" > </div>\n <div class=\"charts-selector\" >\n\n </div>\n </div>\n\n </div>\n `;\n this.chartTab = this.hostElement.querySelector('.chart-tab');\n this.textTab = this.hostElement.querySelector('.text-tab');\n this.functionalTabs = this.hostElement.querySelector('.functional-tabs');\n this.chartPanel = this.hostElement.querySelector('.chart-panel');\n this.textPanel = this.hostElement.querySelector('.text-panel');\n this.lattConstantsField = this.hostElement.querySelector('.latt-constants-field');\n this.volumeField = this.hostElement.querySelector('.volume-field');\n this.volumeFieldValue = this.hostElement.querySelector('.volume-value');\n this.densityField = this.hostElement.querySelector('.density-field');\n this.massDensityValue = this.hostElement.querySelector('.mass-density-value');\n this.atomicDensityValue = this.hostElement.querySelector('.atomic-density-value');\n\n this.statsViewer = new StatsViewer();\n let chartsPlaceholder = this.hostElement.querySelector('.charts-placeholder');\n this.statsViewer.attach(chartsPlaceholder, 350, 200);\n\n this.chartsSelector = this.hostElement.querySelector('.charts-selector');\n\n this.chartTab.addEventListener( \"click\", e => {\n this.chartTab.style.fill = '#777';\n this.viewType = 'chart';\n this.textTab.style.fill = '#c7c7c7';\n this.chartPanel.style.display = 'block';\n this.textPanel.style.display = 'none';\n });\n\n this.textTab.addEventListener( \"click\", e => {\n this.textTab.style.fill = '#777';\n this.viewType = 'text';\n this.chartTab.style.fill = '#c7c7c7';\n this.textPanel.style.display = 'block';\n this.chartPanel.style.display = 'none';\n });\n\n this.functionalTabs.addEventListener( \"click\", e => {\n if (e.target.className === 'tab'){\n this.statsViewer.clear();\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]')\n .className = 'tab';\n this.functional = e.target.getAttribute('data-tab');\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]')\n .className = 'tab-selected';\n this._setData();\n }\n });\n\n this.chartsSelector.addEventListener( \"click\", e => {\n if (e.target.className.indexOf('quantity') === 0){\n this.statsViewer.clear();\n let quantity = e.target.getAttribute('data-quantity');\n\n let quantityData = this.quantitiesMap.get(quantity);\n let label = quantityData.label;\n let stats = quantityData.stats;\n this.statsViewer.drawPoints(stats.histogram, label, this.nBins);\n this.chartsSelector.querySelector('.quantity-selected').className = 'quantity';\n e.target.className = 'quantity-selected';\n }\n });\n\n }\n\n /**\n * Called when a functional is selected.\n */\n _setData() {\n let is2Dsystem = (DataStore.getMaterialData().material_type === '2D');\n let isBulk = (DataStore.getMaterialData().material_type === 'bulk');\n let functional = this.functional;\n let calcs = Array.from(this.calcMapByFunctional.get(this.functional));\n let matId = DataStore.getMaterialData().material_id;\n let quantitiesMap = new Map();\n quantitiesMap.set(\"lattice_a\", {label: 'a (Å)'});\n\n if (is2Dsystem | isBulk) {\n quantitiesMap.set(\"lattice_b\", {label: 'b (Å)'});\n quantitiesMap.set(\"gamma\", {label: '&gamma'});\n }\n if (isBulk) {\n quantitiesMap.set(\"cell_volume\", {label: 'Volume (ų)'});\n quantitiesMap.set(\"atomic_density\", {label: 'Atomic density (Å⁻³)'});\n quantitiesMap.set(\"mass_density\", {label: 'Mass density (kg/m³)'});\n quantitiesMap.set(\"lattice_c\", {label: 'c ()'});\n quantitiesMap.set(\"alpha\", {label: '&alpha'});\n quantitiesMap.set(\"beta\", {label: '&beta'});\n }\n\n // Query statistics from the API\n let query = JSON.stringify({\n calculations: calcs,\n properties: Array.from(quantitiesMap.keys()),\n n_histogram_bins: this.nBins,\n });\n util.serverReqPOST(util.getMaterialStatsURL(matId), query, e3 => {\n let results = JSON.parse(e3.target.response);\n\n // Create formatted string for each stat\n for (let quantity of quantitiesMap.keys()) {\n\n let stats = results[quantity];\n\n // Unit conversions\n if (quantity == \"cell_volume\") {\n stats.min *= 1e30;\n stats.avg *= 1e30;\n stats.max *= 1e30;\n stats.histogram.values = stats.histogram.values.map(x => x*1e30);\n } else if (quantity == \"atomic_density\") {\n stats.min *= 1e-30;\n stats.avg *= 1e-30;\n stats.max *= 1e-30;\n stats.histogram.values = stats.histogram.values.map(x => x*1e-30);\n } else if (quantity.startsWith(\"lattice_\")) {\n stats.min *= 1e10;\n stats.avg *= 1e10;\n stats.max *= 1e10;\n stats.histogram.values = stats.histogram.values.map(x => x*1e10);\n }\n let label = quantitiesMap.get(quantity).label;\n stats.equal = (stats.min === stats.max);\n let lls = label.split(':');\n stats.label = lls[0];\n if (lls.length === 2) {\n stats.units = lls[1];\n } else {\n stats.units = '';\n }\n let decimals = 3;\n if (quantity === 'mass_density') decimals = 1;\n let html;\n if (quantity == \"alpha\" || quantity == \"beta\" || quantity == \"gamma\") {\n html = util.rad2degree(stats.avg.toFixed(decimals));\n } else {\n html = stats.avg.toFixed(decimals)+\n ' <span style=\"font-size: 0.9em\">['+stats.min.toFixed(decimals)\n +' , '+stats.max.toFixed(decimals)+']</span>';\n }\n\n quantitiesMap.get(quantity).html = html;\n quantitiesMap.get(quantity).stats = stats;\n };\n\n let lattCFieldHTML = ((is2Dsystem || isBulk) ?\n `<div>b (Å) = ${quantitiesMap.get('lattice_b').html}</div>` : '');\n\n lattCFieldHTML += (isBulk ?\n `<div>c (Å) = ${quantitiesMap.get('lattice_c').html}</div>` : '');\n\n // Construct angle field\n let lattBetaGammaFieldHTML = (isBulk ?\n `<div>α = ${quantitiesMap.get('alpha').html}</div>\n <div>β = ${quantitiesMap.get('beta').html}</div>` : '');\n lattBetaGammaFieldHTML += ((is2Dsystem || isBulk) ?\n `<div>γ = ${quantitiesMap.get('gamma').html}</div>` : '');\n\n // Set text data\n this.lattConstantsField.innerHTML = `\n <div style=\"float: left; \">\n <div>a (Å) = ${quantitiesMap.get('lattice_a').html}</div>\n ${lattCFieldHTML}\n </div>\n <div style=\"float: left; padding-left: 40px;\">\n ${lattBetaGammaFieldHTML}\n </div>\n <div style=\"clear: both;padding: 0\"></div>\n `;\n\n let chartSelectorHTML= `\n <span class=\"quantity-selected\" data-quantity=\"lattice_a\">a</span>\n `;\n\n if (is2Dsystem || isBulk)\n chartSelectorHTML += `<span class=\"quantity\" data-quantity=\"lattice_b\">b</span>`;\n this.densityField.style.display = (isBulk ? 'block' : 'none');\n this.volumeField.style.display = (isBulk ? 'block' : 'none');\n if (isBulk) { // bulk type\n this.volumeFieldValue.innerHTML = quantitiesMap.get('cell_volume').html;\n this.massDensityValue.innerHTML = quantitiesMap.get('mass_density').html;\n this.atomicDensityValue.innerHTML = quantitiesMap.get('atomic_density').html;\n chartSelectorHTML += `\n <span class=\"quantity\" data-quantity=\"lattice_c\">c</span>\n <span class=\"quantity\" data-quantity=\"cell_volume\">volume</span>\n <span class=\"quantity\" data-quantity=\"mass_density\">mass density</span>\n <span class=\"quantity\" data-quantity=\"atomic_density\">atomic density</span>\n `;\n }\n\n this.chartsSelector.innerHTML = chartSelectorHTML;\n this.quantitiesMap = quantitiesMap;\n\n // Display histogram\n let hist = results.lattice_a.histogram;\n this.statsViewer.drawPoints(hist, quantitiesMap.get(\"lattice_a\").label, this.nBins);\n });\n }\n\n build(calcMapByFunctional) {\n this.calcMapByFunctional = calcMapByFunctional;\n this.graphTrigger = null;\n this.statsViewer.clear();\n this.unfoldedElement = null;\n this.functionalQuantityMap = new Map();\n\n // Create tabs for each functional\n this.functionalTabs.innerHTML = \"\";\n this.calcMapByFunctional.forEach( (calcs, functionalName) => {\n this.functionalTabs.innerHTML +=\n '<span class=\"tab\" data-tab=\"'+functionalName+'\">'+functionalName+'</span>';\n });\n\n // Preselect the first functional\n let functionals = Array.from(this.calcMapByFunctional.keys());\n if (this.functional === null || functionals.indexOf(this.functional) < 0)\n this.functional = functionals[0]; // the first one is selected\n this._setData();\n this.functionalTabs.querySelector('[data-tab=\"'+this.functional+'\"]').className = 'tab-selected';\n\n InfoSys.addToInfoSystem(this.hostElement);\n }\n\n}\n\n\n// EXPORTS\nmodule.exports = StructureDetails;\n\n\n//# sourceURL=webpack:///./src/material-mod/StructureDetails.view.js?"); + +/***/ }), + +/***/ "./src/material-mod/ThermalPropsDetails.view.js": +/*!******************************************************!*\ + !*** ./src/material-mod/ThermalPropsDetails.view.js ***! + \******************************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n /*\n 'Details' view container that shows the material calculations info related to\n their vibrational and thermal properties.\n */\n\n\n\nlet DetailsViewBase = __webpack_require__(/*! ./DetailsViewBase.js */ \"./src/material-mod/DetailsViewBase.js\");\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet NavTree = __webpack_require__(/*! ./NavTree.js */ \"./src/material-mod/NavTree.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet HeatCapPlotter = __webpack_require__(/*! ./HeatCapPlotter.js */ \"./src/material-mod/HeatCapPlotter.js\");\nlet HelmholtzPlotter = __webpack_require__(/*! ./HelmholtzPlotter.js */ \"./src/material-mod/HelmholtzPlotter.js\");\nlet PhononDispDOSPlotter = __webpack_require__(/*! ./PhononDispDOSPlotter.js */ \"./src/material-mod/PhononDispDOSPlotter.js\");\n\n\nclass ThermalPropsDetails extends DetailsViewBase{\n\n constructor() {\n super('Thermal Properties');\n this.firstId;\n this.lastId;\n this.navTree = new NavTree();\n this.element.innerHTML+=`\n <div style=\"float: left; width: 30%;\">\n <div class=\"view-box\">\n <div class=\"title\">Calculations </div>\n <div class=\"navTreeWrapper\"></div>\n </div>\n </div>\n\n <div style=\"float: right; width: 70%;\">\n <div class=\"view-box thermal-properties-box\">\n\n <div class=\"title\">Vibrational and thermal properties</div>\n\n <div style=\"padding-top: 10px;\">\n <div class=\"tree-leaf-title\"></div>\n </div>\n\n <div class=\"calc-disp-dos-plotter\" style=\"padding: 30px 100px; \">\n <div class=\"info-fields-label\" style=\"float: left; width: 52%; \">\n <span info-sys-data=\"phonon-dispersion\">Phonon dispersion </span>\n </div>\n <div class=\"info-fields-label\" style=\"float: left;\">\n <span info-sys-data=\"phonon-DOS\">Phonon DOS </span>\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n\n <div class=\"band\" >\n <div style=\"padding: 30px 50px; display: flex; justify-content: space-around; \">\n\n <div >\n <div class=\"info-fields-label\" >\n <span info-sys-data=\"specific-heat-cv\">Specific heat</span>\n </div>\n <div class=\"heat-plotter\" > </div>\n </div>\n\n <div>\n <div class=\"info-fields-label\" >\n <span info-sys-data=\"helmholtz-free-energy\">Helmholtz free energy</span>\n </div>\n <div class=\"helmholtz-plotter\" > </div>\n </div>\n\n </div>\n </div>\n\n </div>\n </div>\n `;\n\n this.navTreeWrapper =\n this.element.getElementsByClassName(\"navTreeWrapper\")[0];\n\n this.rightBox = this.element.querySelector('.thermal-properties-box');\n this.leafTitle = this.element.querySelector('.tree-leaf-title');\n\n this.dispDosPlotter = new PhononDispDOSPlotter();\n this.heatPlotter= new HeatCapPlotter();\n this.helmholtzPlotter= new HelmholtzPlotter();\n\n InfoSys.addToInfoSystem(this.element);\n }\n\n\n _events() {\n super._events();\n }\n\n setMaterialData() {\n let data = DataStore.getMaterialData();\n super.setMaterialData(data);\n\n // Build the navigation tree and attach listeners\n let name = (data.material_name === null ?\n data.formula : data.material_name);\n this.navTree.build(name, \"thermal\");\n this.navTree.selectAll();\n this.navTree.setHeight(600);\n this.navTree.setMarkedLeafIfNoneMarked(null);\n this.attachNavTree(this.navTree);\n this.updateSelection(this.navTree.getTreeSelectedCalcs());\n this.updateMarkedLeaf(this.navTree.getMarkedLeaf());\n this.navTree.setTreeSelectionListener( leafIds => {\n this.updateSelection(leafIds);\n });\n this.navTree.setLeafMarkedListener( leafId => {\n this.updateMarkedLeaf(leafId);\n });\n }\n\n /**\n * Does not do anything for now.\n */\n updateSelection(leafIds) {\n }\n\n updateMarkedLeaf(leafId){\n if (leafId === null) {\n this.leafTitle.innerHTML = 'NO SELECTION';\n this.dispDosPlotter.setNoData();\n this.heatPlotter.setNoData();\n this.helmholtzPlotter.setNoData();\n return;\n } else {\n this.leafTitle.innerHTML = util.getShortCode(leafId);\n }\n\n let calc = DataStore.getCalc(leafId);\n if (!this.dispDosPlotter.isAttached()) {\n this.dispDosPlotter.attach(this.element.querySelector('.calc-disp-dos-plotter'),undefined,360);\n this.heatPlotter.attach(this.element.querySelector('.heat-plotter'),317,317);\n this.helmholtzPlotter.attach(this.element.querySelector('.helmholtz-plotter'),317,317);\n }\n LoadingPopup.show(\"thermal_properties\");\n let matId = DataStore.getMaterialData().material_id;\n\n // Request thermal properties for this calculation\n let url = util.getMaterialCalcURL(matId, calc.calc_id);\n let query = JSON.stringify({properties: [\"phonon_band_structure\", \"phonon_dos\", \"thermodynamical_properties\"]});\n util.serverReqPOST(url, query, e => {\n let response = JSON.parse(e.target.response);\n let dosData = response.phonon_dos;\n let dispersionData= response.phonon_band_structure;\n let thermoData = response.thermodynamical_properties;\n if (dosData !== undefined) {\n this.dispDosPlotter.setUpAndData(dispersionData, dosData);\n } else {\n this.dispDosPlotter.setNoData();\n }\n if (thermoData !== undefined) {\n let t = thermoData.thermodynamical_property_temperature;\n this.heatPlotter.setData(t, thermoData.specific_heat_capacity);\n this.helmholtzPlotter.setData(t, thermoData.specific_vibrational_free_energy_at_constant_volume);\n } else {\n this.heatPlotter.setNoData();\n this.helmholtzPlotter.setNoData();\n }\n document.getElementById('thermal-props-ov').style.visibility = 'visible';\n LoadingPopup.hide(\"thermal_properties\");\n });\n }\n\n\n setPrevCalcListener(listener){\n this.prevCalcListener= listener;\n }\n\n\n setNextCalcListener(listener){\n this.nextCalcListener= listener;\n }\n\n}\n\n// EXPORTS\nmodule.exports = ThermalPropsDetails;\n\n\n//# sourceURL=webpack:///./src/material-mod/ThermalPropsDetails.view.js?"); + +/***/ }), + +/***/ "./src/search-mod/ElemenTable.view.js": +/*!********************************************!*\ + !*** ./src/search-mod/ElemenTable.view.js ***! + \********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is the UI component implementing the interactive Element Table\n in the search front. It need to communicate to other UI components\n so it exposes several listeners\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\n\n// constans\n\nconst GROUPS = new Map([\n [1,['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n [2,['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n [3,['Sc', 'Y']],\n [4,['Ti', 'Zr', 'Hf', 'Rf']],\n [5,['V', 'Nb', 'Ta', 'Ha']],\n [6,['Cr', 'Mo', 'W', 'Sg']],\n [7,['Mn', 'Tc', 'Re', 'Ns']],\n [8,['Fe', 'Ru', 'Os', 'Hs']],\n [9,['Co', 'Rh', 'Ir', 'Mt']],\n [10,['Ni', 'Pd', 'Pt', 'Ds']],\n [11,['Cu', 'Ag', 'Au', 'Rg']],\n [12,['Zn', 'Cd', 'Hg', 'Cn']],\n [13,['B', 'Al', 'Ga', 'In', 'Tl', 'Nh']],\n [14,['C', 'Si', 'Ge', 'Sn', 'Pb', 'Fl']],\n [15,['N', 'P', 'As', 'Sb', 'Bi', 'Mc']],\n [16,['O', 'S', 'Se', 'Te', 'Po', 'Lv']],\n [17,['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n [18,['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n [19,['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu']],\n [20,['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr']]\n]);\n\nconst BLOCKS = new Map([\n ['metalloids',['B', 'Si', 'Ge', 'As', 'Sb', 'Te', 'Po']],\n ['other-non-metals',['H', 'C', 'N', 'O', 'P', 'S', 'Se']],\n ['halogens',['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n ['noble-gases',['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n ['alkali-metals',['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n ['alkaline-earth-metals',['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n ['lanthanoids',['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er'\n ,'Tm','Yb','Lu']],\n ['actinoids',['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm'\n ,'Md','No','Lr']],\n ['transition-metals', ['Sc', 'Y','Ti', 'Zr', 'Hf', 'Rf','V','Nb','Ta','Ha'\n ,'Cr','Mo','W','Sg','Mn','Tc','Re','Ns','Fe','Ru','Os','Hs','Co','Rh','Ir'\n , 'Mt','Ni','Pd','Pt', 'Ds', 'Cu','Ag','Au', 'Rg', 'Zn','Cd','Hg', 'Cn']],\n ['post-transition-metals', ['Al','Ga', 'In', 'Tl', 'Nh', 'Sn', 'Pb', 'Fl', 'Bi', 'Mc', 'Lv']]\n]);\n\nconst BLOCKS_COLORS = new Map([\n ['metalloids','#F9E298'],\n ['other-non-metals','#F2B01D'],\n ['halogens','#85ADC1'],\n ['noble-gases','#F7D660'],\n ['alkali-metals','#D04629'],\n ['alkaline-earth-metals','#F7B57D'],\n ['transition-metals', '#F58737'],\n ['post-transition-metals', '#AE4747'],\n ['lanthanoids','#3B91AE'],\n ['actinoids','#E97147']\n]);\n\nlet elementNames = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium',\n 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine',\n 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon',\n 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium',\n 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium',\n 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper',\n 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium',\n 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium',\n 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium',\n 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium',\n 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon',\n 'Cesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium',\n 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium',\n 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium',\n 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten',\n 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold',\n 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium',\n 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium',\n 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium',\n 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium',\n 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium',\n 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium',\n 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium',\n 'Livermorium', 'Tennessine', 'Oganesson'\n];\n\n\n// utility functions\n\nfunction getElementBlock(elSymbol){\n let block;\n BLOCKS.forEach(function(value, key) {\n //console.log(key + \" \" + value);\n if (value.indexOf(elSymbol) >= 0)\n block= key;\n });\n return block;\n}\n\n\nlet CELL_WIDTH= 36;\n\nfunction getCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cell '+getElementBlock(elSymbol)+'\" data-el=\"el-'+elSymbol+'\">'+\n '<b>'+elSymbol+'</b> <div>'+elNum+'</div> </td>';\n}\n\nfunction getOddCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cellpad '+getElementBlock(elSymbol)+'\" data-el=\"el-X\">'+\n '<b> </b> <div> </div> </td>';\n}\n\n\n/** Group selection not implemented\n\nfunction getGroupSelectorHtml(num){\n return '<th class=\"group-sel\" data-group=\"'+num+'\"><div></div></th>';\n}\n\nfunction getGroupSelectorHtmlLAcAc(num){\n return '<th class=\"group-sel-la-ac\" data-group=\"'+num+'\"><div></div></th>';\n}*/\n\n/************************/\n\n\nclass ElemenTable{\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','elementable');\n\n // header with dropdown\n let tempHtml= '<div class=\"element-info\"></div>';\n\n tempHtml+= '<div class=\"ptWrapper\">';\n\n tempHtml+= '<table id=\"pt-main\">'; // table zone div\n\n /* Deactivated for the moment\n // header with group selectors\n tempHtml+= '<thead><tr>';//'<div id=\"group-selectors\">';\n for (let i= 1; i<=18; i++){\n tempHtml+= getGroupSelectorHtml(i);\n }\n tempHtml+= '</tr></thead>';// selectors\n */\n\n tempHtml+= '<tbody>';\n // row 1\n tempHtml+= '<tr>'+getCellHtml(1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"16\"></td>';\n tempHtml+= getCellHtml(2)+'</tr>';\n\n let get8ElementRowHtml= (initPos) => {\n tempHtml+= '<tr>'+getCellHtml(initPos)+getCellHtml(initPos+1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"10\"></td>';\n for (let i= initPos+2; i< initPos+8; i++) tempHtml+= getCellHtml(i);\n tempHtml+= '</tr>';//div.row\n }\n\n // row 2 and 3\n get8ElementRowHtml(3);\n get8ElementRowHtml(11);\n\n // row 4,5, 6\n let counter= 19;\n for (let i= 0; i<4; i++){\n tempHtml+= '<tr>';\n for (let j= 0; j<18; j++){\n if (counter === 57 || counter === 89){\n tempHtml += getOddCellHtml(counter);\n counter += 15;\n }else{\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n }\n tempHtml+= '</tr>';//div.row\n }\n\n\n tempHtml+= '</tbody></table>';\n\n // Lanthanides and Actinides\n tempHtml+= '<div id=\"specialRows\"><table id=\"pt-laac\">';\n for (let i= 0; i<2; i++){\n tempHtml+= '<tr>';\n counter = (i === 0 ? 57 : 89);\n //tempHtml+= getGroupSelectorHtmlLAcAc((i === 0 ? 19 : 20));\n for (let j= 0; j<15; j++){\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n tempHtml+= '</tr>';//div.row\n }\n tempHtml+= '</table></div>'; //div#specialRows\n\n\n // Block labels\n tempHtml+= `<div class=\"legend\">\n <div class=\"alkali-metals\">Alkali metals</div>\n <div class=\"alkaline-earth-metals\">Alkaline earth metals</div>\n <div class=\"transition-metals\">Transition metals</div>\n <div class=\"post-transition-metals\">Post-transition metals</div>\n <div class=\"metalloids\">Metalloids</div>\n <div class=\"other-non-metals\">Other nonmetals</div>\n <div class=\"halogens\">Halogens</div>\n <div class=\"noble-gases\">Noble gases</div>\n <div class=\"lanthanoids\">Lanthanoids</div>\n <div class=\"actinoids\">Actinoids</div>\n </div>`;//'<div id=\"group-selectors\">';\n\n\n tempHtml+= '</div>'; // ptWrapper\n\n this.element.innerHTML= tempHtml;\n this.elementInfo= this.element.getElementsByClassName('element-info')[0];\n this.tableZone= this.element.getElementsByClassName('ptWrapper')[0];\n //this.specialRows= this.element.querySelector('#specialRows');\n\n this._events();\n }\n\n\n _events() {\n\n // One listener for all diferent clicks (simple element, group)\n var adhocListener= (e) => {\n\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let element = e.target;\n if (className === ''){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (className.indexOf('cellpad') >= 0) return; // structural empty table cells\n\n if (className.indexOf('group-sel') >= 0){ // group selector cells\n //*** Not working at the moment - group selection deactivated\n let elements= GROUPS.get(parseInt(element.getAttribute('data-group')));\n //console.log(\"group-sel \"+elements);\n this.clickListener(elements);//let done =\n //if (done)\n for (var i = 0; i < elements.length; i++)\n this.selectElement(elements[i]);\n\n }else if (className.indexOf('cell') >= 0){ // element cells\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === ' ') return; // blank cells\n //console.log(\"elSymbol-sel \"+elSymbol);\n if (className.indexOf('el-selected') >= 0){ // If selected\n this.deselectListener(elSymbol);//this.deselectElement(elSymbol);\n }else{ // If not selected\n this.clickListener([elSymbol]);//let done = this.clickListener([elSymbol]);\n this.selectElement(elSymbol);//if (done) this.selectElement(elSymbol);\n }\n }\n }\n //e.stopPropagation();\n };\n\n // Event listener set in the root div element\n this.tableZone.addEventListener('click',adhocListener,true);\n\n this.tableZone.addEventListener('mouseover',e => {\n\n let elSymbol= getElement(e);\n //console.log(\"ENTERIG elSymbol-sel \"+elSymbol);\n if (elSymbol !== null){\n //console.log(\"elSymbol-sel \"+elSymbol);\n this.elementInfo.style.display = 'block';\n let borderColor= BLOCKS_COLORS.get(getElementBlock(elSymbol));\n this.elementInfo.style.borderColor = borderColor;\n let number= util.ELEMENTS.indexOf(elSymbol)+1;\n this.elementInfo.innerHTML= `\n <div>\n <div style=\"float: right; padding: 3px 4px;border-left: 3px solid ${borderColor};\n border-bottom: 3px solid ${borderColor}\" > ${number} </div>\n <div style=\"clear: right;\"></div>\n </div>\n <div class=\"symbol\">${elSymbol} </div>\n <div class=\"\">${elementNames[number-1]} </div>\n `\n }\n });\n\n this.tableZone.addEventListener('mouseout',e => {\n let element= getElement(e);\n if (element !== null) this.elementInfo.style.display = 'none';\n });\n\n }\n\n // Observer pattern\n setClickListener(listener) {\n this.clickListener= listener;\n }\n\n\n setDeselectListener(listener) {\n this.deselectListener= listener;\n }\n\n\n selectElement(elSymbol) {\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell el-selected';\n }\n\n\n deselectElement(elSymbol) {\n //document.getElementById('el-'+elSymbol).className= 'cell '+getElementBlock(elSymbol);\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell '+getElementBlock(elSymbol);\n }\n\n\n deselectAllElements(){\n let selectedElements = this.element.querySelectorAll('td.el-selected');\n\n //selectedElements.forEach( element => {\n for (let i = 0; i < selectedElements.length; ++i){\n let elSymbol = selectedElements[i].getAttribute('data-el').substring(3);\n selectedElements[i].className= 'cell '+getElementBlock(elSymbol);\n }\n }\n\n} // class ElemenTable\n\n\nfunction getElement(e){\n\n let element = null;\n let className = null;\n //console.log(\"TARGET \" +e.target.className+' '+e.target.innerHTML+' ');\n if (e.target.className.indexOf('cell ') >= 0){\n element= e.target;\n className = e.target.className;\n\n }else if (e.target.parentElement.className.indexOf('cell ') >= 0){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (element === null) return null;\n else{\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === ' ') return null; // blank cells\n else return elSymbol;\n }\n}\n\n// EXPORTS\nmodule.exports = ElemenTable;\n\n\n//# sourceURL=webpack:///./src/search-mod/ElemenTable.view.js?"); + +/***/ }), + +/***/ "./src/search-mod/MaterialList.view.js": +/*!*********************************************!*\ + !*** ./src/search-mod/MaterialList.view.js ***! + \*********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component implements the list of materials found in the search\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nconst RESULTS_PER_PAGE = 25;\n\n\nclass MaterialList {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'matlist');\n this.formula = false; // **** Maybe this field can be removed\n this.matMap = new Map();\n this.page = 0;\n this.nPages = 0;\n this.before = null;\n this.afters = new Map();\n this.searchJson = null;\n this.element.innerHTML=\n `\n <div class=\"title\"> Results <span class=\"n-materials\"></span></div>\n <div class=\"pag-header\">\n <span class=\"prevButton\">\n <img src=\"img/prev.svg\" style=\"display: inline;\" width=\"7px\"/> prev\n </span> \n <span class=\"page\"> X </span> \n <span class=\"nextButton\"> next \n <img src=\"img/next.svg\" width=\"7px\" />\n </span>\n </div>\n <div class=\"data-container\"> </div>`; // results data container\n\n this.titleBox = this.element.getElementsByClassName(\"title\")[0];\n this.nMaterials = this.element.getElementsByClassName(\"n-materials\")[0];\n // Pagination components\n this.resultsNrTag = this.element.getElementsByClassName(\"pag-header\")[0];\n\n this.prevButton = this.element.getElementsByClassName(\"prevButton\")[0];\n this.pageElement = this.element.getElementsByClassName(\"page\")[0];\n this.nextButton = this.element.getElementsByClassName(\"nextButton\")[0];\n this.resultsContainer = this.element.getElementsByClassName(\"data-container\")[0];\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._events();\n }\n\n _events() {\n\n this.nextButton.addEventListener('click', e => {\n this.page++;\n this._search();\n });\n\n this.prevButton.addEventListener('click', e => {\n if (this.page === 1) return;\n this.page--;\n this._search();\n });\n\n this.resultsContainer.addEventListener('click', (e) => {\n if (e.target !== e.currentTarget) { // When the event source is a child\n\n let element;\n if (e.target.className === 'mat-row') element = e.target;\n else if (e.target.parentElement.className === 'mat-row')\n element = e.target.parentElement;\n else if (e.target.parentElement.parentElement.className === 'mat-row')\n element = e.target.parentElement.parentElement;\n\n if (element) {\n util.setBrowserHashPath('material', element.getAttribute('data-mat-id'));\n }\n e.stopPropagation();\n }\n });\n\n }\n\n _printMatMap(){console.log('MATMAP:');\n this.matMap.forEach(function(value, key, ownerMap) {\n console.log(key + ' '+JSON.stringify(value));\n });\n }\n\n setSearch(searchJson){\n this.resultsContainer.style.visibility = 'hidden';\n this.searchJson = searchJson;\n this.page = 1;\n this._search();\n }\n\n /**\n *\n */\n _search() {\n this.searchJson.search_by.page = this.page;\n this.searchJson.search_by.after = this.afters.get(this.page);\n this.searchJson.search_by.per_page = 10;\n\n let postQuery = JSON.stringify(this.searchJson);\n \n // Hide or show previous page button on first page\n if (this.page === 1) {\n this.prevButton.style.visibility = \"hidden\";\n } else {\n this.prevButton.style.visibility = \"visible\";\n }\n\n LoadingPopup.reset();\n window.allowNewLoadPopup = true;\n LoadingPopup.show();\n let oReq = util.serverReqPOST(util.getSearchURL(), postQuery, e => {\n let data = JSON.parse(e.target.response);\n\n if (e.target.status === 200) {\n // Store the after key for the next query\n this.afters.set(this.page+1, data.pages.after);\n\n // The number of results is set only on the first page, as it is not\n // returned if after key is provided.\n if (this.page === 1) {\n this.nMaterials.innerHTML = \"(total: \" + util.numberWithCommas(data.pages.total) + \")\";\n this.nPages = Math.ceil(data.pages.total / this.searchJson.search_by.per_page);\n }\n\n // Hide next button if no more results to show.\n let nResults = data.results.length;\n if (nResults === 0 || this.page === this.nPages) {\n this.nextButton.style.visibility = \"hidden\";\n } else {\n this.nextButton.style.visibility = \"visible\";\n }\n\n let matData = data.results;\n\n if (data.results.length === 0) {\n this.setData([]);\n util.searchResults = false;\n } else if (data.results.length === 1) {\n util.setBrowserHashPath('material', matData[0].material_id);\n util.searchResults = false;\n } else {\n this.setData(matData);\n util.searchResults = true;\n }\n\n } else { // Right query - results not found\n this.setData([]);\n this.nextButton.style.visibility = \"hidden\";\n }\n this._updateUI();\n this.resultsContainer.style.visibility = 'visible';\n LoadingPopup.hide();\n });\n\n oReq.addEventListener(\"error\", e => { // Not valid query\n console.log('Search ERROR - Not valid query ');\n this.setData([]);\n this._updateUI();\n this.resultsContainer.style.visibility = 'visible';\n LoadingPopup.hide();\n });\n\n }\n\n\n setData(data){\n this.matMap.clear();\n data.forEach( mat => {\n if (this.matMap.has(mat.formula_reduced)){\n let matArray= this.matMap.get(mat.formula_reduced);\n matArray.push(mat);\n } else {\n let newArray= []; newArray.push(mat);\n this.matMap.set(mat.formula_reduced, newArray);\n }\n });\n }\n\n\n reset() {\n this.formula= false;\n this.matMap.clear();\n this.page= 0;\n this.searchJson= null;\n this._updateUI();\n }\n\n\n _updateUI() {\n\n this.pageElement.innerHTML= 'page ' + this.page + \"/\" + util.numberWithCommas(this.nPages);\n let html = '';\n\n if (this.matMap.size === 0) {\n if (this.page === 1) {\n this.resultsNrTag.style.display = 'none';\n this.titleBox.style.display = 'none';\n }\n html+= `<div class=\"not-found\">No results found</div>`;\n } else {\n this.resultsNrTag.style.display = 'block';\n this.titleBox.style.display = 'block';\n html +=`\n <table>\n <thead> <tr>\n <th style=\"width: 24%;\"></th>\n <th style=\"width: 20%;\">\n <span info-sys-data=\"material-type\">Material type</span>\n </th>\n <th style=\"width: 16%;\">\n <span info-sys-data=\"space-group\">Space group</span>\n </th>\n <th style=\"width: 22%;\">\n <span info-sys-data=\"structure-type\">Structure type</span>\n </th>\n <th style=\"width: 18%;\">Nº calculations</th>\n </tr> </thead>\n <tbody>\n `;\n\n this.matMap.forEach((mats, formula) => {\n\n let rFormula = util.getSubscriptedFormula(formula);\n html+= '<tr> <td class=\"formula\" colspan=\"5\"><b>'+rFormula+'</b>';\n if ( mats.length > 1)\n html += '<span style=\"font-size: 0.86em;\"> ('+mats.length+' structures)</span>';\n html += '</td></tr>';\n\n mats.forEach( mat => {\n let label= (mat.material_name !== undefined ? mat.material_name : rFormula);\n html+=\n `<tr class=\"mat-row\" data-mat-id=\"${mat.material_id}\">\n <td > ${label} </td>\n <td style=\"text-align:center\" >\n <!--<span info-sys-data=\"material-type\">-->${mat.material_type} <!--</span>-->\n </td>\n <td style=\"text-align:center\" >\n ${mat.space_group_number === undefined ? '' : mat.space_group_number}\n </td>\n <td> ${mat.structure_type === undefined ? '' : mat.structure_type } </td>\n <td style=\"text-align:center\" > ${mat.n_calculations} </td>\n </tr>`;\n });\n });\n\n html +=` </tbody> </table>`;\n }\n\n this.resultsContainer.innerHTML = html;\n\n InfoSys.addToInfoSystem(this.resultsContainer);\n }\n\n}\n\nmodule.exports = MaterialList;\n\n\n//# sourceURL=webpack:///./src/search-mod/MaterialList.view.js?"); + +/***/ }), + +/***/ "./src/search-mod/PropertiesBox.view.js": +/*!**********************************************!*\ + !*** ./src/search-mod/PropertiesBox.view.js ***! + \**********************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component implements the UI box (Properties) that allows add filters\n to the search\n */\n\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\n\n\n\nclass PropertiesBox {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'search-properties-box');\n this.element.innerHTML=\n `\n <div class=\"props-box-tabs-wrapper\" style=\"float: left; width: 25%; \">\n\n <div class=\"properties-box-tabs\" >\n <div data-tab=\"structure\" class=\"props-tab-selected\" >Structure </div>\n <div data-tab=\"results\">Properties </div>\n <div data-tab=\"method\">Method </div>\n <!-- <div class=\"contributors-tab\">Contributors </div> -->\n </div>\n\n </div>\n\n <div class=\"properties-box-panel\" style=\"float: left;width: 75%;height: 100%\">\n\n <div class=\"props-tab-panel-wrapper\" style=\"height: 86%\" >\n\n <div class=\"structure-panel props-tab-panel\" style=\"display: block\">\n\n\n <div style=\"float: left; width: 47%;\">\n\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"space-group\">Space group number</span></div>\n <input type=\"text\" class=\"space-group-number-field\">\n </div>\n\n <div class=\"field\">\n <div class=\"field-title\">Material type</div>\n <input type=\"checkbox\" class=\"material-type-field\" value=\"bulk\"> Bulk<br>\n <input type=\"checkbox\" class=\"material-type-field\" value=\"2D\"> 2D<br>\n <input type=\"checkbox\" class=\"material-type-field\" value=\"1D\"> 1D<br>\n </div>\n\n <!--\n <div class=\"field\">\n <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>\n Min: <input type=\"text\" class=\"mass-density-min-field\"> \n Max: <input type=\"text\" class=\"mass-density-max-field\">\n </div>\n -->\n\n </div>\n\n <div style=\"float: left; width: 5%;\"></div>\n\n <div style=\"float: right; width: 47%;\">\n\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"structure-type\">Structure type</span></div>\n <select class=\"structure-type-field\" style=\"max-width: 174px\">\n <option></option>\n </select>\n </div>\n\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"crystal-system\">Crystal system</span></div>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"cubic\">\n <span info-sys-data=\"crystal-system.value:cubic\">Cubic</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"hexagonal\">\n <span info-sys-data=\"crystal-system.value:hexagonal\">Hexagonal</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"trigonal\">\n <span info-sys-data=\"crystal-system.value:trigonal\">Trigonal</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"tetragonal\">\n <span info-sys-data=\"crystal-system.value:tetragonal\">Tetragonal</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"orthorhombic\">\n <span info-sys-data=\"crystal-system.value:orthorhombic\">Orthorhombic</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"monoclinic\">\n <span info-sys-data=\"crystal-system.value:monoclinic\">Monoclinic</span><br>\n <input type=\"checkbox\" class=\"crystal-system-field\" value=\"triclinic\">\n <span info-sys-data=\"crystal-system.value:triclinic\">Triclinic</span><br>\n </div>\n\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"results-panel props-tab-panel\" >\n\n <div class=\"field\" style=\"background-color: #CCC; padding: 10px;\">\n <div style=\"font-weight: bold; padding-bottom: 6px\" >Results containing...</div>\n <div style=\"float: left; widt: 47%;\">\n <input type=\"checkbox\" class=\"has-band-structure-field\" value=\"Band structure\">\n <span info-sys-data=\"has-band-structure\">Band structure</span><br>\n <input type=\"checkbox\" class=\"has-dos-field\" value=\"DOS\">\n <span info-sys-data=\"has-dos\">DOS</span><br>\n <!--\n <input type=\"checkbox\" class=\"has-fermi-surface-field\" value=\"Fermi surface\">\n <span info-sys-data=\"has-fermi-surface\">Fermi surface</span><br>\n -->\n </div>\n <div style=\"float: right; width: 47%;\">\n <input type=\"checkbox\" class=\"has-thermal-properties-field\" value=\"Thermal properties\">\n <span info-sys-data=\"has-thermal-properties\">Thermal properties</span>\n <br>\n <!-- <input type=\"checkbox\" class=\"results-containing-field\" value=\"EOS\" disabled>Equation of state<br>-->\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n <div style=\"float: left; width: 47%;\">\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"band-gap\">Band gap</span> <span style=\"font-weight: normal;\">(eV)</span></div>\n <div style=\"display: flex; height: 20px; align-items: center; margin-top: 5px\">\n <input type=\"text\" class=\"band-gap-min-field\">\n <p style=\"padding: 5px\">-</p>\n <input type=\"text\" class=\"band-gap-max-field\">\n </div>\n </div>\n </div>\n\n <!--<div style=\"float: left; width: 5%;\"> </div> -->\n\n <!--\n <div style=\"float: right; width: 47%;\">\n <div class=\"field\" style=\"padding-top: 28px;\">\n <input type=\"radio\" name=\"band-gap-type\" value=\"d\"> Direct<br>\n <input type=\"radio\" name=\"band-gap-type\" value=\"i\"> Indirect<br>\n <input type=\"radio\" name=\"band-gap-type\" value=\"d/i\" checked> Both<br>\n </div>\n </div>\n -->\n <div style=\"clear: both;\"></div>\n\n </div>\n\n <div class=\"method-panel props-tab-panel\" >\n <div style=\"float: left; width: 47%;\">\n\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"functional-type\">Functional type</span></div>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"LDA\">\n <span info-sys-data=\"functional-type.value:LDA\">LDA</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"GGA\">\n <span info-sys-data=\"functional-type.value:GGA\">GGA</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"meta-GGA\">\n <span info-sys-data=\"functional-type.value:meta-GGA\">meta-GGA</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"hybrid-GGA\">\n <span info-sys-data=\"functional-type.value:hybrid-GGA\">hybrid-GGA</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"hybrid-meta-GGA\">\n <span info-sys-data=\"functional-type.value:hybrid-meta-GGA\">hybrid-meta-GGA</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"HF\">\n <span info-sys-data=\"functional-type.value:HF\">HF</span><br>\n <input type=\"checkbox\" class=\"functional-type-field\" value=\"GW\">\n <span info-sys-data=\"functional-type.value:GW\">GW</span><br>\n </div>\n\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"code-name\">Code name</span></div>\n <select class=\"code-name-field\" >\n <option></option>\n </select>\n </div>\n\n </div>\n\n <div style=\"float: left; width: 5%;\"> </div>\n\n <div style=\"float: right; width: 47%;\">\n <div class=\"field\">\n <div class=\"field-title\"><span info-sys-data=\"basis-set\">Basis set</span></div>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"(L)APW+lo\">\n <span info-sys-data=\"basis-set.value:(L)APW+lo\">(L)APW+lo</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"LCAO\">\n <span info-sys-data=\"basis-set.value:LCAO\">LCAO</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"gaussians\">\n <span info-sys-data=\"basis-set.value:gaussians\">gaussians</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"numeric AOs\">\n <span info-sys-data=\"basis-set.value:numeric AOs\">numeric AOs</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"plane waves\">\n <span info-sys-data=\"basis-set.value:plane waves\">plane waves</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"psinc functions\">\n <span info-sys-data=\"basis-set.value:psinc functions\">psinc functions</span><br>\n <input type=\"checkbox\" class=\"basis-set-field\" value=\"real-space grid\">\n <span info-sys-data=\"basis-set.value:real-space grid\">real-space grid</span><br>\n </div>\n </div>\n\n <div style=\"clear: both;\"></div>\n </div>\n\n </div>\n\n\n <div class=\"properties-box-enter-button\" style=\"height: 14%\">\n <button disabled>Add to query</button>\n </div>\n\n </div>\n\n <div style=\"clear: both;\"></div>\n `;\n this.tabsElement = this.element.querySelector('.properties-box-tabs');\n this.tabSelected = this.element.querySelector('[data-tab=\"structure\"]');\n this.tabPanelSelected = this.element.querySelector('.structure-panel');\n this.addButton = this.element.querySelector('.properties-box-enter-button button');\n\n let codeNameSelect = this.element.querySelector('.code-name-field');\n let r = util.serverReq(util.getSuggestionURL('code_name'), () => {\n JSON.parse(r.response).code_name.forEach( codeName => {\n codeNameSelect.innerHTML += '<option>'+codeName+'</option>';\n });\n });\n\n let structureTypeSelect = this.element.querySelector('.structure-type-field');\n let r1 = util.serverReq(util.getSuggestionURL('structure_type'), () => {\n JSON.parse(r1.response).structure_type.forEach( structureType => {\n structureTypeSelect.innerHTML += '<option>'+structureType+'</option>';\n });\n });\n\n InfoSys.addToInfoSystem(this.element);\n\n/* code for the MaxMinSlider component testing\n this.testSlider = this.element.querySelector('.test-slider');\n console.log(\"TAB: \",this.testSlider);\n this.slider = new MaxMinSlider();\n this.slider.setRange(0,10000);\n this.testSlider.appendChild(this.slider.element);\n*/\n // this.checkbox.checked\n this._events();\n\n // Set up input validation for band gap min and max\n function setInputFilter(textbox, inputFilter) {\n [\"input\", \"keydown\", \"keyup\", \"mousedown\", \"mouseup\", \"select\", \"contextmenu\", \"drop\"].forEach(function(event) {\n textbox.addEventListener(event, function() {\n if (inputFilter(this.value)) {\n this.oldValue = this.value;\n this.oldSelectionStart = this.selectionStart;\n this.oldSelectionEnd = this.selectionEnd;\n } else if (this.hasOwnProperty(\"oldValue\")) {\n this.value = this.oldValue;\n this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);\n } else {\n this.value = \"\";\n }\n });\n });\n }\n setInputFilter(this.element.getElementsByClassName(\"band-gap-min-field\")[0], function(value) {\n return /^\\d*\\.?\\d*$/.test(value); // Allow digits and '.' only, using a RegExp\n });\n setInputFilter(this.element.getElementsByClassName(\"band-gap-max-field\")[0], function(value) {\n return /^\\d*\\.?\\d*$/.test(value); // Allow digits and '.' only, using a RegExp\n });\n }\n\n\n _events() {\n\n this.tabsElement.addEventListener( \"click\", (e) => {\n if (e.target !== e.currentTarget) { // When the event source is a child\n\n if (this.tabSelected !== null) this.tabSelected.className = '';\n this.tabSelected = e.target;\n this.tabSelected.className = 'props-tab-selected';\n\n let tabString = e.target.getAttribute('data-tab');\n //console.log(\"TAB: \"+tabString);\n if (this.tabPanelSelected !== null)\n this.tabPanelSelected.style.display = 'none';\n this.tabPanelSelected = this.element.getElementsByClassName(tabString+'-panel')[0];\n this.tabPanelSelected.style.display = 'block';\n\n this.checkPropsValues();\n }\n });\n\n this.addButton.addEventListener( \"click\", (e) => {\n let propsMap = this.getPropsWithValueFromCurrentTab(true);\n //console.log('addButton.propsMap: ',propsMap);\n this.addPropertiesListener(propsMap);\n this.addButton.disabled = true;\n });\n\n let panel = this.element.querySelector('.properties-box-panel');\n panel.addEventListener( 'input', e => { this.checkPropsValues(); });\n panel.addEventListener( 'change', e => { this.checkPropsValues(); });\n }\n\n\n checkPropsValues(){\n let map = this.getPropsWithValueFromCurrentTab(false);\n this.addButton.disabled = (map.size === 0);\n }\n\n\n getPropsWithValueFromCurrentTab(reset){\n let tabString = this.tabSelected.getAttribute('data-tab');\n //console.log('andButton: ',tabString);\n let propsMap = new Map();\n if (tabString === 'structure'){\n this.addPropsFromTextFields(propsMap,['space-group-number'], reset);\n this.addPropsFromDropdownList(propsMap,['structure-type'], reset);\n this.addPropsFromCheckboxes(propsMap,['material-type', 'crystal-system'], reset);\n// this.addMassDensityProps(propsMap, reset);\n }else if (tabString === 'results'){\n this.addBandgapProps(propsMap, reset);\n this.addPropsFromCheckboxes(propsMap,['has-band-structure', 'has-dos',\n 'has-fermi-surface', 'has-thermal-properties'], reset);\n\n }else if (tabString === 'method'){\n this.addPropsFromCheckboxes(propsMap,['functional-type','basis-set'], reset);\n this.addPropsFromDropdownList(propsMap,['code-name'], reset);\n }//else if (this.tabSelected.className === 'contributors-tab'){\n //this.addPropsFromTextFields(propsMap,['contributors']);}\n return propsMap;\n }\n\n\n addPropsFromTextFields(propsMap,propsArray, reset){\n propsArray.forEach(propName => {\n let field = this.element.querySelector('.'+propName+'-field');\n if (field.value !== ''){\n propsMap.set(propName, [field.value]);\n if (reset) field.value = '';\n }\n });\n }\n\n addPropsFromCheckboxes(propsMap,propsArray, reset){\n propsArray.forEach(propName => {\n let checkboxes = this.element.querySelectorAll('.'+propName+'-field');\n let value = [];\n for (var i = 0; i < checkboxes.length; i++) {\n if (checkboxes[i].checked){\n value.push(checkboxes[i].value);\n if (reset) checkboxes[i].checked = false;\n }\n }\n if (value.length > 0) propsMap.set(propName, value);\n });\n }\n\n addPropsFromDropdownList(propsMap,propsArray, reset){\n propsArray.forEach(propName => {\n let field = this.element.querySelector('.'+propName+'-field');\n let value = field.options[field.selectedIndex].value;\n if (value.length > 2) propsMap.set(propName, [value]);\n if (reset) field.selectedIndex = 0;\n });\n }\n\n addBandgapProps(propsMap, reset){\n let minField = document.querySelector('.band-gap-min-field');\n let maxField = document.querySelector('.band-gap-max-field');\n let fieldName = 'band-gap';\n let label = 'Band Gap';\n let empty = true;\n if (minField.value !== '') {\n empty = false;\n label = minField.value+' < '+label;\n if (reset) minField.value = '';\n }\n\n if (maxField.value !== '') {\n empty = false;\n label += ' < '+maxField.value;\n if (reset) maxField.value = '';\n }\n\n if (!empty) {\n propsMap.set(fieldName, [label]);\n }\n\n //if (label !== 'Band Gap'){\n //let val = document.querySelector('input[name=\"band-gap-type\"]:checked').value;\n //if (val === 'd') fieldName += ':True';\n //else if (val === 'i') fieldName += ':False';\n //propsMap.set(fieldName, [label+' '+val]);\n //}\n }\n\n\n addMassDensityProps(propsMap, reset){\n let minField = document.querySelector('.mass-density-min-field');\n let maxField = document.querySelector('.mass-density-max-field');\n let fieldName = 'mass-density';\n let label = 'Mass Density';\n if (minField.value !== ''){\n label = minField.value+' < '+label;\n fieldName += ':'+minField.value;\n if (reset) minField.value = '';\n }\n if (maxField.value !== ''){\n label += ' < '+maxField.value;\n fieldName += ':'+maxField.value;\n if (reset) maxField.value = '';\n }\n if (label !== 'Mass Density') propsMap.set(fieldName, [label]);\n }\n\n\n setAddPropertiesListener(listener) {\n this.addPropertiesListener= listener;\n }\n\n\n}\n\n// EXPORTS\nmodule.exports = PropertiesBox;\n\n\n\n/* To be implemented in the future\n\nclass MaxMinSlider{\n\n constructor(){\n\n this.element = document.createElement('div');\n this.element.innerHTML = `\n <svg class=\"maxminslider\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100px\" height=\"40px\"\n viewBox=\"0 0 100 40\" >\n <line class=\"slider-bar\" x1=\"10\" x2=\"90\" y1=\"30\" y2=\"30\" stroke=\"blue\"\n stroke-width=\"4\"/>\n <circle class=\"min-btn\" cx=\"10\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"min-text maxminslider-text\" x=\"10\" y=\"10\" text-anchor=\"start\"></text>\n <circle class=\"max-btn\" cx=\"90\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"max-text maxminslider-text\" x=\"90\" y=\"10\" text-anchor=\"end\"></text>\n </svg>\n `;\n\n //this.bar = this.element.querySelector('.slider-bar');\n this.svg = this.element.querySelector('svg');\n this.minButton = this.element.querySelector('.min-btn');\n this.minText = this.element.querySelector('.min-text');\n this.maxButton = this.element.querySelector('.max-btn');\n this.maxText = this.element.querySelector('.max-text');\n\n this.BUTTON_R = 6;\n\n this.minButtonDown = false;\n this.minButtonInitX = null;\n\n this.MIN_BUTTON_INIT_X = 10;\n this.minX = 0;\n\n this.maxButtonDown = false;\n this.maxButtonInitX = null;\n this.MAX_VALUE = 80;//this.MAX_BUTTON_INIT_X = 90;\n this.maxX = this.MAX_VALUE;\n\n console.log('minButton', this.minButton.getBoundingClientRect());\n\n this._events();\n }\n\n\n _events() {\n\n this.minButton.addEventListener( \"mousedown\", e => this.minButtonDown = true );\n this.minButton.addEventListener( \"mouseup\", e => this.minButtonDown = false );\n this.minButton.addEventListener( \"mouseleave\", e => this.minButtonDown = false );\n\n this.minButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n if (this.minButtonInitX === null){\n //this.minButtonInitX = this.svg.getBoundingClientRect().left;\n this.minButtonInitX = this.minButton.getBoundingClientRect().left+this.BUTTON_R;//\n //console.log('left', this.minButtonInitX);\n }\n\n if (this.minButtonDown){\n this.minX = e.clientX-this.minButtonInitX ;\n if (this.minX > 0 && this.minX < this.maxX-this.BUTTON_R){\n this.minButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.minX);\n this.minText.textContent = this.minX*this.factor-250;\n }\n\n }\n });\n\n\n this.maxButton.addEventListener( \"mousedown\", e => this.maxButtonDown = true );\n this.maxButton.addEventListener( \"mouseup\", e => this.maxButtonDown = false );\n this.maxButton.addEventListener( \"mouseleave\", e => this.maxButtonDown = false );\n\n this.maxButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n\n if (this.maxButtonInitX === null)\n this.maxButtonInitX = this.maxButton.getBoundingClientRect().left+this.BUTTON_R;//\n\n if (this.maxButtonDown){\n\n this.maxX = e.clientX - this.minButtonInitX;\n //console.log('maxButton', e.clientX, this.maxButtonInitX, this.maxX);\n if (this.maxX < this.MAX_VALUE && this.minX+this.BUTTON_R < this.maxX){\n this.maxButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.maxX);\n this.maxText.textContent = this.maxX*this.factor;\n }\n\n }\n\n });\n\n\n }\n\n\n setRange(min, max){\n this.factor = (max-min)/80;\n }\n\n}\n\n*/\n\n\n//# sourceURL=webpack:///./src/search-mod/PropertiesBox.view.js?"); + +/***/ }), + +/***/ "./src/search-mod/SearchMod.js": +/*!*************************************!*\ + !*** ./src/search-mod/SearchMod.js ***! + \*************************************/ +/***/ ((module, __unused_webpack_exports, __webpack_require__) => { + +"use strict"; +eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the Search Module of the application.\n It's a container UI component that shows the seach part of the application:\n front search interface and search results page.\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet UserGuidance = __webpack_require__(/*! ../common/UserGuidance.js */ \"./src/common/UserGuidance.js\");\nlet ElementTable = __webpack_require__(/*! ./ElemenTable.view.js */ \"./src/search-mod/ElemenTable.view.js\");\nlet MaterialList = __webpack_require__(/*! ./MaterialList.view.js */ \"./src/search-mod/MaterialList.view.js\");\nlet PropertiesBox = __webpack_require__(/*! ./PropertiesBox.view.js */ \"./src/search-mod/PropertiesBox.view.js\");\n\n\n// local utility functions\nfunction getTagHtml(tag, isFormula){\n return `<span class=\"search-label\" data-el=\"${tag}\" >\n <img src=\"img/tag.svg\" height=\"16px\" class=\"remove-label\"\n style=\"vertical-align: bottom\"/>\n ${isFormula ? util.getSubscriptedFormula(tag) : tag}\n <img src=\"img/cross.svg\" height=\"6px\" class=\"remove-label\"\n style=\"vertical-align: middle; padding: 4px 3px 6px 5px;\" />\n </span>`;\n}\n\n\nfunction replaceDashes(s){\n return s.split('-').join('_');\n}\n\n\n\nclass SearchMod {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'search-module');\n this.element.innerHTML=\n `\n <div class=\"search-page\">\n\n <div class=\"searchline\" style=\"visibility: visible\">\n <div class=\"search-query-wrapper\" style=\"float: left;\">\n <div class=\"search-query-box\" style=\"float: left;\">\n </div>\n <button class=\"clean-btn\" style=\"float: right;\">Clear all</button>\n </div>\n\n <button style=\"float: right\" class=\"search-btn\" >Search</button>\n <div style=\"clear: both;\"></div>\n <div style=\"display: flex\">\n <div class=\"perm-tooltip search-option\" style=\"margin-right: 10px\">\n <input id=\"exclusive-search\" name=\"exclusive-search\" type=\"checkbox\" >\n <label for=\"exclusive-search\" class=\"perm-tooltip\">Exclusive elements</label>\n <span class=\"tooltiptext\">If selected, the query will return materials with a composition that exclusively contains the selected atoms.</span>\n </div>\n <div class=\"perm-tooltip search-option\">\n <input id=\"restricted-search\" name=\"restricted-search\" type=\"checkbox\" >\n <label for=\"restricted-search\" class=\"perm-tooltip\">Restrict to individual calculations</label>\n <span class=\"tooltiptext\">If selected, the query will return materials that have individual calculations matching all your search criteria.</span>\n </div>\n </div>\n </div>\n\n\n <div class=\"add-buttons\" style=\"display: flex\">\n <div class=\"tab-buttons\" style=\"width: 70%\">\n <button class=\"element-add-btn\" id=\"add-tab-selected\">Element</button>\n <button class=\"formula-add-btn\" style=\"padding: 10px 20px;\" >Formula/Material</button>\n <button class=\"props-add-btn\" >Properties</button>\n </div>\n <!--<div class=\"bool-buttons\" style=\"width: 28%; display: inline-block\" >\n <button disabled >AND</button>\n <button disabled >OR</button>\n <button disabled >NOT</button>\n <button disabled >(</button> <button disabled >)</button>\n </div> -->\n </div>\n\n\n <div>\n <div class=\"triangle element-tri\" style=\"margin: -10px 64px; visibility: visible\"></div>\n <div class=\"triangle formula-tri\" style=\"margin: -10px 30px; visibility: hidden\"></div>\n <div class=\"triangle props-tri\" style=\"margin: -10px 70px; visibility: hidden\"></div>\n </div>\n\n\n <div class=\"add-panel\">\n </div>\n\n </div> <!-- search-page -->\n\n\n <div class=\"results-page\" style=\"display: none\">\n </div>\n\n `;\n\n this.searchPage = this.element.querySelector('.search-page');\n this.searchQueryBox= this.element.getElementsByClassName(\"search-query-box\")[0];\n this.searchLine = this.element.querySelector('.searchline');\n\n this.mainButton= this.element.querySelector('.search-btn');\n this.cleanButton= this.element.querySelector('.clean-btn');\n this.addButtonsBox= this.element.querySelector('.add-buttons');\n this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn');\n this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn');\n\n this.addPanel= this.element.querySelector('.add-panel');\n\n this.elementTable= new ElementTable();\n this.elementTable.setClickListener(elementArray => {\n this.addElementsInSearchQuery(elementArray);\n this.addFormulaButton.disabled = true; // Not always necessary but it simplifies the code\n });\n this.elementTable.setDeselectListener(e => this.removeElementORFormulaInSearchQuery(e));\n\n this.propertiesBox= new PropertiesBox();\n this.propertiesBox.setAddPropertiesListener(propsMap => {\n this.addPropertiesInSearchQuery(propsMap);\n });\n\n this.formulaBox = new FormulaBox();\n this.formulaBox.setAddFormulaListener(formula => {\n if (formula.trim() !== ''){\n this.addTagInSearchQuery(formula, 'F');\n this.addElementButton.disabled = true;\n this.formulaBox.disable(true);\n }\n });\n\n this.formulaBox.setAddMaterialListener(materialName => {\n if (materialName.trim() !== ''){\n this.addTagInSearchQuery(materialName, 'material-name');\n this.addElementButton.disabled = true;\n this.formulaBox.disable(true);\n }\n });\n\n this.materialList= new MaterialList();\n this.resultsPage = this.element.querySelector('.results-page');\n this.materialList.attachAndSetEvents(this.resultsPage);\n\n this.currentTab = 'element';\n this.addPanel.appendChild(this.elementTable.element);\n\n this.userGuidance = true; // can enabled/disabled\n\n this.showingSearchBox = false;\n this.searchQuery = [];\n this.queryTypes = []; //**** Types associated to query elements\n // Types: element (E), formula (F), symbol (S) and prop names\n\n this._events();\n }\n\n\n _events() {\n // External event - Search button press\n this.mainButton.addEventListener( \"click\", (e) => {\n if (this.searchQuery.lenght === 0){\n util.showUserMsg('No query');\n } else {\n let queryObj = {};\n let elements = [];\n queryObj.search_by = {};\n this.searchQuery.forEach( (item, i) => {\n if (this.queryTypes[i] === 'F') {\n queryObj.search_by.formula = item;\n } else if (this.queryTypes[i] === 'E') {\n elements.push(item);\n } else if (this.queryTypes[i] !== 'S') { // property\n if (this.queryTypes[i].indexOf('band-gap') === 0) { // special case\n let bandGapData = item.split('<');\n let bandGapQuery = {};\n if (bandGapData.length == 3) {\n bandGapQuery.min = parseFloat(bandGapData[0]);\n bandGapQuery.max = parseFloat(bandGapData[2]);\n } else {\n if (item.startsWith(\"Band Gap\")) {\n bandGapQuery.min = null;\n bandGapQuery.max = parseFloat(bandGapData[1]);\n } else {\n bandGapQuery.min = parseFloat(bandGapData[0]);\n bandGapQuery.max = null;\n }\n }\n queryObj.band_gap = bandGapQuery;\n console.log(queryObj.band_gap);\n //queryObj.band_gap_direct = bandGapData[3];\n //}else if (this.queryTypes[i].indexOf('mass-density') === 0){ // special case\n //let massDensity = this.queryTypes[i].split(':');\n //queryObj.mass_density = {\"min\": massDensity[1], \"max\": massDensity[2]};\n //}else */\n } else {\n if (this.queryTypes[i].indexOf('has') === 0) {// boolean fields\n queryObj[replaceDashes(this.queryTypes[i])] = true;\n } else {// general fields\n queryObj[replaceDashes(this.queryTypes[i])] = item.split(' | ');\n }\n }\n }\n });\n\n if (elements.length > 0)\n queryObj.search_by.elements = elements;\n\n let exclusiveEl = this.element.querySelector('#exclusive-search:checked');\n let restrictedEl = this.element.querySelector('#restricted-search:checked');\n queryObj.search_by.exclusive = (exclusiveEl === null ? '0' : '1');\n queryObj.search_by.restricted = (restrictedEl === null ? '0' : '1');\n\n this.materialList.setSearch(queryObj);\n util.setBrowserHashPath('search', 'results');\n }\n\n });\n\n this.cleanButton.addEventListener( \"click\", (e) => {\n this.searchQuery = [];\n this.queryTypes = [];\n this.updateSearchQuery();\n this.addFormulaButton.disabled = false;\n this.addElementButton.disabled = false;\n this.formulaBox.disable(false);\n this.elementTable.deselectAllElements();\n });\n\n this.addButtonsBox.addEventListener( \"click\", (e) => {\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let index = className.indexOf('add-btn');\n\n if (index > 0){\n let selectingElement;\n let selectingTab = className.substring(0, index-1);\n if (selectingTab === 'element')\n selectingElement = this.elementTable.element;\n else if (selectingTab === 'props')\n selectingElement = this.propertiesBox.element;\n else if (selectingTab === 'formula')\n selectingElement = this.formulaBox.element;\n\n this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);\n\n // Change the styles of the buttons\n let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn');\n this._setTabSelectedStyles(selEl, false);\n this._setTabSelectedStyles(e.target, true);\n\n // Change the triangle\n this.element.querySelector('.'+this.currentTab+'-tri').style.visibility = 'hidden';\n this.element.querySelector('.'+selectingTab+'-tri').style.visibility = 'visible';\n\n this.currentTab = selectingTab;\n\n if (this.userGuidance){\n if (selectingTab === 'element'){\n UserGuidance.showIndependentTip(7, false);\n UserGuidance.showIndependentTip(3, true);\n }\n else if (selectingTab === 'props'){\n UserGuidance.showIndependentTip(3, false);\n UserGuidance.showIndependentTip(7, true);\n }else if (selectingTab === 'formula'){\n UserGuidance.showIndependentTip(3, false);\n UserGuidance.showIndependentTip(7, false);\n }\n }\n }\n }\n });\n\n this.searchQueryBox.addEventListener( \"click\", (e) => {\n let className = e.target.className;\n if (className === 'remove-label'){\n let elSymbol = e.target.parentElement.getAttribute('data-el');\n this.removeElementORFormulaInSearchQuery(elSymbol);\n }\n });\n\n }\n\n _setTabSelectedStyles(element, value){\n /*\n element.style.fontWeight = (value ? 'bold' : 'normal');\n element.style.color = (value ? '#E56400' : '#777');\n element.style.borderColor = (value ? '#E56400' : '#777');\n */\n element.id = (value ? 'add-tab-selected' : '');\n }\n\n _showSearchBox(){\n if (!this.showingSearchBox){\n this.showingSearchBox = true;\n this.searchLine.style.visibility = 'visible';\n\n if (this.userGuidance) UserGuidance.setFinal();\n }\n\n }\n\n _addItemInSearchQuery(item, type){\n this.searchQuery.push(item);\n this.queryTypes.push(type);\n }\n\n\n addTagInSearchQuery(tag, type){\n // If the it's an element and is already in the query it's not inserted\n if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return;\n\n if ( this.searchQuery.length > 0 )\n this._addItemInSearchQuery('&', 'S');\n this._addItemInSearchQuery(tag, type);\n this.updateSearchQuery();\n this._showSearchBox();\n }\n\n\n addElementsInSearchQuery(elementArray){\n let index = elementArray.length;\n while (index--) {\n this.addTagInSearchQuery(elementArray[index], 'E');\n }\n return true;\n }\n\n\n addPropertiesInSearchQuery(propsMap){\n propsMap.forEach((values/*Array*/, propName) => {\n let i = this.queryTypes.indexOf(propName);\n if (i < 0)\n this.addTagInSearchQuery(values.join(' | '), propName);\n else {\n // If a new band gap range is given it overrides the old one.\n if (propName === \"band-gap\") {\n this.searchQuery[i] = values[0];\n this.updateSearchQuery();\n this._showSearchBox();\n return;\n }\n\n let currentValues = this.searchQuery[i].split(' | ');\n //console.log('Current VAlues: ',currentValues);\n values.forEach(value => {\n if (currentValues.indexOf(value) < 0) currentValues.push(value);\n });\n this.searchQuery[i] = currentValues.join(' | ');\n this.updateSearchQuery();\n this._showSearchBox();\n }\n });\n }\n\n\n removeElementORFormulaInSearchQuery(element){\n let index = this.searchQuery.indexOf(element);\n if (index >= 0){\n this.searchQuery.splice(index, 1);\n this.queryTypes.splice(index, 1);\n if ( index > 0 ){\n let prevSymbol = this.searchQuery[index-1];\n if (prevSymbol === '&' || prevSymbol === '|') {\n this.searchQuery.splice(index-1, 1);\n this.queryTypes.splice(index-1, 1);\n }\n }\n // Check if after the removal a & or | symbol remains as the first in query\n if (this.queryTypes[0] === 'S'){\n this.searchQuery.splice(0, 1);\n this.queryTypes.splice(0, 1);\n }\n\n this.updateSearchQuery();\n\n if (util.ELEMENTS.indexOf(element) >= 0){\n this.elementTable.deselectElement(element);\n if (this.queryTypes.indexOf('E') < 0)\n this.addFormulaButton.disabled = false;\n }else{\n this.addElementButton.disabled = false;\n this.formulaBox.disable(false);\n }\n\n //console.log(\" final searchQuery: \",this.searchQuery);\n }\n\n return true;\n }\n\n\n updateSearchQuery(){\n let html= '';\n for (let i = 0; i < this.searchQuery.length; i++) {\n let type = this.queryTypes[i];\n\n if (type === 'S')\n html+= `<span class=\"search-query-symbol\" > ${this.searchQuery[i]} </span>`;\n else\n html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false));\n }\n console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes);\n this.searchQueryBox.innerHTML= html;\n }\n\n showResultsPage() {\n this.searchPage.style.display = 'none';\n this.resultsPage.style.display = 'block';\n if (this.userGuidance) UserGuidance.show(false);\n }\n\n showSearchPage(){\n this.searchPage.style.display= 'block';\n this.resultsPage.style.display= 'none';\n\n if (this.userGuidance){\n setTimeout(() => {\n UserGuidance.init(this.addButtonsBox, this.elementTable.element,\n this.searchLine, this.propertiesBox.tabsElement);\n UserGuidance.show(true, this.currentTab === 'element',\n this.currentTab === 'props');\n }, 400);\n }\n\n }\n}\n\n\nclass FormulaBox{\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'formula-box');\n this.element.innerHTML=\n `\n <div style=\"padding-bottom: 40px;\">\n <input type=\"text\" class=\"formula-text-field\"\n placeholder=\"Add formula to the search query above\" >\n <button class=\"adding-formula-btn\" disabled>Add to query</button>\n </div>\n <div>\n <input type=\"text\" class=\"material-name-text-field\"\n placeholder=\"Add material name to the search query above\" >\n <button class=\"adding-material-name-btn\" disabled>Add to query</button>\n </div>\n `;\n this.formulaTextField = this.element.querySelector('.formula-text-field');\n this.formulaButton = this.element.querySelector('.adding-formula-btn');\n\n this.materialTextField = this.element.querySelector('.material-name-text-field');\n this.materialButton = this.element.querySelector('.adding-material-name-btn');\n\n this.formulaButton.addEventListener( \"click\", (e) => {\n this.addFormulaListener(this.formulaTextField.value);\n this.formulaTextField.value = '';\n });\n\n this.materialButton.addEventListener( \"click\", (e) => {\n this.addMaterialListener(this.materialTextField.value);\n this.materialTextField.value = '';\n });\n\n this.formulaTextField.addEventListener( 'input', e => {\n //console.log('formulaTextField input: ',this.formulaTextField.value);\n this.formulaButton.disabled = (this.formulaTextField.value === '');\n });\n\n this.materialTextField.addEventListener( 'input', e => {\n //console.log('formulaTextField input: ',this.formulaTextField.value);\n this.materialButton.disabled = (this.materialTextField.value === '');\n });\n }\n\n setAddFormulaListener(listener) {\n this.addFormulaListener= listener;\n }\n\n setAddMaterialListener(listener) {\n this.addMaterialListener= listener;\n }\n\n disable(bool) {\n this.formulaTextField.disabled = bool;\n this.formulaButton.disabled = true;//bool;\n this.materialTextField.disabled = bool;\n this.materialButton.disabled = true;//bool;\n }\n}\n\n// EXPORTS\nmodule.exports = SearchMod;\n\n\n//# sourceURL=webpack:///./src/search-mod/SearchMod.js?"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module can't be inlined because the eval devtool is used. +/******/ var __webpack_exports__ = __webpack_require__("./src/main.js"); +/******/ +/******/ })() +; \ No newline at end of file diff --git a/client/css/styles.css b/client/css/styles.css index 36067d244cbafacc0cf597e96c70a04b7d689a52..416fb1d5c567ad8a2d2f17d038956f6ed9d0a8ed 100644 --- a/client/css/styles.css +++ b/client/css/styles.css @@ -1224,6 +1224,14 @@ text.structure-viewer-legend-labels{ font-size: 16px; } +#missing-prompt{ + margin-top: 0.5rem; + background-color: #E56400; + color: white; + padding: 16px; + display: none; +} + /* .tooltip { visibility: hidden; diff --git a/client/src/material-mod/DataStore.js b/client/src/material-mod/DataStore.js index 111048e59bebe1483062760e9912d0d506b01b4d..e37a6a481bcc6b21a48df5cbfde273bd80a97f21 100644 --- a/client/src/material-mod/DataStore.js +++ b/client/src/material-mod/DataStore.js @@ -33,6 +33,7 @@ let calcMap = new Map(); let ready = false; let hasThermal; let hasElecStructure; +let missing = false; function setMaterialData(dataFromAPI){ materialData = dataFromAPI; @@ -42,6 +43,14 @@ function getMaterialData(){ return materialData; } +function setMissing(value) { + missing = value; +} + +function getMissing() { + return missing; +} + function setIdealizedStructure(structure){ idealizedStructure = structure; } @@ -129,6 +138,9 @@ function getGroupId(leafId) { function isReady(matId) { if (materialData !== undefined) { + if (missing && matId == materialData.material_id) { + return true; + } if (idealizedStructure !== undefined) { if (calcs !== undefined) { if (groups !== undefined) { @@ -147,6 +159,7 @@ function clear() { calcs = undefined; groups = undefined; idealizedStructure = undefined; + missing = false; } function isInAnyGroup(calcId){ @@ -192,6 +205,8 @@ module.exports = { isInAnyNotDisabledGroup, getGroupLeafId, isReady, + setMissing, + getMissing, clear, setIdealizedStructure, getIdealizedStructure, diff --git a/client/src/material-mod/MaterialMod.js b/client/src/material-mod/MaterialMod.js index 7b1a0cda62cafc98d0790cb334d1a29e987aebe5..583f59cbe45b14a429afb0fb87d6fef3f1764656 100644 --- a/client/src/material-mod/MaterialMod.js +++ b/client/src/material-mod/MaterialMod.js @@ -48,6 +48,9 @@ class MaterialMod { constructor(){ this.element = document.createElement('div'); this.element.setAttribute("id",'material-module'); + this.missingPrompt = document.createElement('div'); + this.missingPrompt.id = "missing-prompt"; + this.element.appendChild(this.missingPrompt) this.overview = new Overview(); this.overview.attachAndSetEvents(this.element); @@ -138,30 +141,45 @@ class MaterialMod { * Called upon loading the overview page for a specific material. */ _loadMaterial(matId, view) { - - // Set to loading mode - this._setView(view); - let show = () => { - let materialData = DataStore.getMaterialData(); - let idealizedStructure = DataStore.getIdealizedStructure(); - // Cell viewer needs to be set only after page is visible so that it is - // resized correctly. - if (this.currentDetailView !== null) { - this.currentDetailView.load(); - if (view === util.MAT_VIEW.structure) { - this._setCellViewer(this.structureDetails.vizBox); - } - if (view === util.MAT_VIEW.methodology) { - this.methodologyDetails.updateSelection(); + // Show error message if resource not found + const missingPrompt = document.getElementById("missing-prompt"); + if (DataStore.getMissing()) { + // Hide the current view + if (this.currentDetailView === null) { + this.overview.element.style.display = 'none'; + } else { + this.currentDetailView.element.style.display = 'none'; } + const msg = ` + Could not find information for material with identifier ${matId}. + Either the material does not exist or is not yet visible in the Encyclopedia. + `; + missingPrompt.textContent = msg; + missingPrompt.style.display = "block"; } else { - document.querySelector('title').innerHTML = - 'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false); - this.overview.setMaterialData(); - let name = (materialData.material_name === null ? materialData.formula : materialData.material_name); - this.overview.setCalcsData(markedTreeLeafs); - this._setCellViewer(this.overview.vizBox); + missingPrompt.style.display = "none"; + this._setView(view); + let materialData = DataStore.getMaterialData(); + let idealizedStructure = DataStore.getIdealizedStructure(); + // Cell viewer needs to be set only after page is visible so that it is + // resized correctly. + if (this.currentDetailView !== null) { + this.currentDetailView.load(); + if (view === util.MAT_VIEW.structure) { + this._setCellViewer(this.structureDetails.vizBox); + } + if (view === util.MAT_VIEW.methodology) { + this.methodologyDetails.updateSelection(); + } + } else { + document.querySelector('title').innerHTML = + 'NOMAD Encyclopedia - '+util.getMaterialTitle(materialData, false); + this.overview.setMaterialData(); + let name = (materialData.material_name === null ? materialData.formula : materialData.material_name); + this.overview.setCalcsData(markedTreeLeafs); + this._setCellViewer(this.overview.vizBox); + } } }; let isReady = () => { @@ -175,6 +193,7 @@ class MaterialMod { // If material is already loaded, nothing fetched. if (!isReady()) { DataStore.clear(); + this.overview.clearCalcsData(); LoadingPopup.reset(); this.structureViewer.axisCheckbox.checked = true; this.structureViewer.bondsCheckbox.checked = true; @@ -187,9 +206,16 @@ class MaterialMod { // Request basic material data LoadingPopup.show("load_basic"); util.serverReq(util.getMaterialURL(matId), e1 => { - let materialData = JSON.parse(e1.target.response); - DataStore.setMaterialData(materialData); - util.materialId = materialData.material_id; + // Check for error + let stat = e1.target.status; + if (stat >= 400 && stat < 500) { + DataStore.setMissing(true); + DataStore.setMaterialData({material_id: matId}); + } else { + let materialData = JSON.parse(e1.target.response); + DataStore.setMaterialData(materialData); + util.materialId = materialData.material_id; + } isReady(); LoadingPopup.hide("load_basic"); }); @@ -197,32 +223,50 @@ class MaterialMod { // Request basic details for all calculations related to this material LoadingPopup.show("load_calculations"); util.serverReq(util.getMaterialXsURL('calculations', matId), e4 => { - let calculations = JSON.parse(e4.target.response); - let representatives = calculations.representatives; - let idealId = representatives.idealized_structure; - DataStore.setCalculations(calculations); - - // Get the idealized structure - //util.serverReq(util.getMaterialXsURL('idealized_structure', matId), e2 => { - let query = JSON.stringify({properties: ["idealized_structure"]}); - LoadingPopup.show("load_idealized"); - util.serverReqPOST(util.getMaterialCalcURL(matId, idealId), query, e2 => { - let struct = JSON.parse(e2.target.response).idealized_structure; - DataStore.setIdealizedStructure(struct); - this.structureViewer.load(struct); - document.getElementById('structure-ov').style.visibility = 'visible'; - document.getElementById('methodology-ov').style.visibility = 'visible'; - isReady(); - LoadingPopup.hide("load_idealized"); - }); + // Check for error + let stat = e4.target.status; + if (stat >= 400 && stat < 500) { + DataStore.setMissing(true); + } else { + let calculations = JSON.parse(e4.target.response); + let representatives = calculations.representatives; + let idealId = representatives.idealized_structure; + DataStore.setCalculations(calculations); + + // Get the idealized structure + //util.serverReq(util.getMaterialXsURL('idealized_structure', matId), e2 => { + let query = JSON.stringify({properties: ["idealized_structure"]}); + LoadingPopup.show("load_idealized"); + util.serverReqPOST(util.getMaterialCalcURL(matId, idealId), query, e2 => { + // Check for error + let stat = e2.target.status; + if (stat >= 400 && stat < 500) { + DataStore.setMissing(true); + } else { + let struct = JSON.parse(e2.target.response).idealized_structure; + DataStore.setIdealizedStructure(struct); + this.structureViewer.load(struct); + document.getElementById('structure-ov').style.visibility = 'visible'; + document.getElementById('methodology-ov').style.visibility = 'visible'; + } + isReady(); + LoadingPopup.hide("load_idealized"); + }); + } LoadingPopup.hide("load_calculations"); }); // Request groups LoadingPopup.show("load_groups"); util.serverReq(util.getMaterialXsURL('groups', matId), e5 => { - let groups = JSON.parse(e5.target.response); - DataStore.setGroups(groups); + // Check for error + let stat = e5.target.status; + if (stat >= 400 && stat < 500) { + DataStore.setMissing(true); + } else { + let groups = JSON.parse(e5.target.response); + DataStore.setGroups(groups); + } isReady(); LoadingPopup.hide("load_groups"); }); diff --git a/client/src/material-mod/Overview.view.js b/client/src/material-mod/Overview.view.js index 8087cc7e28b0424208adc270fdf40fabc194f5e9..ed1072eb2f4a0039fe05ab6feb89f8e8532e9a08 100644 --- a/client/src/material-mod/Overview.view.js +++ b/client/src/material-mod/Overview.view.js @@ -358,6 +358,14 @@ class Overview { return false; } + clearCalcsData() { + this.materialId = null; + this.calcMaterialId = null; + let bsLoaded = false; + let phononLoaded = false; + let dosLoaded = false; + } + setCalcsData(markedTreeLeafs) { let matData = DataStore.getMaterialData(); let calcs = DataStore.getCalculations();