diff --git a/client/bundle.js b/client/bundle.js index 160b80f6e9a31c9f52e0486ecb89388ce6a3737b..dbc94fdc9e3cbc0ab8e9cac12ece0542353e0031 100644 --- a/client/bundle.js +++ b/client/bundle.js @@ -1 +1,525 @@ -!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="Basic "+btoa(window.nomadEnv.guestUserToken+":");const n=window.nomadEnv.host+window.nomadEnv.path;document.querySelector("#guest-user a").href=n+"saml/?sso2";let a=i,l=null;function r(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 o(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 d(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"],setAuthRequestHeader:function(e,t){void 0===t?(a=i,l=null):(a="Basic "+btoa(t+":"),l=e)},getUserData:function(){return l},getServerLocation:function(){return document.location.hostname},authServerReq:function(e,t){var s=new XMLHttpRequest;return s.addEventListener("load",t),console.log("util.authServerReq: ",n+e),s.open("GET",n+e),s.send(),s},serverReq:function(e,t){var s=new XMLHttpRequest;return s.addEventListener("load",t),s.open("GET",e),s.setRequestHeader("Authorization",a),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;return i.addEventListener("load",s),i.open("POST",e),i.setRequestHeader("Content-Type","application/json"),i.setRequestHeader("Authorization",a),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 n+"materials"},getSuggestionURL:function(e){return n+"suggestions?property="+e},getMaterialURL:function(e){return n+"materials/"+e},getMaterialCalcURL:function(e,t,s=""){return n+"materials/"+e+"/calculations/"+t+(""===s?"":"?property="+s)},getMaterialGroupURL:function(e,t,s){return n+"materials/"+e+"/groups/"+t+"/"+s},getMaterialStatsURL:function(e){return n+"materials/"+e+"/statistics"},getMaterialXsURL:function(e,t){return n+"materials/"+t+"/"+e},getCalcEnergiesURL:function(e,t){return n+"materials/"+e+"/calculations/"+t+"/energies"},getFlaggingURL:function(){return n+"flagme"},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:o,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=o(e.lattice_parameters);i[0]+=t[3],i[1]+=t[4],i[2]+=t[5]}}),s?`<div>α = ${d(i[0]/e.size)}</div>\n <div>β = ${d(i[1]/e.size)}</div>\n <div>γ = ${d(i[2]/e.size)}</div>`:t?`<div>α = ${d(i[0]/e.size)}</div>`:""},rad2degree:d,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=r(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:r,getAverage:function(e){let t=0;for(var s=0;s<e.length;s++)t+=e[s];return t/e.length},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 if("unavailable"!==e.functional_type){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},getCalc:function(e){return p.get(e)},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},getGroups:function(){return n},getGroupId:y,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)})},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){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")}}},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():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;Math.abs(t)<.01&&(t=0),i.addText(this.plotArea,-5,-(e*n-3),t.toFixed(s),"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=1/5034117012222e10,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];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"),this.vrLinksContainer=this.footerElement.querySelector(".vr-download"),this.vrDropDown=new y,this.vrLinksContainer.appendChild(this.vrDropDown.element);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){this.vrDropDown.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")}}}class y{constructor(e){this.folded=!0,this.element=document.createElement("div"),this.element.innerHTML+=`\n <div >\n <span style=" vertical-align: 30%; ">Virtual Reality files</span>\n <img style="cursor: pointer" src="${n.IMAGE_DIR}folded.png" />\n </div>\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?n.IMAGE_DIR+"folded.png":n.IMAGE_DIR+"unfolded.png",this.foldingPanel.style.display=this.folded?"none":"block"}),this.element.addEventListener("blur",e=>{setTimeout(()=>{this.folded=!0,this.foldBtn.src=n.IMAGE_DIR+"folded.png",this.foldingPanel.style.display="none"},300)})}setMaterialId(e){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?${e}">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 \x3c!--\n <div style="padding: 5px; ">\n <a href="http://nomad.srv.lrz.de/NOMAD/NOMADGearvrT.apk">Samsung GearVR</a>\n </div>\n --\x3e\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 `}}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(),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-58*s[d];if(r.push(t),o.push(e),this.pointsSpin1.push({x:t,y:e}),null!==i){let t=1602176565e-58*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):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=e.indexOf(600)+1,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))}let l=n.slice(0,s),r=a.slice(0,s),o=Math.max.apply(null,r);this.setRangeAndLabels("T (K)",0,600,"Cv (J/K/kg)",0,200*Math.ceil(o/200)),this.drawAxis(4,4,0);let d="";l.forEach((e,t)=>{let s=r[t];d+=" "+this.xRel*e+" -"+this.yRel*(s-this.yMin)}),i.addPolyline(this.plotArea,d,"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),a=s(17),l=s(18),r=s(19),o=s(10),d=s(33),h=s(15),c=s(2),p=document.getElementById("content"),u=document.querySelector("title"),m=document.getElementById("calc-flagging-tab");m.style.top=window.innerHeight/2+"px",m.addEventListener("click",e=>{a.show(v.getCurrentPageStatus())});let y,v,f,g,b=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",c.getMaterialData().material_id+"/"+e.target.value)});let e=this;this.detailsDropDown.addEventListener("focus",(function t(){let s=e.detailsDropDown.querySelector('option[value="electronicstruct"]');c.hasElecStructureData()||e.detailsDropDown.removeChild(s);let i=e.detailsDropDown.querySelector('option[value="thermalprops"]');c.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 x(e){g&&p.removeChild(g),g=e,p.appendChild(g)}l.subscribe("show-material",e=>{console.log("Handling event show-material: "+e.material_id+" view: "+e.view),b.setState("material",e.view),void 0===f&&(v=new o,f=v.element),x(f),v.setMaterialView(e),h.show(!1),null!==i.getUserData()&&(m.style.visibility="visible")}),l.subscribe("show-search",e=>{console.log("Handling event show-search: "+e),u.innerHTML="NOMAD Encyclopedia - Search",b.setState("search",e),void 0===e?(y.showSearchPage(),n.hide()):"results"===e&&y.showResultsPage(),x(y.element),"hidden"!==m.style.visibility&&(m.style.visibility="hidden")}),r.add("search",e=>l.publish("show-search",e)),r.add("material",(e,t)=>l.publish("show-material",{material_id:e,view:t})),y=new d,""===document.location.hash&&(document.location+="#/search"),r.route();let S=document.querySelector("#user-name"),w=document.querySelector("#logout-button");let _=function(e){let t=("; "+document.cookie).split("; "+e+"=");if(2===t.length)return t.pop().split(";").shift()}("user_info");if(void 0!==_){let e=JSON.parse((T=_).substring(1,T.length-1).replace(/\\054/g,",").replace(/\\/g,""));"Authenticated"===(E=e).status&&(S.innerHTML=E.user.username,document.querySelector("#guest-user").style.display="none",document.querySelector("#auth-user").style.display="inline",i.setAuthRequestHeader(E.user,E.token.data),g===f&&(m.style.visibility="visible"))}var E,T;w.addEventListener("click",e=>{document.cookie="user_info=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain="+window.nomadEnv.userCookieDomain+"; path=/",S.innerHTML="",document.querySelector("#guest-user").style.display="inline",document.querySelector("#auth-user").style.display="none",i.setAuthRequestHeader(),"hidden"!==m.style.visibility&&(m.style.visibility="hidden")})},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 <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 detected" ></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=null,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];if(r.disabled||""!==t.value)if(r.disabled&&""===h.value&&t.value!==i.MAT_VIEW.methodology)p.innerHTML="The subcategory fields must be set";else{p.innerHTML="Sending report...";let e=l.querySelector("textarea").value,s=n.getMaterialData().material_id,a=i.getUserData(),o="User issue | Material "+s,d="**Server:** "+i.getServerLocation()+"\\n\\n**User:** "+a.username+", "+a.email;r.disabled?(d+="\\n\\n**Category:** "+t.text,t.value!==i.MAT_VIEW.methodology&&(d+="\\n\\n**Subcategory:** "+h.options[h.selectedIndex].text+"\\n\\n**Calculation/group marked on the tree:** "+m)):(d+="\\n\\n**Category:** Overview / "+t.text,t.value===i.MAT_VIEW.electronicstruct&&null!==y&&(d+="\\n\\n**Chosen calculations:** "+(null===y.bs?"":"BS calculation "+y.bs)+(null===y.dos?"":" DOS calculation "+y.dos))),d+="\\n\\n**User text:** "+e;let c=`{\n "title": "${o}",\n "description": "${d}"}`;console.log("Flagging POST request Json: ",c),i.serverReqPOST(i.getFlaggingURL(),c,e=>{console.log("response",e),200===e.target.status&&v()})}else p.innerHTML="The category fields must be set"}),e.exports={show:function(e){m=e.markedLeaf,y=e.eStructCalcs,o.style.display=n.hasElecStructureData()?"block":"none",d.style.display=n.hasThermalData()?"block":"none",function(e){if(h.innerHTML="",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=new Map;function n(){let e,t,s,n=document.location.hash.substring(2);if(n.lastIndexOf("/")===n.length-1&&(n=n.substring(0,n.length-1)),n.indexOf("/")>0){let i=n.split("/");e=i[0],t=i[1],s=i[2]}else e=n;i.has(e)&&i.get(e)(t,s)}window.addEventListener("hashchange",n),e.exports={add:function(e,t){i.set(e,t)},route:n}},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>System type</span></b>:\n <span class="system-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 40px 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(".system-type-field"),this.spaceGroupField=this.element.querySelector(".space-group-field"),this.spaceGroupValue=this.element.querySelector(".space-group-value"),this.structTypeField=this.element.querySelector(".structure-type-field"),this.structTypeValue=this.element.querySelector(".structure-type-value"),this.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.system_type;if(this.systemType.textContent=e.system_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 s=JSON.parse(t.target.response).electronic_band_structure;this.bandPlotter.setBandStructureData(s),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===s.section_k_band_segment[0].band_energies.length&&(this.spinLegend.style.display="block"),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 s=JSON.parse(t.target.response).electronic_dos;this.dosPlotter.setPoints(s,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===s.dos_values.length&&(this.spinLegend.style.display="block"),this.dosLoaded=!0,e()}a.hide("overview_electronic_dos")})}}if(d.hasThermalData())if(document.getElementById("thermal-props-ov").style.display="block",d.hasThermalData=!0,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.setAttribute("info-sys-data","similar-materials"),this.element.style="float : right; border: 2px solid #DDD; padding: 2px 2px 0px 3px;",this.element.innerHTML+=`\n <span style=" vertical-align: 30%;" ><b>Similar materials</b></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){const t=Object.keys(e).map((function(t){return{data:e[t],key:t}}));t.sort((function(e,t){return e.data.Tc<t.data.Tc?1:e.data.Tc>t.data.Tc?-1:0}));const s=t.slice(0,5),n=document.createElement("table");n.setAttribute("class","similar-materials-panel-unfolded"),n.style="width: 230px; padding-left: 5px;",this.foldingPanel.appendChild(n);const a=document.createElement("tr");a.style="padding: 5px; ",a.innerHTML='<th style = "text-align: left;">Formula (space group)</th><th>:</th><th style = "text-align: left;">Tc</th>',n.appendChild(a),s.forEach((function(e){const t=e.key.split(":")[0],s=document.createElement("tr");s.style="padding: 5px; font-family: 'Arimo', sans-serif; font-size: 10pt; ";const a=`${window.location.toString().replace(/#.*$/,"")}#/material/${t}`;s.innerHTML=`<td><a href="${a}" target="_${t}" style="color:#777; font-family: 'Arimo', sans-serif; font-size: 10pt;">${i.getSubscriptedFormula(e.data.formula)} (${e.data.space_group_number})</a></td><td>:</td> <td>${e.data.Tc.toPrecision(3)}</td>`,n.appendChild(s)}))}}}},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.system_type,a="bulk"===t.system_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().system_type,t="bulk"===h.getMaterialData().system_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.system_type,this.systemTypeField.textContent=e.system_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);const o=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 d{constructor(e){this.element=document.createElement("div"),this.element.className=e,this.filtersOn=[],this.folded=!0,this.element.innerHTML+=`\n <div>\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">\n <table style="width: 100%;">\n <thead>\n <tr>\n <th style="width: 12%;"> </th>\n <th style="width: 16%;">\n <span>Type</span>\n </th>\n <th style="width: 18%;">\n <span info-sys-data="functional-type">Density functional</span>\n </th>\n <th style="width: 12%;">\n <span info-sys-data="code-name">Code</span>\n </th>\n <th style="width: 16%;">\n <span info-sys-data="pseudopotential-type">Potential</span>\n </th>\n <th style="width: 11%;">\n <span info-sys-data="basis-set-type">Basis set</span>\n </th>\n \x3c!-- <th style="width: 9%;"> </th> --\x3e\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,o.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="<td></td>",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 h(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 \x3c!-- <th style="width: 8%;">\n <span info-sys-data="basis-set-type">Link</span>\n\n </th>\n <th style="width: 8%;">\n <span info-sys-data="basis-set-type">RV</span>\n\n </th> --\x3e\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 d("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="";null!==t.calculation_pid&&void 0!==t.calculation_pid&&(i='<a href="https://repository.nomad-coe.eu/NomadRepository-1.1/views/calculation.zul?pid='+t.calculation_pid+'" target="blank"> <img src="img/download.svg" height="20px" /> </a>');let 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 ${h(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 ${h(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 ${h(t.basis_set_quality_quantifier)}\n </td>\n \x3c!--\n <td style="padding-top: 8px;padding-bottom: 4px;">${i}\n </td>\n\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:60,right:16,top:10,bottom:32}),this.tooltip}setData(e,t){this.clear();let s=e.indexOf(600)+1,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))}let l=n.slice(0,s),r=a.slice(0,s),o=Math.max.apply(null,r),d=Math.min.apply(null,r),h=1e3*Math.ceil(o/1e3),c=1e3*Math.floor(d/1e3);this.setRangeAndLabels("T (K)",0,600,"F (J/kg)",c,h),this.drawAxis(4,null,0);let p="";l.forEach((e,t)=>{let s=r[t];p+=" "+this.xRel*e+" -"+this.yRel*(s-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: hidden">\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 class="exclusive-search-line">\n Exclusive search <input class="exclusive-search" type="checkbox" >\n </div>\n </div>\n\n\n <div class="add-buttons" >\n <div class="tab-buttons" style="width: 70%; display: inline-block">\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 \x3c!--<input type="checkbox" name="and-or" class="not-symbol-btn" />NOT--\x3e\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> \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)=>{"F"===this.queryTypes[i]?e.search_by.formula=s:"E"===this.queryTypes[i]?t.push(s):"S"!==this.queryTypes[i]&&(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.element=t.join(","));let s=this.element.querySelector(".exclusive-search:checked");e.search_by.exclusive=null===s?"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{let t=this.searchQuery[s].split(" | ");e.forEach(e=>{t.indexOf(e)<0&&t.push(e)}),this.searchQuery[s]=t.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.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: "+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+"/"+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="system-type">System 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="system-type">--\x3e${t.system_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">System type</div>\n <input type="checkbox" class="system-type-field" value="bulk"> Bulk<br>\n <input type="checkbox" class="system-type-field" value="2D"> 2D<br>\n <input type="checkbox" class="system-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 \x3c!--\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 Min: <input type="text" class="band-gap-min-field"> \n Max: <input type="text" class="band-gap-max-field">\n </div>\n </div>\n\n <div style="float: left; width: 5%;"> </div>\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 --\x3e\n <div style="clear: both;"></div>\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; width: 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>\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 \x3c!--\n <input type="checkbox" class="code-name-field" value="VASP">VASP<br>\n <input type="checkbox" class="code-name-field" value="quantum-espresso">Quantum Espresso<br>\n <input type="checkbox" class="code-name-field" value="FHI-aims">FHI-aims<br>\n <input type="checkbox" class="code-name-field" value="exciting">exciting<br>\n --\x3e\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-type">Basis set</span></div>\n <input type="checkbox" class="basis-set-type-field" value="(L)APW+lo">\n <span info-sys-data="basis-set-type.value:(L)APW+lo">(L)APW+lo</span><br>\n <input type="checkbox" class="basis-set-type-field" value="FLAPW">\n <span info-sys-data="basis-set-type.value:FLAPW">FLAPW</span><br>\n <input type="checkbox" class="basis-set-type-field" value="gaussians">\n <span info-sys-data="basis-set-type.value:gaussians">gaussians</span><br>\n <input type="checkbox" class="basis-set-type-field" value="numeric AOs">\n <span info-sys-data="basis-set-type.value:numeric AOs">numeric AOs</span><br>\n <input type="checkbox" class="basis-set-type-field" value="plane waves">\n <span info-sys-data="basis-set-type.value:plane waves">plane waves</span><br>\n <input type="checkbox" class="basis-set-type-field" value="psinc functions">\n <span info-sys-data="basis-set-type.value:psinc functions">psinc functions</span><br>\n <input type="checkbox" class="basis-set-type-field" value="real-space grid">\n <span info-sys-data="basis-set-type.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>"})});n.addToInfoSystem(this.element),this._events()}_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,["system-type","crystal-system"],e)):"results"===t?this.addPropsFromCheckboxes(s,["has-band-structure","has-dos","has-fermi-surface","has-thermal-properties"],e):"method"===t&&(this.addPropsFromCheckboxes(s,["functional-type","basis-set-type"],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="Band Gap";if(""!==s.value&&(a=s.value+" < "+a,n+=":"+s.value,t&&(s.value="")),""!==i.value&&(a+=" < "+i.value,n+=":"+i.value,t&&(i.value="")),"Band Gap"!==a){let t=document.querySelector('input[name="band-gap-type"]:checked').value;"d"===t?n+=":True":"i"===t&&(n+=":False"),e.set(n,[a+" "+t])}}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 +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./src/main.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./src/common/FlaggingFormPopup.js": +/*!*****************************************!*\ + !*** ./src/common/FlaggingFormPopup.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, 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 <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 detected\" ></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`;\n\n// Form elements\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');\n\nlet treeLeaf = null;\nlet overviewEStructCalcs = null;\n\n\nfunction _setCurrentPage(pageId){\n\n subcategoryField.innerHTML = '';\n\n if (pageId === null){\n categoryField.disabled = false;\n subcategoryField.style.display = 'none';\n\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} // function _setCurrentPage\n\n\nfunction show(pageStatus){\n //console.log('pageStatus : ',pageStatus);\n treeLeaf = pageStatus.markedLeaf;\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\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\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\n\ncloseButton.addEventListener('click', e => {\n hide();\n});\n\n\nsendButton.addEventListener('click', e => {\n\n let categoryChosen = categoryField.options[categoryField.selectedIndex];\n\n if (!categoryField.disabled && categoryChosen.value === ''){ // Overview case\n validationMsg.innerHTML = 'The category fields must be set';\n\n }else if (categoryField.disabled && subcategoryField.value === '' // Detaisl pages case\n && categoryChosen.value !== util.MAT_VIEW.methodology){\n validationMsg.innerHTML = 'The subcategory fields must be set';\n\n }else{\n validationMsg.innerHTML = 'Sending report...';\n let textareaText = flaggingFormPopup.querySelector('textarea').value;\n let materialId = DataStore.getMaterialData().material_id;\n let userdata = util.getUserData();\n\n\n let titleText = 'User issue | Material '+materialId;\n let descriptionText = '**Server:** '+util.getServerLocation()+\n '\\\\n\\\\n**User:** '+userdata.username+', '+userdata.email;\n\n // Overview page\n if ( !categoryField.disabled){\n descriptionText += '\\\\n\\\\n**Category:** Overview / '+categoryChosen.text;\n\n if (categoryChosen.value === util.MAT_VIEW.electronicstruct\n && overviewEStructCalcs !== null)\n descriptionText += '\\\\n\\\\n**Chosen calculations:** '+\n (overviewEStructCalcs.bs === null ? '' : 'BS calculation '+overviewEStructCalcs.bs)+\n (overviewEStructCalcs.dos === null ? '' : ' DOS calculation '+overviewEStructCalcs.dos);\n\n }else{ // Details pages\n descriptionText += '\\\\n\\\\n**Category:** '+categoryChosen.text;\n\n if (categoryChosen.value !== util.MAT_VIEW.methodology){\n descriptionText += '\\\\n\\\\n**Subcategory:** '+\n subcategoryField.options[subcategoryField.selectedIndex].text+\n '\\\\n\\\\n**Calculation/group marked on the tree:** '+treeLeaf;\n }\n }\n\n descriptionText += '\\\\n\\\\n**User text:** '+textareaText;\n\n let queryJson =`{\n \"title\": \"${titleText}\",\n \"description\": \"${descriptionText}\"}`;\n console.log('Flagging POST request Json: ',queryJson);//, util.getFlaggingURL());\n\n\n util.serverReqPOST(util.getFlaggingURL(), queryJson, e => {\n console.log('response',e);\n if (e.target.status === 200) hide();\n });\n\n }\n\n});\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 ***! + \*******************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, 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 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\n// EXPORTS\nmodule.exports = { show, hide };\n\n\n//# sourceURL=webpack:///./src/common/LoadingPopup.js?"); + +/***/ }), + +/***/ "./src/common/PlotterBase.js": +/*!***********************************!*\ + !*** ./src/common/PlotterBase.js ***! + \***********************************/ +/*! no static exports found */ +/***/ (function(module, 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() : 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 // Fix to prevent the -0 printing\n if (Math.abs(numberToPaint) < 0.01) numberToPaint = 0;\n svg.addText(this.plotArea,-5, -(yStep*i-3), numberToPaint.toFixed(decimals), '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 ***! + \******************************/ +/*! no static exports found */ +/***/ (function(module, 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 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 ***! + \******************************/ +/*! no static exports found */ +/***/ (function(module, 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 routes = new Map();\n\n\nfunction add(route, func){\n routes.set(route, func);\n}\n\n\nwindow.addEventListener(\"hashchange\", route);\n\nfunction route() {\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 ***! + \***************************/ +/*! no static exports found */ +/***/ (function(module, 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 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 ***! + \***************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, 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 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 ***! + \****************************/ +/*! no static exports found */ +/***/ (function(module, 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 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 AUTH_REQUEST_HEADER_GUEST_USER = 'Basic '+ btoa(window.nomadEnv.guestUserToken+':');\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.host + window.nomadEnv.path;\n\n// Mockup URLs\n//const FERMI_SURFACE_URL= HOST+'files/fermi/'+\n//'fed3fa9fbc68aa6c5e51845396889666ca37bb2e626e1da53.x3d';\n\n// Local variables\n\nlet authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER;\n//console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue);\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, not build if value is unavailable\n if (calc.functional_type !== \"unavailable\") {\n let newFunctionalArray = new Set();\n newFunctionalArray.add(calc.calc_id);\n functCalcMap.set(calc.functional_type, newFunctionalArray);\n }\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 setAuthRequestHeader(userDataP, value){\n\n if (value === undefined){// default value\n authRequestHeaderValue = AUTH_REQUEST_HEADER_GUEST_USER;\n userData = null;\n //console.log('user: ANONYMOUS authRequestHeader: ',authRequestHeaderValue);\n } else{\n authRequestHeaderValue = 'Basic '+ btoa(value+':');\n userData = userDataP;\n //console.log('user',user,'authRequestHeader: ',authRequestHeaderValue);\n }\n}\n\n\nfunction serverReq(url, callback){\n var oReq = new XMLHttpRequest();\n oReq.addEventListener(\"load\", callback);\n oReq.open(\"GET\", url);\n //console.log('authRequestHeaderValue: ',authRequestHeaderValue);\n oReq.setRequestHeader('Authorization', authRequestHeaderValue);\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 //console.log('authRequestHeaderValue: ',authRequestHeaderValue);\n oReq.setRequestHeader('Authorization', authRequestHeaderValue);\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 getFlaggingURL(){\n return API_BASE_URL+'flagme';\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\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 setAuthRequestHeader,\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 getFlaggingURL,\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 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 ***! + \*********************/ +/*! no static exports found */ +/***/ (function(module, 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');\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 */\n\nlet flaggingTab = document.getElementById('calc-flagging-tab');\nflaggingTab.style.top = (window.innerHeight/2)+'px';\n\nflaggingTab.addEventListener('click',e => {\n FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus());\n});\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 //console.log('User data:',util.getUserData());\n if (util.getUserData() !== null) flaggingTab.style.visibility = 'visible';\n});\n\n\nPubSub.subscribe('show-search', search => {\n console.log('Handling event show-search: '+search);\n\n titleElement.innerHTML = 'NOMAD Encyclopedia - Search';\n breadcrumb.setState('search',search);\n\n if (search === undefined){\n searchMod.showSearchPage();\n LoadingPopup.hide(); // In case it comes from the result page\n\n }else if (search === 'results')\n searchMod.showResultsPage();\n\n showModuleDOM(searchMod.element);\n\n if (flaggingTab.style.visibility !== 'hidden')\n flaggingTab.style.visibility = 'hidden';\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\n//function setAppAuthenticated(data){\n //if (data.status === 'Authenticated'){\n //userNameElement.innerHTML = data.user.username;\n //document.querySelector('#guest-user').style.display = 'none';\n //document.querySelector('#auth-user').style.display = 'inline';\n //util.setAuthRequestHeader(data.user, data.token.data);\n //if (currentModule === materialModDOM) flaggingTab.style.visibility = 'visible';\n //}\n//}\n\n\n//function logout(){\n //userNameElement.innerHTML = '';\n //util.setAuthRequestHeader();\n\n //if (flaggingTab.style.visibility !== 'hidden')\n //flaggingTab.style.visibility = 'hidden';\n//}\n\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\n\nfunction parseCookie(userData) {\n return userData.substring(1, userData.length-1).replace(/\\\\054/g,',').replace(/\\\\/g,'');\n}\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 ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \***************************************/ +/*! no static exports found */ +/***/ (function(module, 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];\n\n // keeping the segment limits (x coordenate) 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 ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +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 ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, 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-49;\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) : 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 ***! + \***************************************/ +/*! no static exports found */ +/***/ (function(module, 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, hasElecStructure;\n\nfunction setMaterialData(dataFromAPI){\n materialData = dataFromAPI;\n}\n\nfunction getMaterialData(){\n return materialData;\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 (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}\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 = { getRepresentatives, setMaterialData, getMaterialData, getCalculations, getCalc,\n setCalculations, getGroups, getGroupId, setGroups, isGroup, getGroupType,\n getReprCalc, isInAnyGroup, isInAnyNotDisabledGroup, getGroupLeafId, isReady, clear, setIdealizedStructure, getIdealizedStructure,\n hasThermalData, hasElecStructureData};\n\n\n//# sourceURL=webpack:///./src/material-mod/DataStore.js?"); + +/***/ }), + +/***/ "./src/material-mod/DetailsViewBase.js": +/*!*********************************************!*\ + !*** ./src/material-mod/DetailsViewBase.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \**********************************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, 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 indexOf600K = x.indexOf(600)+1;\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 // Up to 600K data is taken\n let xClipped = xClean.slice(0, indexOf600K);\n let yClipped = yClean.slice(0, indexOf600K);\n\n let yMaxValue = Math.max.apply(null, yClipped);\n this.setRangeAndLabels('T (K)', 0, 600, 'Cv (J/K/kg)', 0, Math.ceil(yMaxValue/200)*200);\n this.drawAxis(4, 4, 0);\n\n let polylinePoints = '';\n xClipped.forEach( (t, i) => {\n let y = yClipped[i];;\n polylinePoints+= ' '+this.xRel*t+' -'+this.yRel*(y - 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 ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, 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: 60, 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 indexOf600K = x.indexOf(600)+1;\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 // Up to 600K data is taken\n let xClipped = xClean.slice(0, indexOf600K);\n let yClipped = yClean.slice(0, indexOf600K);\n\n let yMaxValue = Math.max.apply(null, yClipped);\n let yMinValue = Math.min.apply(null, yClipped);\n let yAxisMaxValue = Math.ceil(yMaxValue/1000)*1000;\n let yAxisMinValue = Math.floor(yMinValue/1000)*1000;\n\n this.setRangeAndLabels('T (K)', 0, 600, 'F (J/kg)', yAxisMinValue, yAxisMaxValue);\n this.drawAxis(4, null, 0);\n\n let polylinePoints = '';\n xClipped.forEach( (t, i) => {\n let y = yClipped[i];;\n polylinePoints += ' ' + this.xRel*t + ' -' + this.yRel * (y - 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 ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, 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\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\n // Set to loading mode\n this._setView(view);\n\n let show = () => {\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 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.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 let materialData = JSON.parse(e1.target.response);\n DataStore.setMaterialData(materialData);\n util.materialId = materialData.material_id;\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 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 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 isReady();\n LoadingPopup.hide(\"load_idealized\");\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 let groups = JSON.parse(e5.target.response);\n DataStore.setGroups(groups);\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 ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, 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\n\nconst REPOSITORY_LINK =\n 'https://repository.nomad-coe.eu/NomadRepository-1.1/views/calculation.zul?pid=';\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>\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\">\n <table style=\"width: 100%;\">\n <thead>\n <tr>\n <th style=\"width: 12%;\"> </th>\n <th style=\"width: 16%;\">\n <span>Type</span>\n </th>\n <th style=\"width: 18%;\">\n <span info-sys-data=\"functional-type\">Density functional</span>\n </th>\n <th style=\"width: 12%;\">\n <span info-sys-data=\"code-name\">Code</span>\n </th>\n <th style=\"width: 16%;\">\n <span info-sys-data=\"pseudopotential-type\">Potential</span>\n </th>\n <th style=\"width: 11%;\">\n <span info-sys-data=\"basis-set-type\">Basis set</span>\n </th>\n <!-- <th style=\"width: 9%;\"> </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.foldedPanel = this.element.querySelector('.filter-groups-c-folded');\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.foldedPanel.style.display = (this.folded ? 'block' : 'none');\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 = '<td></td>'; // calculation Id column\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: 8%;\">\n <span info-sys-data=\"basis-set-type\">Link</span>\n\n </th>\n <th style=\"width: 8%;\">\n <span info-sys-data=\"basis-set-type\">RV</span>\n\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 if (calc.calculation_pid !== null && calc.calculation_pid !== undefined)\n repositoryLinkHtml =\n '<a href=\"'+REPOSITORY_LINK+calc.calculation_pid+'\" 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 <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 ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, 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>System type</span></b>:\n <span class=\"system-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 40px 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('.system-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.system_type === 'bulk');\n this.systemType.textContent= data.system_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 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 this.bsLoaded = true;\n isReady();\n }\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 this.dosLoaded = true;\n isReady();\n }\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 DataStore.hasThermalData = true;\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 ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, 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.className = className;\n this.element.setAttribute(\"info-sys-data\",\"similar-materials\")\n this.element.style = \"float : right; border: 2px solid #DDD; padding: 2px 2px 0px 3px;\"\n this.element.innerHTML+=`\n <span style=\" vertical-align: 30%;\" ><b>Similar materials</b></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 const dataAsArray = Object.keys(similarityData).map(function(key) {\n return {\n data: similarityData[key],\n key: key\n }\n })\n dataAsArray.sort(function(a, b) {\n if (a.data.Tc < b.data.Tc) {\n return 1\n } else if (a.data.Tc > b.data.Tc) {\n return -1\n } else {\n return 0\n }\n })\n\n const slicedData = dataAsArray.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 material_id = item.key.split(':')[0]\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/${material_id}`\n element.innerHTML = `<td><a href=\"${url}\" target=\"_${material_id}\" style=\"color:#777; font-family: 'Arimo', sans-serif; font-size: 10pt;\">${util.getSubscriptedFormula(item.data.formula)} (${item.data.space_group_number})</a></td><td>:</td> <td>${item.data.Tc.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 ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, 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.system_type === 'bulk');\n\n this.systemTypeField.textContent= data.system_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.system_type === '2D');\n let isBulk = (materialData.system_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().system_type === '2D');\n let isBulk = (DataStore.getMaterialData().system_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 ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, 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 ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, 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 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.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: \" + 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 + \"/\" +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=\"system-type\">System 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=\"system-type\">-->${mat.system_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_matches} </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 ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, 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\">System type</div>\n <input type=\"checkbox\" class=\"system-type-field\" value=\"bulk\"> Bulk<br>\n <input type=\"checkbox\" class=\"system-type-field\" value=\"2D\"> 2D<br>\n <input type=\"checkbox\" class=\"system-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 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 Min: <input type=\"text\" class=\"band-gap-min-field\"> \n Max: <input type=\"text\" class=\"band-gap-max-field\">\n </div>\n </div>\n\n <div style=\"float: left; width: 5%;\"> </div>\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 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; width: 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>\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 <!--\n <input type=\"checkbox\" class=\"code-name-field\" value=\"VASP\">VASP<br>\n <input type=\"checkbox\" class=\"code-name-field\" value=\"quantum-espresso\">Quantum Espresso<br>\n <input type=\"checkbox\" class=\"code-name-field\" value=\"FHI-aims\">FHI-aims<br>\n <input type=\"checkbox\" class=\"code-name-field\" value=\"exciting\">exciting<br>\n -->\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-type\">Basis set</span></div>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"(L)APW+lo\">\n <span info-sys-data=\"basis-set-type.value:(L)APW+lo\">(L)APW+lo</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"FLAPW\">\n <span info-sys-data=\"basis-set-type.value:FLAPW\">FLAPW</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"gaussians\">\n <span info-sys-data=\"basis-set-type.value:gaussians\">gaussians</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"numeric AOs\">\n <span info-sys-data=\"basis-set-type.value:numeric AOs\">numeric AOs</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"plane waves\">\n <span info-sys-data=\"basis-set-type.value:plane waves\">plane waves</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"psinc functions\">\n <span info-sys-data=\"basis-set-type.value:psinc functions\">psinc functions</span><br>\n <input type=\"checkbox\" class=\"basis-set-type-field\" value=\"real-space grid\">\n <span info-sys-data=\"basis-set-type.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\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,['system-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-type'], 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 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 !== '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 ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, 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: hidden\">\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 class=\"exclusive-search-line\">\n Exclusive search <input class=\"exclusive-search\" type=\"checkbox\" >\n </div>\n </div>\n\n\n <div class=\"add-buttons\" >\n <div class=\"tab-buttons\" style=\"width: 70%; display: inline-block\">\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 <!--<input type=\"checkbox\" name=\"and-or\" class=\"not-symbol-btn\" />NOT-->\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') queryObj.search_by.formula = item;\n else if (this.queryTypes[i] === 'E') 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 = this.queryTypes[i].split(':');\n queryObj.band_gap = {\"min\": util.eV2J(bandGapData[1]),\n \"max\": util.eV2J(bandGapData[2])};\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 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 if (elements.length > 0)\n queryObj.search_by.element = elements.join(',');\n\n let exclusiveEl = this.element.querySelector('.exclusive-search:checked');\n queryObj.search_by.exclusive = (exclusiveEl === 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 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\n showResultsPage(){\n this.searchPage.style.display= 'none';\n this.resultsPage.style.display= 'block';\n\n if (this.userGuidance) UserGuidance.show(false);\n }\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?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/client/conf.js b/client/conf.js index 758991d353648484c8ff0f16d4813075f584f46a..278d7bcb17958e304213eaf324ea6d4c5eebc4ae 100644 --- a/client/conf.js +++ b/client/conf.js @@ -9,5 +9,8 @@ window.nomadEnv = { //path: "", //userCookieDomain: ".localhost", guestUserToken: 'eyJhbGciOiJIUzI1NiIsImlhdCI6MTUyMzg4MDE1OSwiZXhwIjoxNjgxNTYwMTU5fQ.ey'+ - 'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4' + 'JpZCI6ImVuY2d1aSJ9.MsMWQa3IklH7cQTxRaIRSF9q8D_2LD5Fs2-irpWPTp4', + keycloakBase: 'https://nomad-lab.eu/fairdi/keycloak/auth/', + keycloakRealm: 'fairdi_nomad_test', + keycloakClientId: 'nomad_gui_dev' }; diff --git a/client/css/styles.css b/client/css/styles.css index fd91ab24d66620f1b3ec5696397a8ce3e15ae2a1..881664416450e088463d13d50df4b5655d701132 100644 --- a/client/css/styles.css +++ b/client/css/styles.css @@ -187,8 +187,27 @@ body { font-family: 'Arimo', sans-serif; header{ background-color: #23356d; } +#logo-header { + display: flex; +} + +#auth-panel { + width: 25%; + text-align: right; + padding-top:5px; +} + +#logo-panel { + width: 25%; + text-align: right; + padding-top:5px; +} + +#spacer { + flex-grow: 1; +} -div#logo-header{ +div#logo-header { width: 1200px; margin: 0 auto; /* padding-top: 10px;padding-bottom: 56px;*/ padding: 3px 0; diff --git a/client/index.html b/client/index.html index 8751b0166c46385f44a19575ea6ea5d020db1643..8089b4113bd6a7a3f7d1d0cf1c9196a1c1f9a0ef 100644 --- a/client/index.html +++ b/client/index.html @@ -11,6 +11,8 @@ <link rel="stylesheet" href="css/styles.css"/> <script defer src="conf.js"></script> + <script defer src="keycloak.min.js"></script> + <script defer src="loadkeycloak.js"></script> <script defer src="lib/3d-viewers/three.min.js"></script> <script defer src="lib/3d-viewers/orthographiccontrols.js"></script> <script defer src="lib/3d-viewers/matviewer.min.js"></script> @@ -49,50 +51,29 @@ <header> <div id="logo-header" > - - <div style="float: left; width: 20%;padding-top:9px"> - <!--<a href="#/search/new"> NOMAD Encyclopedia</a>--> - <a href="https://nomad-coe.eu/index.php?page=materials-encyclopedia" target="_blank"> - Introduction to NOMAD Encyclopedia + <div "logo-panel"> + <a href="https://nomad-lab.eu/" target="_blank"> + The NOMAD Laboratory + <img src="img/nomad-logo.png" /> <!--style="padding-top:10px"--> </a> </div> - - <div style="float: right; width: 16%;text-align: right; padding-top:5px; "> + <div id="spacer"></div> + <div id="auth-panel"> <span style="vertical-align: middle;"> - - <span id="guest-user">Guest - <a style="text-decoration: underline">(LOGIN)</a> - </span> - - <span id="auth-user" style="display: none"> + <span id="auth-user"> <span id="user-name"></span> - <a id="logout-button" style="text-decoration: underline; cursor: pointer" > - (logout)</a> + <span id="login-button" style="display: none; text-decoration: underline; cursor: pointer">(LOGIN)</span> + <span id="logout-button" style="display: none; text-decoration: underline; cursor: pointer">(LOGOUT)</span> </span> - </span> <img src="img/user.png" /> </div> - - <div style="float: right; text-align: right; width: 40%;" > - <a href="https://nomad-coe.eu/" target="_blank"> - The NOMAD Laboratory - <img src="img/nomad-logo.png" /> <!--style="padding-top:10px"--> - </a> - </div> - - <div style="clear: both;"></div> - </div> - <!--</div>--> - - </header> <div id="second-header"> <div style="float: left; width: 60%;"> <a href=""> <img src="img/NOMADEncyclopedia.png" /></a> - </div> <div style="float: right; width: 30%;text-align: right"> @@ -109,6 +90,5 @@ <div id="content"> </div> -<!-- <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script>--> </body> </html> diff --git a/client/keycloak.min.js b/client/keycloak.min.js new file mode 100644 index 0000000000000000000000000000000000000000..870382560fa56b6aa101ca01dd7919005ef35a79 --- /dev/null +++ b/client/keycloak.min.js @@ -0,0 +1,82 @@ +/* + Chen, Yi-Cyuan 2014-2017 + @license MIT + Kirill, Fomichev 2014 + @license MIT + Hakes, Taylor 2014 + @license MIT +*/ +(function(K,L){if("object"===typeof exports)"object"===typeof module?module.exports=L(require("js-sha256"),require("base64-js")):exports.keycloak=L(require("js-sha256"),require("base64-js"));else{!function(){function p(b,a){a?(r[0]=r[16]=r[1]=r[2]=r[3]=r[4]=r[5]=r[6]=r[7]=r[8]=r[9]=r[10]=r[11]=r[12]=r[13]=r[14]=r[15]=0,this.blocks=r):this.blocks=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];b?(this.h0=3238371032,this.h1=914150663,this.h2=812702999,this.h3=4144912697,this.h4=4290775857,this.h5=1750603025,this.h6= +1694076839,this.h7=3204075428):(this.h0=1779033703,this.h1=3144134277,this.h2=1013904242,this.h3=2773480762,this.h4=1359893119,this.h5=2600822924,this.h6=528734635,this.h7=1541459225);this.block=this.start=this.bytes=this.hBytes=0;this.finalized=this.hashed=!1;this.first=!0;this.is224=b}function w(b,a,d){var z;z=typeof b;if("string"===z){var e,x=[],k=b.length,c=0;for(z=0;z<k;++z)128>(e=b.charCodeAt(z))?x[c++]=e:2048>e?(x[c++]=192|e>>6,x[c++]=128|63&e):55296>e||57344<=e?(x[c++]=224|e>>12,x[c++]=128| +e>>6&63,x[c++]=128|63&e):(e=65536+((1023&e)<<10|1023&b.charCodeAt(++z)),x[c++]=240|e>>18,x[c++]=128|e>>12&63,x[c++]=128|e>>6&63,x[c++]=128|63&e);b=x}else{if("object"!==z)throw Error(l);if(null===b)throw Error(l);if(D&&b.constructor===ArrayBuffer)b=new Uint8Array(b);else if(!(Array.isArray(b)||D&&ArrayBuffer.isView(b)))throw Error(l);}64<b.length&&(b=(new p(a,!0)).update(b).array());e=[];x=[];for(z=0;64>z;++z)k=b[z]||0,e[z]=92^k,x[z]=54^k;p.call(this,a,d);this.update(x);this.oKeyPad=e;this.inner=!0; +this.sharedMemory=d}var l="input is invalid type",u="object"==typeof window,n=u?window:{};n.JS_SHA256_NO_WINDOW&&(u=!1);var u=!u&&"object"==typeof self,y=!n.JS_SHA256_NO_NODE_JS&&"object"==typeof process&&process.versions&&process.versions.node;y?n=global:u&&(n=self);var u=!n.JS_SHA256_NO_COMMON_JS&&"object"==typeof module&&module.exports,q="function"==typeof define&&define.amd,D=!n.JS_SHA256_NO_ARRAY_BUFFER&&"undefined"!=typeof ArrayBuffer,a="0123456789abcdef".split(""),g=[-2147483648,8388608,32768, +128],d=[24,16,8,0],c=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817, +3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298],f=["hex","array","digest","arrayBuffer"],r=[];!n.JS_SHA256_NO_NODE_JS&&Array.isArray||(Array.isArray=function(b){return"[object Array]"===Object.prototype.toString.call(b)});!D||!n.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW&&ArrayBuffer.isView||(ArrayBuffer.isView=function(b){return"object"==typeof b&&b.buffer&& +b.buffer.constructor===ArrayBuffer});var m=function(b,a){return function(d){return(new p(a,!0)).update(d)[b]()}},I=function(b){var a=m("hex",b);y&&(a=Q(a,b));a.create=function(){return new p(b)};a.update=function(b){return a.create().update(b)};for(var d=0;d<f.length;++d){var c=f[d];a[c]=m(c,b)}return a},Q=function(b,a){var d=eval("require('crypto')"),c=eval("require('buffer').Buffer"),e=a?"sha224":"sha256";return function(a){if("string"==typeof a)return d.createHash(e).update(a,"utf8").digest("hex"); +if(null===a||void 0===a)throw Error(l);return a.constructor===ArrayBuffer&&(a=new Uint8Array(a)),Array.isArray(a)||ArrayBuffer.isView(a)||a.constructor===c?d.createHash(e).update(new c(a)).digest("hex"):b(a)}},F=function(b,a){return function(d,c){return(new w(d,a,!0)).update(c)[b]()}},J=function(b){var a=F("hex",b);a.create=function(a){return new w(a,b)};a.update=function(b,d){return a.create(b).update(d)};for(var d=0;d<f.length;++d){var c=f[d];a[c]=F(c,b)}return a};p.prototype.update=function(b){if(!this.finalized){var a, +c=typeof b;if("string"!==c){if("object"!==c)throw Error(l);if(null===b)throw Error(l);if(D&&b.constructor===ArrayBuffer)b=new Uint8Array(b);else if(!(Array.isArray(b)||D&&ArrayBuffer.isView(b)))throw Error(l);a=!0}for(var f,e=0,r=b.length,k=this.blocks;e<r;){if(this.hashed&&(this.hashed=!1,k[0]=this.block,k[16]=k[1]=k[2]=k[3]=k[4]=k[5]=k[6]=k[7]=k[8]=k[9]=k[10]=k[11]=k[12]=k[13]=k[14]=k[15]=0),a)for(c=this.start;e<r&&64>c;++e)k[c>>2]|=b[e]<<d[3&c++];else for(c=this.start;e<r&&64>c;++e)128>(f=b.charCodeAt(e))? +k[c>>2]|=f<<d[3&c++]:2048>f?(k[c>>2]|=(192|f>>6)<<d[3&c++],k[c>>2]|=(128|63&f)<<d[3&c++]):55296>f||57344<=f?(k[c>>2]|=(224|f>>12)<<d[3&c++],k[c>>2]|=(128|f>>6&63)<<d[3&c++],k[c>>2]|=(128|63&f)<<d[3&c++]):(f=65536+((1023&f)<<10|1023&b.charCodeAt(++e)),k[c>>2]|=(240|f>>18)<<d[3&c++],k[c>>2]|=(128|f>>12&63)<<d[3&c++],k[c>>2]|=(128|f>>6&63)<<d[3&c++],k[c>>2]|=(128|63&f)<<d[3&c++]);this.lastByteIndex=c;this.bytes+=c-this.start;64<=c?(this.block=k[16],this.start=c-64,this.hash(),this.hashed=!0):this.start= +c}return 4294967295<this.bytes&&(this.hBytes+=this.bytes/4294967296<<0,this.bytes%=4294967296),this}};p.prototype.finalize=function(){if(!this.finalized){this.finalized=!0;var b=this.blocks,a=this.lastByteIndex;b[16]=this.block;b[a>>2]|=g[3&a];this.block=b[16];56<=a&&(this.hashed||this.hash(),b[0]=this.block,b[16]=b[1]=b[2]=b[3]=b[4]=b[5]=b[6]=b[7]=b[8]=b[9]=b[10]=b[11]=b[12]=b[13]=b[14]=b[15]=0);b[14]=this.hBytes<<3|this.bytes>>>29;b[15]=this.bytes<<3;this.hash()}};p.prototype.hash=function(){var b, +a,d,f,e,r,k,m=this.h0,g=this.h1,n=this.h2,l=this.h3,p=this.h4,h=this.h5,t=this.h6,A=this.h7,C=this.blocks;for(b=16;64>b;++b)a=((e=C[b-15])>>>7|e<<25)^(e>>>18|e<<14)^e>>>3,d=((e=C[b-2])>>>17|e<<15)^(e>>>19|e<<13)^e>>>10,C[b]=C[b-16]+a+C[b-7]+d<<0;k=g&n;for(b=0;64>b;b+=4)this.first?(this.is224?(r=300032,A=(e=C[0]-1413257819)-150054599<<0,l=e+24177077<<0):(r=704751109,A=(e=C[0]-210244248)-1521486534<<0,l=e+143694565<<0),this.first=!1):(a=(m>>>2|m<<30)^(m>>>13|m<<19)^(m>>>22|m<<10),f=(r=m&g)^m&n^k,A= +l+(e=A+((p>>>6|p<<26)^(p>>>11|p<<21)^(p>>>25|p<<7))+(p&h^~p&t)+c[b]+C[b])<<0,l=e+(a+f)<<0),a=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),f=(k=l&m)^l&g^r,t=n+(e=t+((A>>>6|A<<26)^(A>>>11|A<<21)^(A>>>25|A<<7))+(A&p^~A&h)+c[b+1]+C[b+1])<<0,a=((n=e+(a+f)<<0)>>>2|n<<30)^(n>>>13|n<<19)^(n>>>22|n<<10),f=(d=n&l)^n&m^k,h=g+(e=h+((t>>>6|t<<26)^(t>>>11|t<<21)^(t>>>25|t<<7))+(t&A^~t&p)+c[b+2]+C[b+2])<<0,a=((g=e+(a+f)<<0)>>>2|g<<30)^(g>>>13|g<<19)^(g>>>22|g<<10),f=(k=g&n)^g&l^d,p=m+(e=p+((h>>>6|h<<26)^(h>>>11| +h<<21)^(h>>>25|h<<7))+(h&t^~h&A)+c[b+3]+C[b+3])<<0,m=e+(a+f)<<0;this.h0=this.h0+m<<0;this.h1=this.h1+g<<0;this.h2=this.h2+n<<0;this.h3=this.h3+l<<0;this.h4=this.h4+p<<0;this.h5=this.h5+h<<0;this.h6=this.h6+t<<0;this.h7=this.h7+A<<0};p.prototype.hex=function(){this.finalize();var b=this.h0,c=this.h1,d=this.h2,f=this.h3,e=this.h4,m=this.h5,k=this.h6,g=this.h7,b=a[b>>28&15]+a[b>>24&15]+a[b>>20&15]+a[b>>16&15]+a[b>>12&15]+a[b>>8&15]+a[b>>4&15]+a[15&b]+a[c>>28&15]+a[c>>24&15]+a[c>>20&15]+a[c>>16&15]+a[c>> +12&15]+a[c>>8&15]+a[c>>4&15]+a[15&c]+a[d>>28&15]+a[d>>24&15]+a[d>>20&15]+a[d>>16&15]+a[d>>12&15]+a[d>>8&15]+a[d>>4&15]+a[15&d]+a[f>>28&15]+a[f>>24&15]+a[f>>20&15]+a[f>>16&15]+a[f>>12&15]+a[f>>8&15]+a[f>>4&15]+a[15&f]+a[e>>28&15]+a[e>>24&15]+a[e>>20&15]+a[e>>16&15]+a[e>>12&15]+a[e>>8&15]+a[e>>4&15]+a[15&e]+a[m>>28&15]+a[m>>24&15]+a[m>>20&15]+a[m>>16&15]+a[m>>12&15]+a[m>>8&15]+a[m>>4&15]+a[15&m]+a[k>>28&15]+a[k>>24&15]+a[k>>20&15]+a[k>>16&15]+a[k>>12&15]+a[k>>8&15]+a[k>>4&15]+a[15&k];return this.is224|| +(b+=a[g>>28&15]+a[g>>24&15]+a[g>>20&15]+a[g>>16&15]+a[g>>12&15]+a[g>>8&15]+a[g>>4&15]+a[15&g]),b};p.prototype.toString=p.prototype.hex;p.prototype.digest=function(){this.finalize();var b=this.h0,a=this.h1,c=this.h2,d=this.h3,f=this.h4,m=this.h5,k=this.h6,g=this.h7,b=[b>>24&255,b>>16&255,b>>8&255,255&b,a>>24&255,a>>16&255,a>>8&255,255&a,c>>24&255,c>>16&255,c>>8&255,255&c,d>>24&255,d>>16&255,d>>8&255,255&d,f>>24&255,f>>16&255,f>>8&255,255&f,m>>24&255,m>>16&255,m>>8&255,255&m,k>>24&255,k>>16&255,k>> +8&255,255&k];return this.is224||b.push(g>>24&255,g>>16&255,g>>8&255,255&g),b};p.prototype.array=p.prototype.digest;p.prototype.arrayBuffer=function(){this.finalize();var b=new ArrayBuffer(this.is224?28:32),a=new DataView(b);return a.setUint32(0,this.h0),a.setUint32(4,this.h1),a.setUint32(8,this.h2),a.setUint32(12,this.h3),a.setUint32(16,this.h4),a.setUint32(20,this.h5),a.setUint32(24,this.h6),this.is224||a.setUint32(28,this.h7),b};w.prototype=new p;w.prototype.finalize=function(){if(p.prototype.finalize.call(this), +this.inner){this.inner=!1;var b=this.array();p.call(this,this.is224,this.sharedMemory);this.update(this.oKeyPad);this.update(b);p.prototype.finalize.call(this)}};var B=I();B.sha256=B;B.sha224=I(!0);B.sha256.hmac=J();B.sha224.hmac=J(!0);u?module.exports=B:(n.sha256=B.sha256,n.sha224=B.sha224,q&&define(function(){return B}))}();(function(p){"object"===typeof exports&&"undefined"!==typeof module?module.exports=p():"function"===typeof define&&define.amd?define([],p):("undefined"!==typeof window?window: +"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).base64js=p()})(function(){return function(){function p(w,l,u){function n(q,a){if(!l[q]){if(!w[q]){var g="function"==typeof require&&require;if(!a&&g)return g(q,!0);if(y)return y(q,!0);a=Error("Cannot find module '"+q+"'");throw a.code="MODULE_NOT_FOUND",a;}a=l[q]={exports:{}};w[q][0].call(a.exports,function(a){return n(w[q][1][a]||a)},a,a.exports,p,w,l,u)}return l[q].exports}for(var y="function"==typeof require&&require,q=0;q< +u.length;q++)n(u[q]);return n}return p}()({"/":[function(p,w,l){function u(a){var g=a.length;if(0<g%4)throw Error("Invalid string. Length must be a multiple of 4");a=a.indexOf("\x3d");-1===a&&(a=g);return[a,a===g?0:4-a%4]}function n(a,g,d){for(var c=[],f=g;f<d;f+=3)g=(a[f]<<16&16711680)+(a[f+1]<<8&65280)+(a[f+2]&255),c.push(y[g>>18&63]+y[g>>12&63]+y[g>>6&63]+y[g&63]);return c.join("")}l.byteLength=function(a){a=u(a);var g=a[1];return 3*(a[0]+g)/4-g};l.toByteArray=function(a){var g,d=u(a);g=d[0];for(var d= +d[1],c=new D(3*(g+d)/4-d),f=0,r=0<d?g-4:g,m=0;m<r;m+=4)g=q[a.charCodeAt(m)]<<18|q[a.charCodeAt(m+1)]<<12|q[a.charCodeAt(m+2)]<<6|q[a.charCodeAt(m+3)],c[f++]=g>>16&255,c[f++]=g>>8&255,c[f++]=g&255;2===d&&(g=q[a.charCodeAt(m)]<<2|q[a.charCodeAt(m+1)]>>4,c[f++]=g&255);1===d&&(g=q[a.charCodeAt(m)]<<10|q[a.charCodeAt(m+1)]<<4|q[a.charCodeAt(m+2)]>>2,c[f++]=g>>8&255,c[f++]=g&255);return c};l.fromByteArray=function(a){for(var g=a.length,d=g%3,c=[],f=0,r=g-d;f<r;f+=16383)c.push(n(a,f,f+16383>r?r:f+16383)); +1===d?(a=a[g-1],c.push(y[a>>2]+y[a<<4&63]+"\x3d\x3d")):2===d&&(a=(a[g-2]<<8)+a[g-1],c.push(y[a>>10]+y[a>>4&63]+y[a<<2&63]+"\x3d"));return c.join("")};var y=[],q=[],D="undefined"!==typeof Uint8Array?Uint8Array:Array;for(p=0;64>p;++p)y[p]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[p],q["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charCodeAt(p)]=p;q[45]=62;q[95]=63},{}]},{},[])("/")});!function(p,w){"object"==typeof exports&&"undefined"!=typeof module?w(): +"function"==typeof define&&define.amd?define(w):w()}(0,function(){function p(a){var c=this.constructor;return this.then(function(d){return c.resolve(a()).then(function(){return d})},function(d){return c.resolve(a()).then(function(){return c.reject(d)})})}function w(){}function l(a){if(!(this instanceof l))throw new TypeError("Promises must be constructed via new");if("function"!=typeof a)throw new TypeError("not a function");this._state=0;this._handled=!1;this._value=void 0;this._deferreds=[];D(a, +this)}function u(a,c){for(;3===a._state;)a=a._value;0!==a._state?(a._handled=!0,l._immediateFn(function(){var d=1===a._state?c.onFulfilled:c.onRejected;if(null!==d){var g;try{g=d(a._value)}catch(m){return void y(c.promise,m)}n(c.promise,g)}else(1===a._state?n:y)(c.promise,a._value)})):a._deferreds.push(c)}function n(a,c){try{if(c===a)throw new TypeError("A promise cannot be resolved with itself.");if(c&&("object"==typeof c||"function"==typeof c)){var d=c.then;if(c instanceof l)return a._state=3,a._value= +c,void q(a);if("function"==typeof d)return void D(function(a,c){return function(){a.apply(c,arguments)}}(d,c),a)}a._state=1;a._value=c;q(a)}catch(r){y(a,r)}}function y(a,c){a._state=2;a._value=c;q(a)}function q(a){2===a._state&&0===a._deferreds.length&&l._immediateFn(function(){a._handled||l._unhandledRejectionFn(a._value)});for(var c=0,d=a._deferreds.length;d>c;c++)u(a,a._deferreds[c]);a._deferreds=null}function D(a,c){var d=!1;try{a(function(a){d||(d=!0,n(c,a))},function(a){d||(d=!0,y(c,a))})}catch(r){d|| +(d=!0,y(c,r))}}var a=setTimeout;l.prototype["catch"]=function(a){return this.then(null,a)};l.prototype.then=function(a,c){var d=new this.constructor(w);return u(this,new function(a,c,d){this.onFulfilled="function"==typeof a?a:null;this.onRejected="function"==typeof c?c:null;this.promise=d}(a,c,d)),d};l.prototype["finally"]=p;l.all=function(a){return new l(function(c,d){function g(a,m){try{if(m&&("object"==typeof m||"function"==typeof m)){var n=m.then;if("function"==typeof n)return void n.call(m,function(b){g(a, +b)},d)}f[a]=m;0==--l&&c(f)}catch(b){d(b)}}if(!a||"undefined"==typeof a.length)return d(new TypeError("Promise.all accepts an array"));var f=Array.prototype.slice.call(a);if(0===f.length)return c([]);for(var l=f.length,n=0;f.length>n;n++)g(n,f[n])})};l.resolve=function(a){return a&&"object"==typeof a&&a.constructor===l?a:new l(function(c){c(a)})};l.reject=function(a){return new l(function(c,d){d(a)})};l.race=function(a){return new l(function(c,d){if(!a||"undefined"==typeof a.length)return d(new TypeError("Promise.race accepts an array")); +for(var g=0,f=a.length;f>g;g++)l.resolve(a[g]).then(c,d)})};l._immediateFn="function"==typeof setImmediate&&function(a){setImmediate(a)}||function(d){a(d,0)};l._unhandledRejectionFn=function(a){void 0!==console&&console&&console.warn("Possible Unhandled Promise Rejection:",a)};var g=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if("undefined"!=typeof global)return global;throw Error("unable to locate global object");}();"Promise"in g?g.Promise.prototype["finally"]|| +(g.Promise.prototype["finally"]=p):g.Promise=l});var E=L(K.sha256,K.base64js);K.Keycloak=E;"function"===typeof define&&define.amd&&define("keycloak",[],function(){return E})}})(window,function(K,L){function E(){u||(u=!0,console.warn("[KEYCLOAK] Usage of legacy style promise methods such as `.error()` and `.success()` has been deprecated and support will be removed in future versions. Use standard style promise methods such as `.then() and `.catch()` instead."))}function p(n){n.__proto__=w.prototype; +return n}function w(n){return p(new Promise(n))}function l(n){function p(b,a){var h;var c=window.crypto||window.msCrypto;if(c&&c.getRandomValues&&window.Uint8Array)h=new Uint8Array(b),c.getRandomValues(h);else for(h=Array(b),c=0;c<h.length;c++)h[c]=Math.floor(256*Math.random());for(var c=Array(b),t=0;t<b;t++)c[t]=a.charCodeAt(h[t]%a.length);return String.fromCharCode.apply(null,c)}function q(){if("undefined"!==typeof b.authServerUrl)return"/"==b.authServerUrl.charAt(b.authServerUrl.length-1)?b.authServerUrl+ +"realms/"+encodeURIComponent(b.realm):b.authServerUrl+"/realms/"+encodeURIComponent(b.realm)}function u(a,c){function h(h,t,v,d){e=(e+(new Date).getTime())/2;g(h,t,v,e);N&&(b.tokenParsed&&b.tokenParsed.nonce!=a.storedNonce||b.refreshTokenParsed&&b.refreshTokenParsed.nonce!=a.storedNonce||b.idTokenParsed&&b.idTokenParsed.nonce!=a.storedNonce)?(G("[KEYCLOAK] Invalid nonce, clearing token"),b.clearToken(),c&&c.setError()):d&&(b.onAuthSuccess&&b.onAuthSuccess(),c&&c.setSuccess())}var t=a.code,d=a.error, +v=a.prompt,e=(new Date).getTime();a.kc_action_status&&b.onActionUpdate&&b.onActionUpdate(a.kc_action_status);if(d)"none"!=v?(t={error:d,error_description:a.error_description},b.onAuthError&&b.onAuthError(t),c&&c.setError(t)):c&&c.setSuccess();else if("standard"!=b.flow&&(a.access_token||a.id_token)&&h(a.access_token,null,a.id_token,!0),"implicit"!=b.flow&&t){var t="code\x3d"+t+"\x26grant_type\x3dauthorization_code",d=b.endpoints.token(),f=new XMLHttpRequest;f.open("POST",d,!0);f.setRequestHeader("Content-type", +"application/x-www-form-urlencoded");t+="\x26client_id\x3d"+encodeURIComponent(b.clientId);t+="\x26redirect_uri\x3d"+a.redirectUri;a.pkceCodeVerifier&&(t+="\x26code_verifier\x3d"+a.pkceCodeVerifier);f.withCredentials=!0;f.onreadystatechange=function(){if(4==f.readyState)if(200==f.status){var a=JSON.parse(f.responseText);h(a.access_token,a.refresh_token,a.id_token,"standard"===b.flow);E()}else b.onAuthError&&b.onAuthError(),c&&c.setError()};f.send(t)}}function a(a){function h(a){b.endpoints=a?{authorize:function(){return a.authorization_endpoint}, +token:function(){return a.token_endpoint},logout:function(){if(!a.end_session_endpoint)throw"Not supported by the OIDC server";return a.end_session_endpoint},checkSessionIframe:function(){if(!a.check_session_iframe)throw"Not supported by the OIDC server";return a.check_session_iframe},register:function(){throw'Redirection to "Register user" page not supported in standard OIDC mode';},userinfo:function(){if(!a.userinfo_endpoint)throw"Not supported by the OIDC server";return a.userinfo_endpoint}}:{authorize:function(){return q()+ +"/protocol/openid-connect/auth"},token:function(){return q()+"/protocol/openid-connect/token"},logout:function(){return q()+"/protocol/openid-connect/logout"},checkSessionIframe:function(){var a=q()+"/protocol/openid-connect/login-status-iframe.html";b.iframeVersion&&(a=a+"?version\x3d"+b.iframeVersion);return a},register:function(){return q()+"/protocol/openid-connect/registrations"},userinfo:function(){return q()+"/protocol/openid-connect/userinfo"}}}var c=m(),d;n?"string"===typeof n&&(d=n):d="keycloak.json"; +if(d){var e=new XMLHttpRequest;e.open("GET",d,!0);e.setRequestHeader("Accept","application/json");e.onreadystatechange=function(){if(4==e.readyState)if(200==e.status||0==e.status&&e.responseText&&e.responseURL.startsWith("file:")){var a=JSON.parse(e.responseText);b.authServerUrl=a["auth-server-url"];b.realm=a.realm;b.clientId=a.resource;h(null);c.setSuccess()}else c.setError()};e.send()}else{if(!n.clientId)throw"clientId missing";b.clientId=n.clientId;if(a=n.oidcProvider)"string"===typeof a?(a="/"== +a.charAt(a.length-1)?a+".well-known/openid-configuration":a+"/.well-known/openid-configuration",e=new XMLHttpRequest,e.open("GET",a,!0),e.setRequestHeader("Accept","application/json"),e.onreadystatechange=function(){if(4==e.readyState)if(200==e.status||0==e.status&&e.responseText&&e.responseURL.startsWith("file:")){var b=JSON.parse(e.responseText);h(b);c.setSuccess()}else c.setError()},e.send()):(h(a),c.setSuccess());else{if(!n.url)for(a=document.getElementsByTagName("script"),d=0;d<a.length;d++)if(a[d].src.match(/.*keycloak\.js/)){n.url= +a[d].src.substr(0,a[d].src.indexOf("/js/keycloak.js"));break}if(!n.realm)throw"realm missing";b.authServerUrl=n.url;b.realm=n.realm;h(null);c.setSuccess()}}return c.promise}function g(a,c,e,f){b.tokenTimeoutHandle&&(clearTimeout(b.tokenTimeoutHandle),b.tokenTimeoutHandle=null);c?(b.refreshToken=c,b.refreshTokenParsed=d(c)):(delete b.refreshToken,delete b.refreshTokenParsed);e?(b.idToken=e,b.idTokenParsed=d(e)):(delete b.idToken,delete b.idTokenParsed);if(a){if(b.token=a,b.tokenParsed=d(a),b.sessionId= +b.tokenParsed.session_state,b.authenticated=!0,b.subject=b.tokenParsed.sub,b.realmAccess=b.tokenParsed.realm_access,b.resourceAccess=b.tokenParsed.resource_access,f&&(b.timeSkew=Math.floor(f/1E3)-b.tokenParsed.iat),null!=b.timeSkew&&(G("[KEYCLOAK] Estimated time difference between browser and server is "+b.timeSkew+" seconds"),b.onTokenExpired))if(a=1E3*(b.tokenParsed.exp-(new Date).getTime()/1E3+b.timeSkew),G("[KEYCLOAK] Token expires in "+Math.round(a/1E3)+" s"),0>=a)b.onTokenExpired();else b.tokenTimeoutHandle= +setTimeout(b.onTokenExpired,a)}else delete b.token,delete b.tokenParsed,delete b.subject,delete b.realmAccess,delete b.resourceAccess,b.authenticated=!1}function d(a){a=a.split(".")[1];a=a.replace("/-/g","+");a=a.replace("/_/g","/");switch(a.length%4){case 0:break;case 2:a+="\x3d\x3d";break;case 3:a+="\x3d";break;default:throw"Invalid token";}a=(a+"\x3d\x3d\x3d").slice(0,a.length+a.length%4);a=a.replace(/-/g,"+").replace(/_/g,"/");a=decodeURIComponent(escape(atob(a)));return a=JSON.parse(a)}function c(){var a= +p(36,"0123456789abcdef").split("");a[14]="4";a[19]="0123456789abcdef".substr(a[19]&3|8,1);a[8]=a[13]=a[18]=a[23]="-";return a.join("")}function f(a){a:{var h;switch(b.flow){case "standard":h=["code","state","session_state","kc_action_status"];break;case "implicit":h="access_token token_type id_token state session_state expires_in kc_action_status".split(" ");break;case "hybrid":h="access_token id_token code state session_state kc_action_status".split(" ")}h.push("error");h.push("error_description"); +h.push("error_uri");var c=a.indexOf("?"),d=a.indexOf("#"),e,v;"query"===b.responseMode&&-1!==c?(e=a.substring(0,c),v=r(a.substring(c+1,-1!==d?d:a.length),h),""!==v.paramsString&&(e+="?"+v.paramsString),-1!==d&&(e+=a.substring(d))):"fragment"===b.responseMode&&-1!==d&&(e=a.substring(0,d),v=r(a.substring(d+1),h),""!==v.paramsString&&(e+="#"+v.paramsString));if(v&&v.oauthParams)if("standard"===b.flow||"hybrid"===b.flow){if((v.oauthParams.code||v.oauthParams.error)&&v.oauthParams.state){v.oauthParams.newUrl= +e;a=v.oauthParams;break a}}else if("implicit"===b.flow&&(v.oauthParams.access_token||v.oauthParams.error)&&v.oauthParams.state){v.oauthParams.newUrl=e;a=v.oauthParams;break a}a=void 0}if(a){if(h=M.get(a.state))a.valid=!0,a.redirectUri=h.redirectUri,a.storedNonce=h.nonce,a.prompt=h.prompt,a.pkceCodeVerifier=h.pkceCodeVerifier;return a}}function r(a,b){a=a.split("\x26");for(var h={paramsString:"",oauthParams:{}},c=0;c<a.length;c++){var d=a[c].indexOf("\x3d"),e=a[c].slice(0,d);-1!==b.indexOf(e)?h.oauthParams[e]= +a[c].slice(d+1):(""!==h.paramsString&&(h.paramsString+="\x26"),h.paramsString+=a[c])}return h}function m(){var a={setSuccess:function(b){a.resolve(b)},setError:function(b){a.reject(b)}};a.promise=new w(function(b,c){a.resolve=b;a.reject=c});return a}function I(){var a=m();if(!e.enable||e.iframe)return a.setSuccess(),a.promise;var c=document.createElement("iframe");e.iframe=c;c.onload=function(){var c=b.endpoints.authorize();"/"===c.charAt(0)?(c=window.location.origin?window.location.origin:window.location.protocol+ +"//"+window.location.hostname+(window.location.port?":"+window.location.port:""),e.iframeOrigin=c):e.iframeOrigin=c.substring(0,c.indexOf("/",8));a.setSuccess()};var d=b.endpoints.checkSessionIframe();c.setAttribute("src",d);c.setAttribute("title","keycloak-session-iframe");c.style.display="none";document.body.appendChild(c);window.addEventListener("message",function(a){if(a.origin===e.iframeOrigin&&e.iframe.contentWindow===a.source&&("unchanged"==a.data||"changed"==a.data||"error"==a.data)){"unchanged"!= +a.data&&b.clearToken();for(var c=e.callbackList.splice(0,e.callbackList.length),h=c.length-1;0<=h;--h){var d=c[h];"error"==a.data?d.setError():d.setSuccess("unchanged"==a.data)}}},!1);return a.promise}function E(){e.enable&&b.token&&setTimeout(function(){F().then(function(a){a&&E()})},1E3*e.interval)}function F(){var a=m();if(e.iframe&&e.iframeOrigin){var c=b.clientId+" "+(b.sessionId?b.sessionId:"");e.callbackList.push(a);var d=e.iframeOrigin;1==e.callbackList.length&&e.iframe.contentWindow.postMessage(c, +d)}else a.setSuccess();return a.promise}function J(a){if(!a||"default"==a)return{login:function(a){window.location.replace(b.createLoginUrl(a));return m().promise},logout:function(a){window.location.replace(b.createLogoutUrl(a));return m().promise},register:function(a){window.location.replace(b.createRegisterUrl(a));return m().promise},accountManagement:function(){var a=b.createAccountUrl();if("undefined"!==typeof a)window.location.href=a;else throw"Not supported by the OIDC server";return m().promise}, +redirectUri:function(a,c){1==arguments.length&&(c=!0);return a&&a.redirectUri?a.redirectUri:b.redirectUri?b.redirectUri:location.href}};if("cordova"==a){e.enable=!1;var c=function(a,b,c){return window.cordova&&window.cordova.InAppBrowser?window.cordova.InAppBrowser.open(a,b,c):window.open(a,b,c)},h=function(a){return a&&a.cordovaOptions?Object.keys(a.cordovaOptions).reduce(function(b,c){b[c]=a.cordovaOptions[c];return b},{}):{}},d=function(a){return Object.keys(a).reduce(function(b,c){b.push(c+"\x3d"+ +a[c]);return b},[]).join(",")},g=function(a){var b=h(a);b.location="no";a&&"none"==a.prompt&&(b.hidden="yes");return d(b)};return{login:function(a){var h=m(),d=g(a);a=b.createLoginUrl(a);var e=c(a,"_blank",d),t=!1,v=!1;e.addEventListener("loadstart",function(a){0==a.url.indexOf("http://localhost")&&(a=f(a.url),u(a,h),v=!0,e.close(),t=!0)});e.addEventListener("loaderror",function(a){t||(0==a.url.indexOf("http://localhost")?(a=f(a.url),u(a,h),v=!0,e.close(),t=!0):(h.setError(),v=!0,e.close()))});e.addEventListener("exit", +function(a){v||h.setError({reason:"closed_by_user"})});return h.promise},logout:function(a){var h=m();a=b.createLogoutUrl(a);var d=c(a,"_blank","location\x3dno,hidden\x3dyes"),e;d.addEventListener("loadstart",function(a){0==a.url.indexOf("http://localhost")&&d.close()});d.addEventListener("loaderror",function(a){0!=a.url.indexOf("http://localhost")&&(e=!0);d.close()});d.addEventListener("exit",function(a){e?h.setError():(b.clearToken(),h.setSuccess())});return h.promise},register:function(a){var h= +m(),d=b.createRegisterUrl();a=g(a);var e=c(d,"_blank",a);e.addEventListener("loadstart",function(a){0==a.url.indexOf("http://localhost")&&(e.close(),a=f(a.url),u(a,h))});return h.promise},accountManagement:function(){var a=b.createAccountUrl();if("undefined"!==typeof a){var h=c(a,"_blank","location\x3dno");h.addEventListener("loadstart",function(a){0==a.url.indexOf("http://localhost")&&h.close()})}else throw"Not supported by the OIDC server";},redirectUri:function(a){return"http://localhost"}}}if("cordova-native"== +a)return e.enable=!1,{login:function(a){var c=m();a=b.createLoginUrl(a);universalLinks.subscribe("keycloak",function(a){universalLinks.unsubscribe("keycloak");window.cordova.plugins.browsertab.close();a=f(a.url);u(a,c)});window.cordova.plugins.browsertab.openUrl(a);return c.promise},logout:function(a){var c=m();a=b.createLogoutUrl(a);universalLinks.subscribe("keycloak",function(a){universalLinks.unsubscribe("keycloak");window.cordova.plugins.browsertab.close();b.clearToken();c.setSuccess()});window.cordova.plugins.browsertab.openUrl(a); +return c.promise},register:function(a){var c=m();a=b.createRegisterUrl(a);universalLinks.subscribe("keycloak",function(a){universalLinks.unsubscribe("keycloak");window.cordova.plugins.browsertab.close();a=f(a.url);u(a,c)});window.cordova.plugins.browsertab.openUrl(a);return c.promise},accountManagement:function(){var a=b.createAccountUrl();if("undefined"!==typeof a)window.cordova.plugins.browsertab.openUrl(a);else throw"Not supported by the OIDC server";},redirectUri:function(a){return a&&a.redirectUri? +a.redirectUri:b.redirectUri?b.redirectUri:"http://localhost"}};throw"invalid adapter type: "+a;}function B(a){return function(){b.enableLogging&&a.apply(console,Array.prototype.slice.call(arguments))}}if(!(this instanceof l))return new l(n);for(var b=this,x,z=[],M,e={enable:!0,callbackList:[],interval:5},H=document.getElementsByTagName("script"),k=0;k<H.length;k++)-1===H[k].src.indexOf("keycloak.js")&&-1===H[k].src.indexOf("keycloak.min.js")||-1===H[k].src.indexOf("version\x3d")||(b.iframeVersion= +H[k].src.substring(H[k].src.indexOf("version\x3d")+8).split("\x26")[0]);var N=!0,G=B(console.info),R=B(console.warn);b.init=function(c){function d(){var a=function(a){a||(d.prompt="none");b.login(d).then(function(){l.setSuccess()}).catch(function(){l.setError()})},h=function(){var a=document.createElement("iframe"),c=b.createLoginUrl({prompt:"none",redirectUri:b.silentCheckSsoRedirectUri});a.setAttribute("src",c);a.setAttribute("title","keycloak-silent-check-sso");a.style.display="none";document.body.appendChild(a); +var h=function(b){b.origin===window.location.origin&&a.contentWindow===b.source&&(b=f(b.data),u(b,l),document.body.removeChild(a),window.removeEventListener("message",h))};window.addEventListener("message",h)},d={};switch(c.onLoad){case "check-sso":e.enable?I().then(function(){F().then(function(c){c?l.setSuccess():b.silentCheckSsoRedirectUri?h():a(!1)}).catch(function(){l.setError()})}):b.silentCheckSsoRedirectUri?h():a(!1);break;case "login-required":a(!0);break;default:throw"Invalid value for onLoad"; +}}b.authenticated=!1;a:{try{M=new O;break a}catch(v){}M=new P}var h=["default","cordova","cordova-native"];x=c&&-1<h.indexOf(c.adapter)?J(c.adapter):c&&"object"===typeof c.adapter?c.adapter:window.Cordova||window.cordova?J("cordova"):J();if(c){"undefined"!==typeof c.useNonce&&(N=c.useNonce);"undefined"!==typeof c.checkLoginIframe&&(e.enable=c.checkLoginIframe);c.checkLoginIframeInterval&&(e.interval=c.checkLoginIframeInterval);"login-required"===c.onLoad&&(b.loginRequired=!0);if(c.responseMode)if("query"=== +c.responseMode||"fragment"===c.responseMode)b.responseMode=c.responseMode;else throw"Invalid value for responseMode";if(c.flow){switch(c.flow){case "standard":b.responseType="code";break;case "implicit":b.responseType="id_token token";break;case "hybrid":b.responseType="code id_token token";break;default:throw"Invalid value for flow";}b.flow=c.flow}null!=c.timeSkew&&(b.timeSkew=c.timeSkew);c.redirectUri&&(b.redirectUri=c.redirectUri);c.silentCheckSsoRedirectUri&&(b.silentCheckSsoRedirectUri=c.silentCheckSsoRedirectUri); +if(c.pkceMethod){if("S256"!==c.pkceMethod)throw"Invalid value for pkceMethod";b.pkceMethod=c.pkceMethod}b.enableLogging="boolean"===typeof c.enableLogging?c.enableLogging:!1}b.responseMode||(b.responseMode="fragment");b.responseType||(b.responseType="code",b.flow="standard");var k=m(),l=m();l.promise.then(function(){b.onReady&&b.onReady(b.authenticated);k.setSuccess(b.authenticated)}).catch(function(a){k.setError(a)});h=a(n);h.then(function(){var a=f(window.location.href);a&&window.history.replaceState(window.history.state, +null,a.newUrl);if(a&&a.valid)return I().then(function(){u(a,l)}).catch(function(a){l.setError()});c?c.token&&c.refreshToken?(g(c.token,c.refreshToken,c.idToken),e.enable?I().then(function(){F().then(function(a){a?(b.onAuthSuccess&&b.onAuthSuccess(),l.setSuccess(),E()):l.setSuccess()}).catch(function(){l.setError()})}):b.updateToken(-1).then(function(){b.onAuthSuccess&&b.onAuthSuccess();l.setSuccess()}).catch(function(){b.onAuthError&&b.onAuthError();c.onLoad?d():l.setError()})):c.onLoad?d():l.setSuccess(): +l.setSuccess()});h.catch(function(){k.setError()});return k.promise};b.login=function(a){return x.login(a)};b.createLoginUrl=function(a){var d=c(),e=c(),h=x.redirectUri(a),f={state:d,nonce:e,redirectUri:encodeURIComponent(h)};a&&a.prompt&&(f.prompt=a.prompt);var g;g=a&&"register"==a.action?b.endpoints.register():b.endpoints.authorize();var k;k=a&&a.scope?-1!=a.scope.indexOf("openid")?a.scope:"openid "+a.scope:"openid";d=g+"?client_id\x3d"+encodeURIComponent(b.clientId)+"\x26redirect_uri\x3d"+encodeURIComponent(h)+ +"\x26state\x3d"+encodeURIComponent(d)+"\x26response_mode\x3d"+encodeURIComponent(b.responseMode)+"\x26response_type\x3d"+encodeURIComponent(b.responseType)+"\x26scope\x3d"+encodeURIComponent(k);N&&(d=d+"\x26nonce\x3d"+encodeURIComponent(e));a&&a.prompt&&(d+="\x26prompt\x3d"+encodeURIComponent(a.prompt));a&&a.maxAge&&(d+="\x26max_age\x3d"+encodeURIComponent(a.maxAge));a&&a.loginHint&&(d+="\x26login_hint\x3d"+encodeURIComponent(a.loginHint));a&&a.idpHint&&(d+="\x26kc_idp_hint\x3d"+encodeURIComponent(a.idpHint)); +a&&a.action&&"register"!=a.action&&(d+="\x26kc_action\x3d"+encodeURIComponent(a.action));a&&a.locale&&(d+="\x26ui_locales\x3d"+encodeURIComponent(a.locale));if(b.pkceMethod){a=p(96,"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");f.pkceCodeVerifier=a;a:switch(b.pkceMethod){case "S256":a=new Uint8Array(K.arrayBuffer(a));a=L.fromByteArray(a).replace(/\+/g,"-").replace(/\//g,"_").replace(/\=/g,"");break a;default:throw"Invalid value for pkceMethod";}d=d+("\x26code_challenge\x3d"+a)+ +("\x26code_challenge_method\x3d"+b.pkceMethod)}M.add(f);return d};b.logout=function(a){return x.logout(a)};b.createLogoutUrl=function(a){return b.endpoints.logout()+"?redirect_uri\x3d"+encodeURIComponent(x.redirectUri(a,!1))};b.register=function(a){return x.register(a)};b.createRegisterUrl=function(a){a||(a={});a.action="register";return b.createLoginUrl(a)};b.createAccountUrl=function(a){var c=q(),d=void 0;"undefined"!==typeof c&&(d=c+"/account?referrer\x3d"+encodeURIComponent(b.clientId)+"\x26referrer_uri\x3d"+ +encodeURIComponent(x.redirectUri(a)));return d};b.accountManagement=function(){return x.accountManagement()};b.hasRealmRole=function(a){var c=b.realmAccess;return!!c&&0<=c.roles.indexOf(a)};b.hasResourceRole=function(a,c){if(!b.resourceAccess)return!1;c=b.resourceAccess[c||b.clientId];return!!c&&0<=c.roles.indexOf(a)};b.loadUserProfile=function(){var a=q()+"/account",c=new XMLHttpRequest;c.open("GET",a,!0);c.setRequestHeader("Accept","application/json");c.setRequestHeader("Authorization","bearer "+ +b.token);var d=m();c.onreadystatechange=function(){4==c.readyState&&(200==c.status?(b.profile=JSON.parse(c.responseText),d.setSuccess(b.profile)):d.setError())};c.send();return d.promise};b.loadUserInfo=function(){var a=b.endpoints.userinfo(),c=new XMLHttpRequest;c.open("GET",a,!0);c.setRequestHeader("Accept","application/json");c.setRequestHeader("Authorization","bearer "+b.token);var d=m();c.onreadystatechange=function(){4==c.readyState&&(200==c.status?(b.userInfo=JSON.parse(c.responseText),d.setSuccess(b.userInfo)): +d.setError())};c.send();return d.promise};b.isTokenExpired=function(a){if(!b.tokenParsed||!b.refreshToken&&"implicit"!=b.flow)throw"Not authenticated";if(null==b.timeSkew)return G("[KEYCLOAK] Unable to determine if token is expired as timeskew is not set"),!0;var c=b.tokenParsed.exp-Math.ceil((new Date).getTime()/1E3)+b.timeSkew;if(a){if(isNaN(a))throw"Invalid minValidity";c-=a}return 0>c};b.updateToken=function(a){var c=m();if(!b.refreshToken)return c.setError(),c.promise;a=a||5;var d=function(){var d= +!1;if(-1==a)d=!0,G("[KEYCLOAK] Refreshing token: forced refresh");else if(!b.tokenParsed||b.isTokenExpired(a))d=!0,G("[KEYCLOAK] Refreshing token: token expired");if(d){var d="grant_type\x3drefresh_token\x26refresh_token\x3d"+b.refreshToken,e=b.endpoints.token();z.push(c);if(1==z.length){var f=new XMLHttpRequest;f.open("POST",e,!0);f.setRequestHeader("Content-type","application/x-www-form-urlencoded");f.withCredentials=!0;var d=d+("\x26client_id\x3d"+encodeURIComponent(b.clientId)),h=(new Date).getTime(); +f.onreadystatechange=function(){if(4==f.readyState)if(200==f.status){G("[KEYCLOAK] Token refreshed");h=(h+(new Date).getTime())/2;var a=JSON.parse(f.responseText);g(a.access_token,a.refresh_token,a.id_token,h);b.onAuthRefreshSuccess&&b.onAuthRefreshSuccess();for(a=z.pop();null!=a;a=z.pop())a.setSuccess(!0)}else for(R("[KEYCLOAK] Failed to refresh token"),400==f.status&&b.clearToken(),b.onAuthRefreshError&&b.onAuthRefreshError(),a=z.pop();null!=a;a=z.pop())a.setError(!0)};f.send(d)}}else c.setSuccess(!1)}; +e.enable?F().then(function(){d()}).catch(function(){c.setError()}):d();return c.promise};b.clearToken=function(){b.token&&(g(null,null,null),b.onAuthLogout&&b.onAuthLogout(),b.loginRequired&&b.login())};var O=function(){function a(){for(var a=(new Date).getTime(),b=0;b<localStorage.length;b++){var c=localStorage.key(b);if(c&&0==c.indexOf("kc-callback-")){var d=localStorage.getItem(c);if(d)try{var e=JSON.parse(d).expires;(!e||e<a)&&localStorage.removeItem(c)}catch(S){localStorage.removeItem(c)}}}} +if(!(this instanceof O))return new O;localStorage.setItem("kc-test","test");localStorage.removeItem("kc-test");this.get=function(b){if(b){b="kc-callback-"+b;var c=localStorage.getItem(b);c&&(localStorage.removeItem(b),c=JSON.parse(c));a();return c}};this.add=function(b){a();var c="kc-callback-"+b.state;b.expires=(new Date).getTime()+36E5;localStorage.setItem(c,JSON.stringify(b))}},P=function(){if(!(this instanceof P))return new P;this.get=function(c){if(c){var d;a:{d="kc-callback-"+c+"\x3d";for(var e= +document.cookie.split(";"),f=0;f<e.length;f++){for(var g=e[f];" "==g.charAt(0);)g=g.substring(1);if(0==g.indexOf(d)){d=g.substring(d.length,g.length);break a}}d=""}b("kc-callback-"+c,"",a(-100));if(d)return JSON.parse(d)}};this.add=function(c){b("kc-callback-"+c.state,JSON.stringify(c),a(60))};this.removeItem=function(c){b(c,"",a(-100))};var a=function(a){var b=new Date;b.setTime(b.getTime()+6E4*a);return b},b=function(a,b,c){a=a+"\x3d"+b+"; expires\x3d"+c.toUTCString()+"; ";document.cookie=a}}}if("undefined"=== +typeof Promise)throw Error("Keycloak requires an environment that supports Promises. Make sure that you include the appropriate polyfill.");var u=!1;w.prototype=Object.create(Promise.prototype);w.prototype.constructor=w;w.prototype.success=function(l){E();var n=this.then(function(n){l(n)});return p(n)};w.prototype.error=function(l){E();var n=this.catch(function(n){l(n)});return p(n)};return l}); +//# sourceMappingURL=keycloak.min.js.map \ No newline at end of file diff --git a/client/loadkeycloak.js b/client/loadkeycloak.js new file mode 100644 index 0000000000000000000000000000000000000000..b743b5e25a356bc218f3c1ca572ae317b037f847 --- /dev/null +++ b/client/loadkeycloak.js @@ -0,0 +1,47 @@ +// This separate script is used to handle the login procedure before loading +// the application. As of 0.8.3 nomad-FAIR is using KeyCloak 7.0.0, which does +// not support the "silentCheckSsoRedirectUri" option. This option enables a +// silent login check that does not enforce reloads. In order to do such silent +// login, the Javascript adapter for KeyCloak 8.0.0 is used instead. This is +// against the best practice of directly downloading the Javascript adapter +// from the authentication server (window.nomadEnv.keycloakBase + +// "js/keycloak.min.js"), but is in this case acceptable as it result is a much +// smoother user experience. + +var keycloak = new Keycloak({ + url: window.nomadEnv.keycloakBase, + realm: window.nomadEnv.keycloakRealm, + clientId: window.nomadEnv.keycloakClientId +}); +keycloak.init({ + onLoad: "check-sso", + silentCheckSsoRedirectUri: "http://localhost:3000/gui/silent-check-sso.html", + promiseType: "native", +}).then((authenticated) => { + let loginButton = document.querySelector('#login-button'); + let logoutButton = document.querySelector('#logout-button'); + let userName = document.querySelector('#user-name'); + if (authenticated) { + keycloak.loadUserProfile() + .then(function(profile) { + userName.textContent = `${profile.firstName} ${profile.lastName}`; + }).catch(function() { + console.log('Failed to load user profile.'); + }); + loginButton.style.display = 'none'; + logoutButton.style.display = 'inline'; + } else { + loginButton.style.display = 'inline'; + logoutButton.style.display = 'none'; + userName.textContent = "Guest"; + } +}); +let loginButton = document.querySelector('#login-button'); +let logoutButton = document.querySelector('#logout-button'); +loginButton.onclick = () => { + keycloak.login({redirectUri: "http://" + window.location.host + "/gui/#/search"}) + .catch(() => {console.log("Authentication error.")}) +}; +logoutButton.onclick = () => { + keycloak.logout() +}; diff --git a/client/silent-check-sso.html b/client/silent-check-sso.html new file mode 100644 index 0000000000000000000000000000000000000000..9435ce65b4cd1d4b299e6bf9637123ccf196a1be --- /dev/null +++ b/client/silent-check-sso.html @@ -0,0 +1,8 @@ +<!doctype html> +<html> + <body> + <script> + parent.postMessage(location.href, location.origin) + </script> + </body> +</html> diff --git a/client/src/common/Router.js b/client/src/common/Router.js index e6b9b6cfd575d8ea0b6f1bca3d217fe66e838439..6f354c1dc4c4b154047752d0e05522042939297b 100644 --- a/client/src/common/Router.js +++ b/client/src/common/Router.js @@ -35,23 +35,27 @@ function add(route, func){ window.addEventListener("hashchange", route); -function route(){ - let hashPath= document.location.hash.substring(2); +function route() { + let hashPath = document.location.hash.substring(2); let command, param, subparam; - // remove the ending / + // Remove the ending / if (hashPath.lastIndexOf('/') === (hashPath.length-1)) hashPath = hashPath.substring(0,hashPath.length-1); - if (hashPath.indexOf('/') >0){ + // Remove state parameters from authentication + let stateIndex = hashPath.indexOf('&state'); + if (stateIndex != -1) { + hashPath = hashPath.substring(0, stateIndex); + } + + if (hashPath.indexOf('/') > 0){ let a= hashPath.split('/'); command= a[0]; param= a[1]; subparam= a[2]; } - else command= hashPath; - - //console.log("hashPath: " + hashPath+' command: '+command+' param: '+param+' subparam: '+subparam); + else command = hashPath; if (routes.has(command)) { routes.get(command)(param, subparam); diff --git a/client/src/common/util.js b/client/src/common/util.js index dce388ef0355492a7266da70066e1c8db148a53e..c6369cb832e42f807c6d9e4f22971a530f4dc4ed 100644 --- a/client/src/common/util.js +++ b/client/src/common/util.js @@ -66,8 +66,6 @@ let ELEMENTS = [ // API URL and user cookie domain configuration const API_BASE_URL = window.nomadEnv.host + window.nomadEnv.path; -document.querySelector('#guest-user a').href = API_BASE_URL+'saml/?sso2'; - // Mockup URLs //const FERMI_SURFACE_URL= HOST+'files/fermi/'+ //'fed3fa9fbc68aa6c5e51845396889666ca37bb2e626e1da53.x3d'; diff --git a/client/src/main.js b/client/src/main.js index 11b37f96d3e09b68620cc893bc6a482f478abb39..c6e5f05a9b7bd3760699751f70ecd8dc3c9f30b6 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -225,28 +225,25 @@ Router.route(); /********* User authentication ***********/ let userNameElement = document.querySelector('#user-name'); -let logoutButton = document.querySelector('#logout-button'); - -function setAppAuthenticated(data){ - if (data.status === 'Authenticated'){ - userNameElement.innerHTML = data.user.username; - document.querySelector('#guest-user').style.display = 'none'; - document.querySelector('#auth-user').style.display = 'inline'; - util.setAuthRequestHeader(data.user, data.token.data); - if (currentModule === materialModDOM) flaggingTab.style.visibility = 'visible'; - } -} +//function setAppAuthenticated(data){ + //if (data.status === 'Authenticated'){ + //userNameElement.innerHTML = data.user.username; + //document.querySelector('#guest-user').style.display = 'none'; + //document.querySelector('#auth-user').style.display = 'inline'; + //util.setAuthRequestHeader(data.user, data.token.data); + //if (currentModule === materialModDOM) flaggingTab.style.visibility = 'visible'; + //} +//} -function setAppLoggedOut(){ - userNameElement.innerHTML = ''; - document.querySelector('#guest-user').style.display = 'inline'; - document.querySelector('#auth-user').style.display = 'none'; - util.setAuthRequestHeader(); - if (flaggingTab.style.visibility !== 'hidden') - flaggingTab.style.visibility = 'hidden'; -} +//function logout(){ + //userNameElement.innerHTML = ''; + //util.setAuthRequestHeader(); + + //if (flaggingTab.style.visibility !== 'hidden') + //flaggingTab.style.visibility = 'hidden'; +//} function getCookie(name) { @@ -262,22 +259,9 @@ function parseCookie(userData) { let userInfoCookie = getCookie('user_info'); -//console.log('Cookies: ', document.cookie, userInfoCookie); if (userInfoCookie !== undefined){ let userInfoData = JSON.parse(parseCookie(userInfoCookie)); //console.log('userInfoData: ', userInfoData); setAppAuthenticated(userInfoData); } - - -// Logout -logoutButton.addEventListener( "click", e => { - - document.cookie='user_info=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain='+ - window.nomadEnv.userCookieDomain+'; path=/'; - //console.log('Logging out document.cookie ',document.cookie); - - setAppLoggedOut(); - //console.log('Logging out ',userNameElement.innerHTML); -}); diff --git a/client/src/material-mod/StructureDetails.view.js b/client/src/material-mod/StructureDetails.view.js index 47df93befc218539267efbc452d5d87f2bb7f1d3..ac60fe6519f64bec41bd953a8b8a3986a03143cf 100644 --- a/client/src/material-mod/StructureDetails.view.js +++ b/client/src/material-mod/StructureDetails.view.js @@ -864,7 +864,7 @@ class SummaryByFunctionalsComponent { // Create tabs for each functional this.functionalTabs.innerHTML = ""; - this.calcMapByFunctional.forEach( (calcs, functionalName) =>{ + this.calcMapByFunctional.forEach( (calcs, functionalName) => { this.functionalTabs.innerHTML += '<span class="tab" data-tab="'+functionalName+'">'+functionalName+'</span>'; }); diff --git a/client/src/search-mod/PropertiesBox.view.js b/client/src/search-mod/PropertiesBox.view.js index 0a50f59958719e8118b52e8773085c89cb819b52..d990079e24c40cf30feb51b17ed7112795b4873e 100644 --- a/client/src/search-mod/PropertiesBox.view.js +++ b/client/src/search-mod/PropertiesBox.view.js @@ -229,7 +229,7 @@ class PropertiesBox { </div> <div style="clear: both;"></div> - ` + `; this.tabsElement = this.element.querySelector('.properties-box-tabs'); this.tabSelected = this.element.querySelector('[data-tab="structure"]'); this.tabPanelSelected = this.element.querySelector('.structure-panel');