Commit 47094dd7 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Fixed issue with loading spinner, removed autocomplete feature for material...

Fixed issue with loading spinner, removed autocomplete feature for material names, added support for using enter to add formula/material name to query.
parent cff6a589
Pipeline #97759 skipped with stage
...@@ -49,7 +49,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac ...@@ -49,7 +49,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component is the loading popup window \n */\n\n\n\n\nlet util = __webpack_require__(/*! ./util.js */ \"./src/common/util.js\");\n\nlet loadingPopup = document.querySelector('#loading-popup');\nlet loadSet = new Set();\n\nfunction show(id) {\n if (!window.allowNewLoadPopup) {\n return;\n }\n loadSet.add(id, true);\n let ttRect = loadingPopup.getBoundingClientRect();\n let leftPos = (window.innerWidth - ttRect.width)/2;\n let topPos = (window.innerHeight -ttRect.height)/2;\n loadingPopup.style.left = leftPos+'px';\n loadingPopup.style.top = (topPos-100)+'px';\n loadingPopup.style.visibility = 'visible';\n}\n\nfunction hide(id) {\n loadSet.delete(id);\n if (loadSet.size === 0) {\n loadingPopup.style.visibility = 'hidden';\n }\n}\n\nfunction reset() {\n loadSet = new Set();\n loadingPopup.style.visibility = 'hidden';\n}\n\n// EXPORTS\nmodule.exports = {show, hide, reset};\n\n\n//# sourceURL=webpack:///./src/common/LoadingPopup.js?"); 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\nfunction reset() {\n loadSet = new Set();\n loadingPopup.style.visibility = 'hidden';\n}\n\n// EXPORTS\nmodule.exports = {show, hide, reset};\n\n\n//# sourceURL=webpack:///./src/common/LoadingPopup.js?");
/***/ }), /***/ }),
...@@ -82,7 +82,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac ...@@ -82,7 +82,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the app Routing system: the feature that allows\n the page navigation in a single-page environment.\n */\n\n\n\n\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet routes = new Map();\n\n\nfunction add(route, func){\n routes.set(route, func);\n}\n\n\nwindow.addEventListener(\"hashchange\", route);\n\nvar hashHistory = [window.location.hash];\nvar historyLength = window.history.length;\nfunction route() {\n\n var hash = window.location.hash, length = window.history.length;\n if (hashHistory.length && historyLength == length) {\n if (hashHistory[hashHistory.length - 2] == hash) {\n hashHistory = hashHistory.slice(0, -1);\n LoadingPopup.reset();\n window.allowNewLoadPopup = false;\n } else {\n window.allowNewLoadPopup = true;\n hashHistory.push(hash);\n }\n } else {\n window.allowNewLoadPopup = true;\n hashHistory.push(hash);\n historyLength = length;\n }\n\n let hashPath = document.location.hash.substring(2);\n let command, param, subparam;\n\n // Remove the ending /\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n\n // Remove state parameters from authentication\n let stateIndex = hashPath.indexOf('&state');\n if (stateIndex != -1) {\n hashPath = hashPath.substring(0, stateIndex);\n }\n\n if (hashPath.indexOf('/') > 0){\n let a= hashPath.split('/');\n command= a[0];\n param= a[1];\n subparam= a[2];\n }\n else command = hashPath;\n\n if (routes.has(command)) {\n routes.get(command)(param, subparam);\n }\n};\n\n\nfunction print(){\n console.log('Router data: ');\n routes.forEach(function(func, url) {\n console.log(url + ': ' + func);\n });\n}\n\n\n// EXPORTS\nmodule.exports = { add: add, route: route };\n\n\n//# sourceURL=webpack:///./src/common/Router.js?"); eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the app Routing system: the feature that allows\n the page navigation in a single-page environment.\n */\n\n\n\n\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet routes = new Map();\n\n\nfunction add(route, func){\n routes.set(route, func);\n}\n\n\nwindow.addEventListener(\"hashchange\", route);\n\nvar hashHistory = [window.location.hash];\nvar historyLength = window.history.length;\nfunction route() {\n\n var hash = window.location.hash, length = window.history.length;\n if (hashHistory.length && historyLength == length) {\n if (hashHistory[hashHistory.length - 2] == hash) {\n hashHistory = hashHistory.slice(0, -1);\n } else {\n hashHistory.push(hash);\n }\n } else {\n hashHistory.push(hash);\n historyLength = length;\n }\n\n let hashPath = document.location.hash.substring(2);\n let command, param, subparam;\n\n // Remove the ending /\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n\n // Remove state parameters from authentication\n let stateIndex = hashPath.indexOf('&state');\n if (stateIndex != -1) {\n hashPath = hashPath.substring(0, stateIndex);\n }\n\n if (hashPath.indexOf('/') > 0){\n let a= hashPath.split('/');\n command= a[0];\n param= a[1];\n subparam= a[2];\n }\n else command = hashPath;\n\n if (routes.has(command)) {\n routes.get(command)(param, subparam);\n }\n};\n\n\nfunction print(){\n console.log('Router data: ');\n routes.forEach(function(func, url) {\n console.log(url + ': ' + func);\n });\n}\n\n\n// EXPORTS\nmodule.exports = { add: add, route: route };\n\n\n//# sourceURL=webpack:///./src/common/Router.js?");
/***/ }), /***/ }),
...@@ -136,7 +136,7 @@ eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache ...@@ -136,7 +136,7 @@ eval("/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache
/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "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/NewSearchMod.js */ \"./src/search-mod/NewSearchMod.js\");//require('./search-mod/SearchMod.js');\nlet UserGuidance = __webpack_require__(/*! ./common/UserGuidance.js */ \"./src/common/UserGuidance.js\");\nlet DataStore = __webpack_require__(/*! ./material-mod/DataStore.js */ \"./src/material-mod/DataStore.js\");\n\n\n// main DOM elements\nlet contentElement = document.getElementById('content');\nlet titleElement = document.querySelector('title');\nwindow.allowNewLoadPopup = true;\n\nvar getUrl = window.location;\nvar guiRoot = getUrl.protocol + \"//\" + getUrl.host + \"/\" + getUrl.pathname;\n\n// As of 0.8.3 nomad-FAIR is using KeyCloak 7.0.0, which does\n// not support the \"silentCheckSsoRedirectUri\" option. This option enables a\n// silent login check that does not enforce reloads. In order to do such silent\n// login, the Javascript adapter for KeyCloak 8.0.0 is used instead. This is\n// against the best practice of directly downloading the Javascript adapter\n// from the authentication server (window.nomadEnv.keycloakBase +\n// \"js/keycloak.min.js\"), but is in this case acceptable as it result is a much\n// smoother user experience.\nPubSub.subscribe('authenticated', data => {\n let hashPath = document.location.hash.substring(2);\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n if (hashPath.indexOf('/') > 0){\n let command = hashPath.split('/')[0];\n if (command === \"material\") {\n flaggingTab.style.visibility = 'visible';\n }\n }\n});\nvar keycloak = new Keycloak({\n url: window.nomadEnv.keycloakBase,\n realm: window.nomadEnv.keycloakRealm,\n clientId: window.nomadEnv.keycloakClientId\n});\nwindow.keycloak = keycloak;\nlet loginButton = document.querySelector('#login-button');\nlet logoutButton = document.querySelector('#logout-button');\nlet userName = document.querySelector('#user-name');\nkeycloak.init({\n onLoad: \"check-sso\",\n silentCheckSsoRedirectUri: `${guiRoot}silent-check-sso.html`,\n promiseType: \"native\",\n}).then((authenticated) => {\n if (authenticated) {\n keycloak.loadUserProfile()\n .then(function(profile) {\n userName.textContent = `${profile.firstName} ${profile.lastName}`;\n loginButton.style.display = 'none';\n logoutButton.style.display = 'inline';\n PubSub.publish('authenticated');\n }).catch(function() {\n console.log('Failed to load user profile.');\n });\n //util.setAuthRequestHeader(data.user, data.token.data);\n } else {\n loginButton.style.display = 'inline';\n logoutButton.style.display = 'none';\n userName.textContent = \"Guest\";\n }\n});\nloginButton.onclick = () => {\n keycloak.login({redirectUri: `${guiRoot}#/search`})\n .catch(() => {console.log(\"Authentication error.\");});\n};\nlogoutButton.onclick = () => {\n keycloak.logout();\n};\n\n\n/********* User flagging side tab ****************/\n\n/* This side vertical tab is hidden initially\n but it has to be set up when the app starts */\nlet flaggingTab = document.getElementById('calc-flagging-tab');\nflaggingTab.style.top = (window.innerHeight/2)+'px';\nflaggingTab.addEventListener('click', e => {\n FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus());\n});\n\n\n/*********** App Breadcrumb component definition ***************/\n\nclass Breadcrumb {\n\n constructor() {\n\n this.element = document.querySelector('#breadcrumb-placeholder');\n this.element.innerHTML = `\n <span class=\"goto-page Search\">Search</span>\n <span class=\"goto-page Results\">&nbsp; > &nbsp; <span>Results</span></span>\n <span class=\"goto-page Overview\">&nbsp; > &nbsp; <span>Overview</span></span>\n <span class=\"Details\">\n &nbsp; > &nbsp;\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\nlet initialized = false; // Has the search been initialized\n\n\nfunction showModuleDOM(module){\n if (currentModule) contentElement.removeChild(currentModule);\n currentModule = module;\n contentElement.appendChild(currentModule);\n}\n\n\n/****** App level events setup ********/\n\nPubSub.subscribe('show-material', data => {\n console.log('Handling event show-material: ' + data.material_id + ' view: '+data.view);\n\n //titleElement.innerHTML = 'NOMAD Encyclopedia - Material '+data.id;\n breadcrumb.setState('material', data.view);\n\n if (typeof materialModDOM === 'undefined'){\n MaterialModule = new MaterialMod();\n materialModDOM = MaterialModule.element;\n }\n showModuleDOM(materialModDOM);\n MaterialModule.setMaterialView(data);\n\n // In case the app comes from the search module through the url (back button)\n UserGuidance.show(false);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n if (keycloak.authenticated) {\n flaggingTab.style.visibility = 'visible';\n }\n});\n\nPubSub.subscribe('show-search', search => {\n console.log('Handling event show-search: '+search);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n flaggingTab.style.visibility = 'hidden';\n\n titleElement.innerHTML = 'NOMAD Encyclopedia - Search';\n breadcrumb.setState('search', search);\n showModuleDOM(searchMod.element);\n if (!initialized) {\n searchMod.sendQuery();\n }\n initialized = true\n});\n\n\n/****** App routing config ******/\nRouter.add('search', search => PubSub.publish('show-search', search));\nRouter.add('material', (matId, view) => PubSub.publish('show-material', {'material_id': matId, 'view': view}));\n\n/****** init ******/\nsearchMod = new SearchModule();\nif (document.location.hash === '') document.location += \"#/search\";\nRouter.route();\n\n/********* User authentication ***********/\n\nlet userNameElement = document.querySelector('#user-name');\n\nfunction getCookie(name) {\n let value = \"; \" + document.cookie;\n let parts = value.split(\"; \" + name + \"=\");\n if (parts.length === 2) return parts.pop().split(\";\").shift();\n}\n\nfunction parseCookie(userData) {\n return userData.substring(1, userData.length-1).replace(/\\\\054/g,',').replace(/\\\\/g,'');\n}\n\nlet userInfoCookie = getCookie('user_info');\n\nif (userInfoCookie !== undefined) {\n let userInfoData = JSON.parse(parseCookie(userInfoCookie));\n //console.log('userInfoData: ', userInfoData);\n setAppAuthenticated(userInfoData);\n}\n\n\n//# sourceURL=webpack:///./src/main.js?"); 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/NewSearchMod.js */ \"./src/search-mod/NewSearchMod.js\");//require('./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\nvar getUrl = window.location;\nvar guiRoot = getUrl.protocol + \"//\" + getUrl.host + \"/\" + getUrl.pathname;\n\n// As of 0.8.3 nomad-FAIR is using KeyCloak 7.0.0, which does\n// not support the \"silentCheckSsoRedirectUri\" option. This option enables a\n// silent login check that does not enforce reloads. In order to do such silent\n// login, the Javascript adapter for KeyCloak 8.0.0 is used instead. This is\n// against the best practice of directly downloading the Javascript adapter\n// from the authentication server (window.nomadEnv.keycloakBase +\n// \"js/keycloak.min.js\"), but is in this case acceptable as it result is a much\n// smoother user experience.\nPubSub.subscribe('authenticated', data => {\n let hashPath = document.location.hash.substring(2);\n if (hashPath.lastIndexOf('/') === (hashPath.length-1))\n hashPath = hashPath.substring(0,hashPath.length-1);\n if (hashPath.indexOf('/') > 0){\n let command = hashPath.split('/')[0];\n if (command === \"material\") {\n flaggingTab.style.visibility = 'visible';\n }\n }\n});\nvar keycloak = new Keycloak({\n url: window.nomadEnv.keycloakBase,\n realm: window.nomadEnv.keycloakRealm,\n clientId: window.nomadEnv.keycloakClientId\n});\nwindow.keycloak = keycloak;\nlet loginButton = document.querySelector('#login-button');\nlet logoutButton = document.querySelector('#logout-button');\nlet userName = document.querySelector('#user-name');\nkeycloak.init({\n onLoad: \"check-sso\",\n silentCheckSsoRedirectUri: `${guiRoot}silent-check-sso.html`,\n promiseType: \"native\",\n}).then((authenticated) => {\n if (authenticated) {\n keycloak.loadUserProfile()\n .then(function(profile) {\n userName.textContent = `${profile.firstName} ${profile.lastName}`;\n loginButton.style.display = 'none';\n logoutButton.style.display = 'inline';\n PubSub.publish('authenticated');\n }).catch(function() {\n console.log('Failed to load user profile.');\n });\n //util.setAuthRequestHeader(data.user, data.token.data);\n } else {\n loginButton.style.display = 'inline';\n logoutButton.style.display = 'none';\n userName.textContent = \"Guest\";\n }\n});\nloginButton.onclick = () => {\n keycloak.login({redirectUri: `${guiRoot}#/search`})\n .catch(() => {console.log(\"Authentication error.\");});\n};\nlogoutButton.onclick = () => {\n keycloak.logout();\n};\n\n\n/********* User flagging side tab ****************/\n\n/* This side vertical tab is hidden initially\n but it has to be set up when the app starts */\nlet flaggingTab = document.getElementById('calc-flagging-tab');\nflaggingTab.style.top = (window.innerHeight/2)+'px';\nflaggingTab.addEventListener('click', e => {\n FlaggingFormPopup.show(MaterialModule.getCurrentPageStatus());\n});\n\n\n/*********** App Breadcrumb component definition ***************/\n\nclass Breadcrumb {\n\n constructor() {\n\n this.element = document.querySelector('#breadcrumb-placeholder');\n this.element.innerHTML = `\n <span class=\"goto-page Search\">Search</span>\n <span class=\"goto-page Results\">&nbsp; > &nbsp; <span>Results</span></span>\n <span class=\"goto-page Overview\">&nbsp; > &nbsp; <span>Overview</span></span>\n <span class=\"Details\">\n &nbsp; > &nbsp;\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\nlet initialized = false; // Has the search been initialized\n\n\nfunction showModuleDOM(module){\n if (currentModule) contentElement.removeChild(currentModule);\n currentModule = module;\n contentElement.appendChild(currentModule);\n}\n\n\n/****** App level events setup ********/\n\nPubSub.subscribe('show-material', data => {\n console.log('Handling event show-material: ' + data.material_id + ' view: '+data.view);\n\n //titleElement.innerHTML = 'NOMAD Encyclopedia - Material '+data.id;\n breadcrumb.setState('material', data.view);\n\n if (typeof materialModDOM === 'undefined'){\n MaterialModule = new MaterialMod();\n materialModDOM = MaterialModule.element;\n }\n showModuleDOM(materialModDOM);\n MaterialModule.setMaterialView(data);\n\n // In case the app comes from the search module through the url (back button)\n UserGuidance.show(false);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n if (keycloak.authenticated) {\n flaggingTab.style.visibility = 'visible';\n }\n});\n\nPubSub.subscribe('show-search', search => {\n console.log('Handling event show-search: '+search);\n\n // When a logged user comes to a material page, the error reporting tab is shown.\n flaggingTab.style.visibility = 'hidden';\n\n titleElement.innerHTML = 'NOMAD Encyclopedia - Search';\n breadcrumb.setState('search', search);\n showModuleDOM(searchMod.element);\n if (!initialized) {\n searchMod.sendQuery();\n }\n initialized = true\n});\n\n\n/****** App routing config ******/\nRouter.add('search', search => PubSub.publish('show-search', search));\nRouter.add('material', (matId, view) => PubSub.publish('show-material', {'material_id': matId, 'view': view}));\n\n/****** init ******/\nsearchMod = new SearchModule();\nif (document.location.hash === '') document.location += \"#/search\";\nRouter.route();\n\n/********* User authentication ***********/\n\nlet userNameElement = document.querySelector('#user-name');\n\nfunction getCookie(name) {\n let value = \"; \" + document.cookie;\n let parts = value.split(\"; \" + name + \"=\");\n if (parts.length === 2) return parts.pop().split(\";\").shift();\n}\n\nfunction parseCookie(userData) {\n return userData.substring(1, userData.length-1).replace(/\\\\054/g,',').replace(/\\\\/g,'');\n}\n\nlet userInfoCookie = getCookie('user_info');\n\nif (userInfoCookie !== undefined) {\n let userInfoData = JSON.parse(parseCookie(userInfoCookie));\n //console.log('userInfoData: ', userInfoData);\n setAppAuthenticated(userInfoData);\n}\n\n\n//# sourceURL=webpack:///./src/main.js?");
/***/ }), /***/ }),
...@@ -297,7 +297,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac ...@@ -297,7 +297,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the Overview view component in the Material Module.\n */\n\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet BSPlotter = __webpack_require__(/*! ./BSPlotter.js */ \"./src/material-mod/BSPlotter.js\");\nlet DOSPlotter = __webpack_require__(/*! ./DOSPlotter.js */ \"./src/material-mod/DOSPlotter.js\");\nlet HeatCapPlotter = __webpack_require__(/*! ./HeatCapPlotter.js */ \"./src/material-mod/HeatCapPlotter.js\");\nlet MaterialMod = __webpack_require__(/*! ./MaterialMod.js */ \"./src/material-mod/MaterialMod.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet SimilarityFinder = __webpack_require__(/*! ./Similarity.js */ \"./src/material-mod/Similarity.js\").SimilarityFinder;\n\nconst ELEMENT_INCLUDED_MSG = 'ELEMENT ALREADY INCLUDED';\n\n\nclass Overview {\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','overview');\n this.materialId;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n this.calcMaterialId = null;\n this.element.innerHTML=`\n\n <div class=\"material-title\">\n </div>\n\n <div style=\"float: left; width: 40%;\">\n\n <div id=\"structure-ov\" class=\"view-box\">\n <div class=\"title\">Structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"viz-box-container\" style=\"height: 260px; position: relative\">\n <div class=\"viz-box\" style=\"height: 90%\"></div>\n </div>\n\n <div class=\"footer\">\n <div ><b><span>Material type</span></b>:\n <span class=\"material-type-field\" ></span>\n </div>\n <div class=\"space-group-field\" style=\"display: none\">\n <b><span info-sys-data=\"space-group\">Space group</span></b>:\n <span class=\"space-group-value\" ></span>\n </div>\n <div class=\"structure-type-field\" style=\"display: none\">\n <b><span info-sys-data=\"structure-type\">Structure type</span></b>:\n <span class=\"structure-type-value\" ></span>\n </div>\n </div>\n </div>\n\n\n<!-- ***** Elastic Constants Box\n\n <div id=\"elastic-ov\" class=\"view-box\">\n <div class=\"title\">Elastic constants\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n Not analyzed yet\n </div>\n\n </div>\n-->\n\n\n <div id=\"methodology-ov\" class=\"view-box\">\n <div class=\"title\">Methodology\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n\n <div class=\"info-fields-label\" > Available calculations </div>\n\n <div style=\"float: left; width: 45%\" >\n <b><span info-sys-data=\"functional-type\">Functional</span></b>\n <div class=\"functional-field\" > </div>\n </div>\n <div style=\"float: right; width: 45%\" >\n <b><span info-sys-data=\"code-name\">Code</span></b>\n <div class=\"code-field\"> </div>\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"float: right; width: 60%;\">\n\n <div id=\"e-structure-ov\" class=\"view-box\" style=\"display: block\">\n <div class=\"title\">Electronic structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div > <!-- style=\"margin: 12% 0; \" -->\n\n <div style=\"float: left; width: 60%; \">\n <div style=\"padding: 20px 0 20px 30px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"band-structure\">Band structure</span>\n </div>\n <div>\n <div id=\"band-plotter\" > </div>\n </div>\n\n <div class=\"footer-bs-calc\"></div>\n </div>\n </div>\n\n <div style=\"float: left; width: 40%; \">\n <div style=\"padding: 20px 30px 20px 40px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"DOS\">DOS</span>\n </div>\n\n <div>\n <div id=\"dos-plotter\" > </div>\n </div>\n <div class=\"footer-dos-calc\"></div>\n </div>\n </div>\n\n\n\n <div style=\"clear: both;\"></div>\n <table style=\"width: 100%\">\n <tr>\n <td class=\"spin-legend\" style=\"font-size: 0.9em; padding: 6px 30px 10px; display: none;\">\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin1\"/></svg>\n Spin <span style='font-size: 1.1em'>⇧</span> &nbsp;&nbsp;&nbsp;\n\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin2\"/></svg>\n Spin <span style='font-size: 1.1em'>⇩</span>\n </td>\n <td class = \"similarity-data-field\">\n <div style=\"float: right; padding: 0px 50px 10px 0px; margin-top: 1px;\" class=\"similarity-finder\">\n </div>\n </td>\n </tr>\n </table>\n </div>\n <div style=\"clear: both;\"></div>\n <!--\n <div class=\"footer\">\n\n <b>Band gap</b>: <span class=\"e-struct-field\" ></span>\n </div>\n -->\n </div>\n\n <div id=\"thermal-props-ov\" class=\"view-box\" style=\"visibility: hidden\">\n <div class=\"title\">Vibrational and thermal properties\n <img style=\"float: right\" class=\"to-detail thermal-props\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div style=\"padding: 36px; \">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"heat-capacity-cv\">Specific heat</span>\n </div>\n\n\n <div>\n <div id=\"heat-plotter\" > </div>\n </div>\n <div class=\"footer-heat-calc\" style=\"text-align: center\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"clear: both;\"></div>\n `;\n\n this.materialTitle= this.element.getElementsByClassName('material-title')[0];\n\n this.systemType= this.element.querySelector('.material-type-field');\n this.spaceGroupField = this.element.querySelector('.space-group-field');\n this.spaceGroupValue = this.element.querySelector('.space-group-value');\n this.structTypeField= this.element.querySelector('.structure-type-field');\n this.structTypeValue= this.element.querySelector('.structure-type-value');\n //this.band_gap = this.element.getElementsByClassName('e-struct-field')[0];\n //fields= this.element.getElementsByClassName('method-field');\n this.functional= this.element.querySelector('.functional-field');//fields[0];\n this.code= this.element.querySelector('.code-field');//fields[1];\n\n let fields= this.element.getElementsByClassName('to-detail');\n this.structureDetailBtn= fields[0];\n this.electronicStructDetailBtn= fields[2];\n this.methodologyDetailBtn= fields[1];\n this.thermalDetailBtn= fields[3];\n/*\n this.elasticDetailBtn= fields[1];\n this.methodologyDetailBtn= fields[2];\n this.electronicStructDetailBtn= fields[3];\n this.thermalDetailBtn= fields[4];\n */\n\n this.vizBox = this.element.getElementsByClassName('viz-box')[0];\n this.bandPlotter= null;\n this.bsCalcIdBox = this.element.getElementsByClassName('footer-bs-calc')[0];\n this.dosPlotter= null;\n this.dosCalcIdBox = this.element.getElementsByClassName('footer-dos-calc')[0];\n this.heatPlotter= null;\n this.heatCalcIdBox = this.element.querySelector('.footer-heat-calc');\n\n this.spinLegend = this.element.querySelector('.spin-legend');\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n\n // Store the state of the calcs chosen on the Elec. Structure box\n this.eStructCalcs = { bs: null, dos: null};\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._events();\n }\n\n\n _events() {\n\n this.structureDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.structure);\n });\n\n this.electronicStructDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.electronicstruct);\n });\n\n this.methodologyDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.methodology);\n });\n\n this.thermalDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.thermalprops);\n });\n\n/*\n this.elasticDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.elasticconst);\n });\n*/\n\n\n //******* Optimize, genralize:\n //this.element.querySelectorAll('.to-detail+.'+detailsId).addEventListener( \"click\", (e) => {\n // util.setBrowserHashPath('material', this.materialId+'/'+detailsId);\n }\n\n\n getEStructChosenCalcs(){\n return this.eStructCalcs;\n }\n\n\n setDetailViewsListener(listener){\n this.detailViewsListener= listener;\n }\n\n setVisible() {\n this.element.style.display = 'block';\n }\n\n\n setMaterialData() {\n\n let data = DataStore.getMaterialData();\n this.materialTitle.innerHTML= util.getMaterialTitle(data);\n this.materialId = data.material_id;\n\n let isBulk = (data.material_type === 'bulk');\n this.systemType.textContent= data.material_type;\n this.structTypeField.style.display =\n (isBulk && data.structure_type !== null ? 'block' : 'none');\n this.spaceGroupField.style.display = (isBulk ? 'block' : 'none');\n\n if (isBulk){\n this.structTypeValue.textContent= data.structure_type;\n this.spaceGroupValue.textContent = data.space_group_number+\n ' ('+data.space_group_international_short_symbol+')';\n InfoSys.addElementToInfoSystem(this.spaceGroupValue,\n 'space-group.value:'+data.space_group_number);\n }\n\n if (this.similarityFinder) {\n this.similarityFinder.element.remove();\n }\n\n if (data.similarity) {\n this.similarityFinder = new SimilarityFinder({left: 40, right: 16, top: 0, bottom: 30});\n\n const similarityFinderContainer = this.element.querySelector('.similarity-finder');\n this.similarityFinder.setSimilarityData(data.similarity);\n similarityFinderContainer.appendChild(this.similarityFinder.element);\n this.similartyDataField = this.element.querySelector('.similarity-data-field');\n InfoSys.addToInfoSystem(this.similartyDataField);\n }\n }\n\n isLoaded(hasBs, hasDOS, hasPhonon) {\n let materialData = DataStore.getMaterialData();\n let material_id = materialData.material_id;\n if (this.calcMaterialId === material_id) {\n if (hasBs && !this.bsLoaded) {\n return false;\n }\n if (hasDOS && !this.dosLoaded) {\n return false;\n }\n if (hasPhonon && !this.phononLoaded) {\n return false;\n }\n return true;\n }\n return false;\n }\n\n clearCalcsData() {\n this.materialId = null;\n this.calcMaterialId = null;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n }\n\n setCalcsData(markedTreeLeafs) {\n let matData = DataStore.getMaterialData();\n let calcs = DataStore.getCalculations();\n let functionalMap = new Map();\n let codeMap = new Map();\n\n // Get the representative calculations from the material data returned\n let calcWithBS = DataStore.getCalc(DataStore.getRepresentatives().electronic_band_structure);\n let calcWithDOS = DataStore.getCalc(DataStore.getRepresentatives().electronic_dos);\n let calcWithHeat = DataStore.getCalc(DataStore.getRepresentatives().thermodynamical_properties);\n\n if (this.isLoaded(calcWithBS !== undefined, calcWithDOS !== undefined, calcWithHeat !== undefined)) {\n return;\n }\n this.calcMaterialId = matData.material_id;\n this.bsLoaded = false;\n this.dosLoaded = false;\n this.phononLoaded = false;\n\n // Gather how many calculations there are for each functional and code\n for (let i = 0; i < calcs.length; i++) {\n if (functionalMap.has(calcs[i].functional_type)) {\n let num = functionalMap.get(calcs[i].functional_type);\n functionalMap.set(calcs[i].functional_type, ++num);\n } else {\n functionalMap.set(calcs[i].functional_type, 1);\n }\n\n let codeNameTrimed= calcs[i].code_name.trim();\n if (codeMap.has(codeNameTrimed)) {\n let num= codeMap.get(codeNameTrimed);\n codeMap.set(codeNameTrimed, ++num);\n } else {\n codeMap.set(codeNameTrimed, 1);\n }\n }\n\n if (calcWithBS !== undefined) this.eStructCalcs.bs = calcWithBS.calc_id;\n if (calcWithDOS !== undefined) this.eStructCalcs.dos = calcWithDOS.calc_id;\n\n let tempCalcId = null;\n if (calcWithBS !== undefined) tempCalcId = calcWithBS.calc_id;\n else if (calcWithDOS !== undefined) tempCalcId = calcWithDOS.calc_id;\n\n if (tempCalcId === null) {\n markedTreeLeafs.eStruct = null; // no graph data\n } else\n markedTreeLeafs.eStruct = +tempCalcId;\n\n if (calcWithHeat === undefined) {\n markedTreeLeafs.thermalProps = null;\n } else {\n markedTreeLeafs.thermalProps = +calcWithHeat.calc_id;\n }\n\n // Add list of functionals\n this.functional.textContent = \"\";\n let container = document.createElement(\"div\");\n functionalMap.forEach((number, functional) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"functional-type.value:\" + util.getDefault(functional));\n span.textContent = number + ' ' + util.getDefault(functional);\n container.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container.appendChild(linebreak);\n });\n this.functional.append(container);\n InfoSys.addToInfoSystem(this.functional);\n\n // Add list of codes\n this.code.textContent = \"\";\n let container2 = document.createElement(\"div\");\n codeMap.forEach((number, codeName) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"code-name.value:\" + codeName);\n span.textContent = number + ' ' + codeName;\n container2.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container2.appendChild(linebreak);\n });\n this.code.append(container2);\n InfoSys.addToInfoSystem(this.code);\n\n if (DataStore.hasElecStructureData()) {\n document.getElementById('e-structure-ov').style.display = 'block';\n let isReady = () => {\n if (this.dosLoaded && this.bsLoaded) {\n document.getElementById('e-structure-ov').style.visibility = 'visible';\n }\n };\n\n if (this.bandPlotter === null){\n this.bandPlotter= new BSPlotter();\n this.bandPlotter.attach(document.getElementById('band-plotter'),undefined,316);\n }\n if (this.dosPlotter === null){\n this.dosPlotter= new DOSPlotter({left: 40, right: 20, top: 0, bottom: 30});\n this.dosPlotter.attach(document.getElementById('dos-plotter'),undefined,317);\n }\n\n if (calcWithBS === undefined){\n this.bandPlotter.setNoData();\n this.bsCalcIdBox.innerHTML = '';\n this.bsLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithBS.calc_id);\n LoadingPopup.show(\"overview_electronic_band_structure\");\n let query = JSON.stringify({properties: [\"electronic_band_structure\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let bandStructData = JSON.parse(e.target.response).electronic_band_structure;\n this.bandPlotter.setBandStructureData(bandStructData);\n this.bsCalcIdBox.innerHTML = 'From calculation <b>' + util.getShortCode(calcWithBS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithBS.functional_type+' - '+calcWithBS.code_name+')</span>';\n if (bandStructData.section_k_band_segment[0].band_energies.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.bandPlotter.setNoData();\n }\n this.bsLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_band_structure\");\n });\n }\n\n if (calcWithDOS === undefined){\n this.dosPlotter.setNoData();\n this.dosCalcIdBox.innerHTML = '';\n this.dosLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithDOS.calc_id);\n LoadingPopup.show(\"overview_electronic_dos\");\n let query = JSON.stringify({properties: [\"electronic_dos\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let dosData= JSON.parse(e.target.response).electronic_dos;\n this.dosPlotter.setPoints(dosData, calcWithDOS);\n this.dosCalcIdBox.innerHTML = 'From calculation <b>'+util.getShortCode(calcWithDOS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithDOS.functional_type+' - '+calcWithDOS.code_name+')</span>';\n if (dosData.dos_values.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.dosPlotter.setNoData();\n }\n this.dosLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_dos\");\n });\n }\n }\n\n if (!DataStore.hasThermalData()) {\n document.getElementById('thermal-props-ov').style.display = 'none';\n } else {\n document.getElementById('thermal-props-ov').style.display = 'block';\n\n if (this.heatPlotter === null){\n this.heatPlotter= new HeatCapPlotter();\n this.heatPlotter.attach(document.getElementById('heat-plotter'),undefined,317);\n }\n\n if (calcWithHeat === undefined){\n this.heatPlotter.setNoData();\n this.heatCalcIdBox.innerHTML = '';\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithHeat.calc_id);\n LoadingPopup.show();\n let query = JSON.stringify({properties: [\"thermodynamical_properties\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200) {\n let response = JSON.parse(e.target.response);\n let thermoProp = response.thermodynamical_properties;\n let t = thermoProp.thermodynamical_property_temperature;\n this.heatPlotter.setData(t, thermoProp.specific_heat_capacity);\n this.heatCalcIdBox.innerHTML = 'From calculation <b>'+util.getShortCode(calcWithHeat.calc_id)+'</b>'+\n '</b> <span style=\"font-size: 0.8em\">('+calcWithHeat.functional_type+' - '+calcWithHeat.code_name+')</span>';\n this.phononLoaded = true;\n document.getElementById('thermal-props-ov').style.visibility = 'visible';\n }\n LoadingPopup.hide();\n });\n }\n }\n }\n}\n\n// EXPORTS\nmodule.exports = Overview;\n\n\n//# sourceURL=webpack:///./src/material-mod/Overview.view.js?"); eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This file implements the Overview view component in the Material Module.\n */\n\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\nlet BSPlotter = __webpack_require__(/*! ./BSPlotter.js */ \"./src/material-mod/BSPlotter.js\");\nlet DOSPlotter = __webpack_require__(/*! ./DOSPlotter.js */ \"./src/material-mod/DOSPlotter.js\");\nlet HeatCapPlotter = __webpack_require__(/*! ./HeatCapPlotter.js */ \"./src/material-mod/HeatCapPlotter.js\");\nlet MaterialMod = __webpack_require__(/*! ./MaterialMod.js */ \"./src/material-mod/MaterialMod.js\");\nlet DataStore = __webpack_require__(/*! ./DataStore.js */ \"./src/material-mod/DataStore.js\");\nlet SimilarityFinder = __webpack_require__(/*! ./Similarity.js */ \"./src/material-mod/Similarity.js\").SimilarityFinder;\n\nconst ELEMENT_INCLUDED_MSG = 'ELEMENT ALREADY INCLUDED';\n\n\nclass Overview {\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','overview');\n this.materialId;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n this.calcMaterialId = null;\n this.element.innerHTML=`\n\n <div class=\"material-title\">\n </div>\n\n <div style=\"float: left; width: 40%;\">\n\n <div id=\"structure-ov\" class=\"view-box\">\n <div class=\"title\">Structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"viz-box-container\" style=\"height: 260px; position: relative\">\n <div class=\"viz-box\" style=\"height: 90%\"></div>\n </div>\n\n <div class=\"footer\">\n <div ><b><span>Material type</span></b>:\n <span class=\"material-type-field\" ></span>\n </div>\n <div class=\"space-group-field\" style=\"display: none\">\n <b><span info-sys-data=\"space-group\">Space group</span></b>:\n <span class=\"space-group-value\" ></span>\n </div>\n <div class=\"structure-type-field\" style=\"display: none\">\n <b><span info-sys-data=\"structure-type\">Structure type</span></b>:\n <span class=\"structure-type-value\" ></span>\n </div>\n </div>\n </div>\n\n\n<!-- ***** Elastic Constants Box\n\n <div id=\"elastic-ov\" class=\"view-box\">\n <div class=\"title\">Elastic constants\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n Not analyzed yet\n </div>\n\n </div>\n-->\n\n\n <div id=\"methodology-ov\" class=\"view-box\">\n <div class=\"title\">Methodology\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div class=\"info-fields\">\n\n <div class=\"info-fields-label\" > Available calculations </div>\n\n <div style=\"float: left; width: 45%\" >\n <b><span info-sys-data=\"functional-type\">Functional</span></b>\n <div class=\"functional-field\" > </div>\n </div>\n <div style=\"float: right; width: 45%\" >\n <b><span info-sys-data=\"code-name\">Code</span></b>\n <div class=\"code-field\"> </div>\n </div>\n <div style=\"clear: both;\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"float: right; width: 60%;\">\n\n <div id=\"e-structure-ov\" class=\"view-box\" style=\"display: block\">\n <div class=\"title\">Electronic structure\n <img style=\"float: right\" class=\"to-detail\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div > <!-- style=\"margin: 12% 0; \" -->\n\n <div style=\"float: left; width: 60%; \">\n <div style=\"padding: 20px 0 20px 30px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"band-structure\">Band structure</span>\n </div>\n <div>\n <div id=\"band-plotter\" > </div>\n </div>\n\n <div class=\"footer-bs-calc\"></div>\n </div>\n </div>\n\n <div style=\"float: left; width: 40%; \">\n <div style=\"padding: 20px 30px 20px 40px\">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"DOS\">DOS</span>\n </div>\n\n <div>\n <div id=\"dos-plotter\" > </div>\n </div>\n <div class=\"footer-dos-calc\"></div>\n </div>\n </div>\n\n\n\n <div style=\"clear: both;\"></div>\n <table style=\"width: 100%\">\n <tr>\n <td class=\"spin-legend\" style=\"font-size: 0.9em; padding: 6px 30px 10px; display: none;\">\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin1\"/></svg>\n Spin <span style='font-size: 1.1em'>⇧</span> &nbsp;&nbsp;&nbsp;\n\n <svg width=\"15px\" height=\"10px\"> <polyline points=\"0,5 15,5\" class=\"plotSpin2\"/></svg>\n Spin <span style='font-size: 1.1em'>⇩</span>\n </td>\n <td class = \"similarity-data-field\">\n <div style=\"float: right; padding: 0px 50px 10px 0px; margin-top: 1px;\" class=\"similarity-finder\">\n </div>\n </td>\n </tr>\n </table>\n </div>\n <div style=\"clear: both;\"></div>\n <!--\n <div class=\"footer\">\n\n <b>Band gap</b>: <span class=\"e-struct-field\" ></span>\n </div>\n -->\n </div>\n\n <div id=\"thermal-props-ov\" class=\"view-box\" style=\"visibility: hidden\">\n <div class=\"title\">Vibrational and thermal properties\n <img style=\"float: right\" class=\"to-detail thermal-props\" src=\"img/more.svg\" />\n <div style=\"clear: both;\"></div>\n </div>\n\n <div style=\"padding: 36px; \">\n <div class=\"info-fields-label\">\n <span info-sys-data=\"heat-capacity-cv\">Specific heat</span>\n </div>\n\n\n <div>\n <div id=\"heat-plotter\" > </div>\n </div>\n <div class=\"footer-heat-calc\" style=\"text-align: center\"></div>\n </div>\n\n </div>\n\n </div>\n\n <div style=\"clear: both;\"></div>\n `;\n\n this.materialTitle= this.element.getElementsByClassName('material-title')[0];\n\n this.systemType= this.element.querySelector('.material-type-field');\n this.spaceGroupField = this.element.querySelector('.space-group-field');\n this.spaceGroupValue = this.element.querySelector('.space-group-value');\n this.structTypeField= this.element.querySelector('.structure-type-field');\n this.structTypeValue= this.element.querySelector('.structure-type-value');\n //this.band_gap = this.element.getElementsByClassName('e-struct-field')[0];\n //fields= this.element.getElementsByClassName('method-field');\n this.functional= this.element.querySelector('.functional-field');//fields[0];\n this.code= this.element.querySelector('.code-field');//fields[1];\n\n let fields= this.element.getElementsByClassName('to-detail');\n this.structureDetailBtn= fields[0];\n this.electronicStructDetailBtn= fields[2];\n this.methodologyDetailBtn= fields[1];\n this.thermalDetailBtn= fields[3];\n/*\n this.elasticDetailBtn= fields[1];\n this.methodologyDetailBtn= fields[2];\n this.electronicStructDetailBtn= fields[3];\n this.thermalDetailBtn= fields[4];\n */\n\n this.vizBox = this.element.getElementsByClassName('viz-box')[0];\n this.bandPlotter= null;\n this.bsCalcIdBox = this.element.getElementsByClassName('footer-bs-calc')[0];\n this.dosPlotter= null;\n this.dosCalcIdBox = this.element.getElementsByClassName('footer-dos-calc')[0];\n this.heatPlotter= null;\n this.heatCalcIdBox = this.element.querySelector('.footer-heat-calc');\n\n this.spinLegend = this.element.querySelector('.spin-legend');\n\n // For static ones\n InfoSys.addToInfoSystem(this.element);\n\n // Store the state of the calcs chosen on the Elec. Structure box\n this.eStructCalcs = { bs: null, dos: null};\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._events();\n }\n\n\n _events() {\n\n this.structureDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.structure);\n });\n\n this.electronicStructDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.electronicstruct);\n });\n\n this.methodologyDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.methodology);\n });\n\n this.thermalDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.thermalprops);\n });\n\n/*\n this.elasticDetailBtn.addEventListener( \"click\", (e) => {\n util.setBrowserHashPath('material', this.materialId+'/'+util.MAT_VIEW.elasticconst);\n });\n*/\n\n\n //******* Optimize, genralize:\n //this.element.querySelectorAll('.to-detail+.'+detailsId).addEventListener( \"click\", (e) => {\n // util.setBrowserHashPath('material', this.materialId+'/'+detailsId);\n }\n\n\n getEStructChosenCalcs(){\n return this.eStructCalcs;\n }\n\n\n setDetailViewsListener(listener){\n this.detailViewsListener= listener;\n }\n\n setVisible() {\n this.element.style.display = 'block';\n }\n\n\n setMaterialData() {\n\n let data = DataStore.getMaterialData();\n this.materialTitle.innerHTML= util.getMaterialTitle(data);\n this.materialId = data.material_id;\n\n let isBulk = (data.material_type === 'bulk');\n this.systemType.textContent= data.material_type;\n this.structTypeField.style.display =\n (isBulk && data.structure_type !== null ? 'block' : 'none');\n this.spaceGroupField.style.display = (isBulk ? 'block' : 'none');\n\n if (isBulk){\n this.structTypeValue.textContent= data.structure_type;\n this.spaceGroupValue.textContent = data.space_group_number+\n ' ('+data.space_group_international_short_symbol+')';\n InfoSys.addElementToInfoSystem(this.spaceGroupValue,\n 'space-group.value:'+data.space_group_number);\n }\n\n if (this.similarityFinder) {\n this.similarityFinder.element.remove();\n }\n\n if (data.similarity) {\n this.similarityFinder = new SimilarityFinder({left: 40, right: 16, top: 0, bottom: 30});\n\n const similarityFinderContainer = this.element.querySelector('.similarity-finder');\n this.similarityFinder.setSimilarityData(data.similarity);\n similarityFinderContainer.appendChild(this.similarityFinder.element);\n this.similartyDataField = this.element.querySelector('.similarity-data-field');\n InfoSys.addToInfoSystem(this.similartyDataField);\n }\n }\n\n isLoaded(hasBs, hasDOS, hasPhonon) {\n let materialData = DataStore.getMaterialData();\n let material_id = materialData.material_id;\n if (this.calcMaterialId === material_id) {\n if (hasBs && !this.bsLoaded) {\n return false;\n }\n if (hasDOS && !this.dosLoaded) {\n return false;\n }\n if (hasPhonon && !this.phononLoaded) {\n return false;\n }\n return true;\n }\n return false;\n }\n\n clearCalcsData() {\n this.materialId = null;\n this.calcMaterialId = null;\n let bsLoaded = false;\n let phononLoaded = false;\n let dosLoaded = false;\n }\n\n setCalcsData(markedTreeLeafs) {\n let matData = DataStore.getMaterialData();\n let calcs = DataStore.getCalculations();\n let functionalMap = new Map();\n let codeMap = new Map();\n\n // Get the representative calculations from the material data returned\n let calcWithBS = DataStore.getCalc(DataStore.getRepresentatives().electronic_band_structure);\n let calcWithDOS = DataStore.getCalc(DataStore.getRepresentatives().electronic_dos);\n let calcWithHeat = DataStore.getCalc(DataStore.getRepresentatives().thermodynamical_properties);\n\n if (this.isLoaded(calcWithBS !== undefined, calcWithDOS !== undefined, calcWithHeat !== undefined)) {\n return;\n }\n this.calcMaterialId = matData.material_id;\n this.bsLoaded = false;\n this.dosLoaded = false;\n this.phononLoaded = false;\n\n // Gather how many calculations there are for each functional and code\n for (let i = 0; i < calcs.length; i++) {\n if (functionalMap.has(calcs[i].functional_type)) {\n let num = functionalMap.get(calcs[i].functional_type);\n functionalMap.set(calcs[i].functional_type, ++num);\n } else {\n functionalMap.set(calcs[i].functional_type, 1);\n }\n\n let codeNameTrimed= calcs[i].code_name.trim();\n if (codeMap.has(codeNameTrimed)) {\n let num= codeMap.get(codeNameTrimed);\n codeMap.set(codeNameTrimed, ++num);\n } else {\n codeMap.set(codeNameTrimed, 1);\n }\n }\n\n if (calcWithBS !== undefined) this.eStructCalcs.bs = calcWithBS.calc_id;\n if (calcWithDOS !== undefined) this.eStructCalcs.dos = calcWithDOS.calc_id;\n\n let tempCalcId = null;\n if (calcWithBS !== undefined) tempCalcId = calcWithBS.calc_id;\n else if (calcWithDOS !== undefined) tempCalcId = calcWithDOS.calc_id;\n\n if (tempCalcId === null) {\n markedTreeLeafs.eStruct = null; // no graph data\n } else\n markedTreeLeafs.eStruct = +tempCalcId;\n\n if (calcWithHeat === undefined) {\n markedTreeLeafs.thermalProps = null;\n } else {\n markedTreeLeafs.thermalProps = +calcWithHeat.calc_id;\n }\n\n // Add list of functionals\n this.functional.textContent = \"\";\n let container = document.createElement(\"div\");\n functionalMap.forEach((number, functional) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"functional-type.value:\" + util.getDefault(functional));\n span.textContent = number + ' ' + util.getDefault(functional);\n container.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container.appendChild(linebreak);\n });\n this.functional.append(container);\n InfoSys.addToInfoSystem(this.functional);\n\n // Add list of codes\n this.code.textContent = \"\";\n let container2 = document.createElement(\"div\");\n codeMap.forEach((number, codeName) => {\n let span = document.createElement(\"span\");\n span.setAttribute(\"info-sys-data\", \"code-name.value:\" + codeName);\n span.textContent = number + ' ' + codeName;\n container2.appendChild(span);\n let linebreak = document.createElement(\"br\");\n container2.appendChild(linebreak);\n });\n this.code.append(container2);\n InfoSys.addToInfoSystem(this.code);\n\n if (DataStore.hasElecStructureData()) {\n document.getElementById('e-structure-ov').style.display = 'block';\n let isReady = () => {\n if (this.dosLoaded && this.bsLoaded) {\n document.getElementById('e-structure-ov').style.visibility = 'visible';\n }\n };\n\n if (this.bandPlotter === null){\n this.bandPlotter= new BSPlotter();\n this.bandPlotter.attach(document.getElementById('band-plotter'),undefined,316);\n }\n if (this.dosPlotter === null){\n this.dosPlotter= new DOSPlotter({left: 40, right: 20, top: 0, bottom: 30});\n this.dosPlotter.attach(document.getElementById('dos-plotter'),undefined,317);\n }\n\n if (calcWithBS === undefined){\n this.bandPlotter.setNoData();\n this.bsCalcIdBox.innerHTML = '';\n this.bsLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithBS.calc_id);\n LoadingPopup.show(\"overview_electronic_band_structure\");\n let query = JSON.stringify({properties: [\"electronic_band_structure\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let bandStructData = JSON.parse(e.target.response).electronic_band_structure;\n this.bandPlotter.setBandStructureData(bandStructData);\n this.bsCalcIdBox.innerHTML = 'From calculation <b>' + util.getShortCode(calcWithBS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithBS.functional_type+' - '+calcWithBS.code_name+')</span>';\n if (bandStructData.section_k_band_segment[0].band_energies.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.bandPlotter.setNoData();\n }\n this.bsLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_band_structure\");\n });\n }\n\n if (calcWithDOS === undefined){\n this.dosPlotter.setNoData();\n this.dosCalcIdBox.innerHTML = '';\n this.dosLoaded = true;\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithDOS.calc_id);\n LoadingPopup.show(\"overview_electronic_dos\");\n let query = JSON.stringify({properties: [\"electronic_dos\"]});\n util.serverReqPOST(url, query, e => {\n if (e.target.status === 200){\n let dosData= JSON.parse(e.target.response).electronic_dos;\n this.dosPlotter.setPoints(dosData, calcWithDOS);\n this.dosCalcIdBox.innerHTML = 'From calculation <b>'+util.getShortCode(calcWithDOS.calc_id)+\n '</b><br><span style=\"font-size: 0.8em\">('+calcWithDOS.functional_type+' - '+calcWithDOS.code_name+')</span>';\n if (dosData.dos_values.length === 2)\n this.spinLegend.style.display = 'block';\n } else {\n this.dosPlotter.setNoData();\n }\n this.dosLoaded = true;\n isReady();\n LoadingPopup.hide(\"overview_electronic_dos\");\n });\n }\n }\n\n if (!DataStore.hasThermalData()) {\n document.getElementById('thermal-props-ov').style.display = 'none';\n } else {\n document.getElementById('thermal-props-ov').style.display = 'block';\n\n if (this.heatPlotter === null){\n this.heatPlotter= new HeatCapPlotter();\n this.heatPlotter.attach(document.getElementById('heat-plotter'),undefined,317);\n }\n\n if (calcWithHeat === undefined){\n this.heatPlotter.setNoData();\n this.heatCalcIdBox.innerHTML = '';\n } else {\n let url = util.getMaterialCalcURL(this.materialId, calcWithHeat.calc_id);\n LoadingPopup.show(\"overview_thermodynamical\");\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(\"overview_thermodynamical\");\n });\n }\n }\n }\n}\n\n// EXPORTS\nmodule.exports = Overview;\n\n\n//# sourceURL=webpack:///./src/material-mod/Overview.view.js?");
/***/ }), /***/ }),
...@@ -375,17 +375,6 @@ eval("/**\n * Copyright 2019-2019 Georg Huhs\n *\n * Licensed under the Apache L ...@@ -375,17 +375,6 @@ eval("/**\n * Copyright 2019-2019 Georg Huhs\n *\n * Licensed under the Apache L
/***/ }), /***/ }),
/***/ "./src/search-mod/AutocompleteTextfield.js":
/*!*************************************************!*\
!*** ./src/search-mod/AutocompleteTextfield.js ***!
\*************************************************/
/***/ ((module) => {
"use strict";
eval("/**\n * Copyright 2019-2019 Georg Huhs\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 Basic class for extending a text field with autocomplete functionality\n */\n\n\n\nclass AutocompleteTextfield {\n\n constructor(id = \"\", placeholder = '', allowEmptyInput = false) {\n this.id = id;\n this.element = document.createElement('div');\n //this.element.style.display = 'inline';\n this.element.className = `AutocompleteTextField ${id}-autocomplete-textfield`;\n\n this.element.innerHTML = `\n <input type=\"text\" placeholder=\"${placeholder}\" /> \n <div class=\"AutocompleteTextField-dropdown ${this.id}-autocomplete-dropdown\"></div>\n `;\n\n this.input = this.element.querySelector('input');\n this.listContainer = this.element.querySelector('div');\n \n this.selectListener;\n\n // state\n this.valueList; // List of autocomplete (possible) values\n this.allowEmptyInput = allowEmptyInput;\n\n // event management\n this.input.addEventListener(\"input\", (e) => {\n this._processInput();\n });\n\n // react to keyboard navigation \n this.input.addEventListener(\"keydown\", (e) => {\n if (e.keyCode == 40) { // arrow DOWN\n if (this._getActiveListItem()) \n this._setActiveListItem(this._getActiveListItem().nextSibling);\n } else if (e.keyCode == 38) { // arrow UP\n if (this._getActiveListItem()) \n this._setActiveListItem(this._getActiveListItem().previousSibling);\n }\n });\n\n // react to enter key \n this.input.addEventListener(\"keypress\", e => {\n if (e.keyCode == 13) { // ENTER\n // simulate a click on the \"active\" item\n this._clickOnActiveItem();//_clickActive();\n }\n });\n\n // react to clicking into the textfield \n this.input.addEventListener(\"click\", (e) => {\n this._processInput();\n e.stopPropagation();\n });\n\n // close lists when someone clicks in the document\n document.addEventListener(\"click\", e => {\n this._cleanList();// this._closeAllLists();\n });\n\n\n this.listContainer.addEventListener(\"click\", e => {\n let listItem = event.target.closest('div'); // (1)\n this.setValue(listItem.textContent);\n });\n\n this.listContainer.addEventListener(\"mouseover\", e => {\n let listItem = event.target.closest('div'); // (1)\n this._setActiveListItem(listItem);\n });\n \n }\n\n\n getValue(){\n return this.input.value;\n }\n\n\n resetValue(){\n this.input.value = '';\n }\n\n\n disable(bool){\n this.input.disabled = bool;\n }\n\n\n setAutocompleteList(valueList){\n this.valueList = valueList;\n }\n\n\n setSelectListener(listener) {\n this.selectListener = listener;\n }\n\n\n _processInput() {\n const currentInput = this.input.value;\n // close any already open lists of autocompleted values\n this._cleanList();//this._closeAllLists();\n\n // in case of an empty input field\n if (!this.allowEmptyInput && !currentInput) {\n return false;\n }\n\n // for each autocomplete value\n let counter = 0;\n const matchingValues = this.valueList.filter( value => {\n const matching = value.toUpperCase().includes(currentInput.toUpperCase());\n if (matching) counter++;\n return counter <= 15 && matching;\n });\n\n this.listContainer.innerHTML = '';\n\n matchingValues.forEach( value => {\n const listItem = generateListItem(value, currentInput);\n this.listContainer.append(listItem);\n // check if a valid option was completely entered. if so, set the focus to the element corresponding to the input\n if (value.toUpperCase() === currentInput.toUpperCase()) \n this._setActiveListItem(listItem);\n\n function generateListItem(value, inputText) {\n const listItem = document.createElement(\"div\");\n if (inputText){\n const pos = value.toUpperCase().indexOf(inputText.toUpperCase()); // console.log('pos', pos)\n listItem.innerHTML += \n `${value.substring(0, pos)}<strong>${value.substring(pos, pos+inputText.length)}</strong>${value.substring(pos + inputText.length)}`;\n }else \n listItem.innerHTML = value;\n return listItem;\n }\n\n });\n\n }\n\n\n setValue(value) {\n // insert the value to the text field\n this.input.value = value;\n // notify listener \n if (this.selectListener) this.selectListener();\n // close the dropdown list of autocompleted values\n this._cleanList();//this._closeAllLists();\n }\n\n\n _getActiveListItem(){\n return this.listContainer.querySelector('.autocomplete-active');\n }\n\n\n _setActiveListItem(element){\n const currentActiveItem = this.listContainer.querySelector('.autocomplete-active');\n if (currentActiveItem) currentActiveItem.classList.remove('autocomplete-active');\n element.classList.add('autocomplete-active');\n }\n\n\n _clickOnActiveItem() {\n const activeItem = this.listContainer.querySelector('.autocomplete-active');\n if (activeItem) activeItem.click();\n }\n\n\n _cleanList() {\n this.listContainer.innerHTML = '';\n }\n \n}\n\n\n// EXPORTS\nmodule.exports = AutocompleteTextfield;\n\n\n//# sourceURL=webpack:///./src/search-mod/AutocompleteTextfield.js?");
/***/ }),
/***/ "./src/search-mod/ElemenTable.view.js": /***/ "./src/search-mod/ElemenTable.view.js":
/*!********************************************!*\ /*!********************************************!*\
!*** ./src/search-mod/ElemenTable.view.js ***! !*** ./src/search-mod/ElemenTable.view.js ***!
...@@ -425,18 +414,7 @@ eval("let util = __webpack_require__(/*! ../common/util.js */ \"./src/common/uti ...@@ -425,18 +414,7 @@ eval("let util = __webpack_require__(/*! ../common/util.js */ \"./src/common/uti
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "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 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\n//const RESULTS_PER_PAGE = 20;\n\n\nclass MaterialList {\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'MaterialList';\n\n // state \n this.visible = false;\n this.noResults = true;\n this.matMap = new Map();\n //this.currentSystemType = 'bulk';\n this.optimadeQuery = null;\n this.newestQuery = null;\n \n this.noResultsBox = document.createElement('div');\n this.noResultsBox.style = 'text-align: center; font-weight: bold'\n this.noResultsBox.innerHTML = 'NO RESULTS FOUND';\n this.element.append(this.noResultsBox);\n\n this.matListWrapper = document.createElement('div');\n this.element.append(this.matListWrapper);\n this.pagControl = new PaginationControl();\n this.matListWrapper.append(this.pagControl.element);\n this.pagControl.setPrevPageListener( page => {\n this._search(page);\n });\n this.pagControl.setNextPageListener( page => {\n this._search(page);\n })\n\n this.matListContainer = new MatListContainer();\n this.matListWrapper.append(this.matListContainer.element);\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._render();\n }\n\n\n invalidateSearch(){\n this.visible = false;\n this._render();\n }\n\n\n initSearch(optimadeQuery){\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show();\n\n let reqJson = {\n query: this.optimadeQuery,\n search_by: {}\n };\n if (page) reqJson.search_by = { page: page}\n\n // Add the restricted option from the checkbox\n let restrictedEl = document.getElementById('restricted-search');\n reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');\n console.log('SEARCHING: ', reqJson );\n const timestamp = Date.now();\n this.newestQuery = timestamp;\n\n document.querySelector('#syntax-error').style.visibility = 'hidden';\n fetch(util.getSearchURL(), {\n method: 'POST',\n headers: {'Content-Type': 'application/json;charset=utf-8'},\n body: JSON.stringify(reqJson)\n })\n .then( resp => resp.json() )\n .then( result => {\n console.log('GETTING: ', result);\n\n // If a newer query has been sent, ignore the results of an old query.\n if (this.newestQuery === timestamp) {\n // Update state\n this.noResults = (result.results.length === 0); \n this._setMatList(result.results);\n this.pagControl.set(result.pages);\n \n this.visible = true;\n this._render();\n }\n })\n .catch(error => {\n console.log(\"Error\")\n document.querySelector('#syntax-error').style.visibility = 'visible';\n })\n .finally(() => {\n LoadingPopup.hide();\n });\n \n \n/*\n oReq.addEventListener(\"error\", e => { // Not valid query\n console.log('Search ERROR - Not valid query ');\n this.total_results= 0;\n this.setData([]);\n this._updateUI();\n this.resultsContainer.style.visibility = 'visible';\n LoadingPopup.hide();\n });\n*/\n }\n\n\n _render(){\n this.element.style.display = this.visible ? '' : 'none';\n if (this.visible) {\n this.noResultsBox.style.display = this.noResults ? '' : 'none';\n this.matListWrapper.style.display = this.noResults ? 'none' : '';\n this.matListContainer.updateList(this.matMap);\n //document.querySelector('.user-msg-box').innerHTML = this.noResults ? 'No search results' : 'See result list below';\n } else {\n //document.querySelector('.user-msg-box').innerHTML = '';\n }\n }\n\n\n _setMatList(matList){\n\n if (matList.length > 0){\n matList.forEach( mat => {\n\n if (this.matMap.has(mat.formula_reduced)){\n let matArray = this.matMap.get(mat.formula_reduced);\n matArray.push(mat);\n }else\n this.matMap.set(mat.formula_reduced, [mat]);\n });\n\n }else this.matMap.clear(); // Right query - results not found\n }\n\n}\n\n\n\nclass PaginationControl{\n\n constructor(){\n this.element = document.createElement('div');\n //this.element.className = 'pag-header';\n this.element.innerHTML = `\n <div class=\"results-total\" >Results</div>\n\n <div class=\"pag-header\" >\n <span class=\"prevButton\">\n <img src=\"img/prev.svg\" style=\"display: inline;\" width=\"7px\"/> &nbsp; prev\n </span> &nbsp;&nbsp;\n <span class=\"page\"> X </span> &nbsp;&nbsp;\n <span class=\"nextButton\"> next &nbsp;\n <img src=\"img/next.svg\" width=\"7px\" />\n </span>\n </div>\n `;\n\n this.titleBox = this.element.querySelector('.results-total');\n\n this.prevButton = this.element.querySelector('.prevButton');\n this.pageElement = this.element.querySelector('.page'); \n this.nextButton = this.element.querySelector('.nextButton');\n\n this.prevButton.addEventListener('click', e => {\n if (this.pagesData.page === 1) return;\n this.prevPageListener(this.pagesData.page-1);\n });\n\n this.nextButton.addEventListener('click', e => {\n console.log('nextButton')\n if (this.pagesData.page === this.pagesData.pages) return;\n this.nextPageListener(this.pagesData.page+1);\n });\n\n this.pagesData;\n\n }\n\n\n set(pagesData){\n this.pagesData = pagesData;\n this.titleBox.innerHTML= 'Results (total: '+pagesData.total+')';\n this.pageElement.innerHTML= 'page '+pagesData.page+' / '+pagesData.pages;\n }\n\n\n setPrevPageListener(listener){\n this.prevPageListener = listener;\n }\n\n\n setNextPageListener(listener){\n this.nextPageListener = listener;\n }\n}\n\n\n\nclass MatListContainer{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'mat-list-container';\n this.element.innerHTML = `\n <table>\n <thead> <tr>\n <th style=\"width: 24%;\"></th>\n <th style=\"width: 16%;\">\n <span info-sys-data=\"space-group\">Space group</span>\n </th>\n <th style=\"width: 20%;\">\n <span >Space gr. int. symbol</span>\n </th>\n\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\n <tbody> </tbody> \n </table>\n `;\n\n this.tbody = this.element.querySelector('tbody');\n\n this.tbody.addEventListener('click', e => {\n\n let materialRow = event.target.closest('tr.mat-row');\n\n if (materialRow) \n util.setBrowserHashPath('material', materialRow.getAttribute('data-mat-id'));\n\n e.stopPropagation();\n });\n\n }\n\n\n updateList(matMap){\n //console.log('matMap', matMap);\n if (matMap.size === 0){ this.tbody.innerHTML = ''; return }\n\n let html = ''\n 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 ? mat.material_name : rFormula);\n //console.log(\"MATERIAL \", mat.formula, mat.material_name, rFormula, label);\n html +=\n `<tr class=\"mat-row\" data-mat-id=\"${mat.material_id}\">\n <td > ${label} [${mat.formula}] </td>\n <td style=\"text-align:center\" >\n ${mat.space_group_number ? mat.space_group_number : '' }\n </td>\n <td>\n ${mat.space_group_international_short_symbol ? \n mat.space_group_international_short_symbol : '' }\n </td>\n <td> ${mat.structure_type ? mat.structure_type : '' } </td>\n <td style=\"text-align:center\" > ${mat.n_calculations ? mat.n_calculations : ''} </td>\n </tr>`;\n });\n });\n\n this.tbody.innerHTML = html;\n\n InfoSys.addToInfoSystem(this.element);\n }\n\n\n}\n\n\nmodule.exports = MaterialList;\n\n\n//# sourceURL=webpack:///./src/search-mod/MaterialList.view.js?"); 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 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\n//const RESULTS_PER_PAGE = 20;\n\n\nclass MaterialList {\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'MaterialList';\n\n // state \n this.visible = false;\n this.noResults = true;\n this.matMap = new Map();\n //this.currentSystemType = 'bulk';\n this.optimadeQuery = null;\n this.newestQuery = null;\n \n this.noResultsBox = document.createElement('div');\n this.noResultsBox.style = 'text-align: center; font-weight: bold'\n this.noResultsBox.innerHTML = 'NO RESULTS FOUND';\n this.element.append(this.noResultsBox);\n\n this.matListWrapper = document.createElement('div');\n this.element.append(this.matListWrapper);\n this.pagControl = new PaginationControl();\n this.matListWrapper.append(this.pagControl.element);\n this.pagControl.setPrevPageListener( page => {\n this._search(page);\n });\n this.pagControl.setNextPageListener( page => {\n this._search(page);\n })\n\n this.matListContainer = new MatListContainer();\n this.matListWrapper.append(this.matListContainer.element);\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._render();\n }\n\n\n invalidateSearch(){\n this.visible = false;\n this._render();\n }\n\n\n initSearch(optimadeQuery){\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show(\"searching\");\n\n let reqJson = {\n query: this.optimadeQuery,\n search_by: {}\n };\n if (page) reqJson.search_by = { page: page}\n\n // Add the restricted option from the checkbox\n let restrictedEl = document.getElementById('restricted-search');\n reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');\n console.log('SEARCHING: ', reqJson );\n const timestamp = Date.now();\n this.newestQuery = timestamp;\n\n document.querySelector('#syntax-error').style.visibility = 'hidden';\n fetch(util.getSearchURL(), {\n method: 'POST',\n headers: {'Content-Type': 'application/json;charset=utf-8'},\n body: JSON.stringify(reqJson)\n })\n .then( resp => resp.json() )\n .then( result => {\n console.log('GETTING: ', result);\n\n // If a newer query has been sent, ignore the results of an old query.\n if (this.newestQuery === timestamp) {\n // Update state\n this.noResults = (result.results.length === 0); \n this._setMatList(result.results);\n this.pagControl.set(result.pages);\n \n this.visible = true;\n this._render();\n }\n })\n .catch(error => {\n console.log(\"Error\")\n document.querySelector('#syntax-error').style.visibility = 'visible';\n })\n .finally(() => {\n LoadingPopup.hide(\"searching\");\n });\n }\n\n\n _render(){\n this.element.style.display = this.visible ? '' : 'none';\n if (this.visible) {\n this.noResultsBox.style.display = this.noResults ? '' : 'none';\n this.matListWrapper.style.display = this.noResults ? 'none' : '';\n this.matListContainer.updateList(this.matMap);\n //document.querySelector('.user-msg-box').innerHTML = this.noResults ? 'No search results' : 'See result list below';\n } else {\n //document.querySelector('.user-msg-box').innerHTML = '';\n }\n }\n\n\n _setMatList(matList){\n\n if (matList.length > 0){\n matList.forEach( mat => {\n\n if (this.matMap.has(mat.formula_reduced)){\n let matArray = this.matMap.get(mat.formula_reduced);\n matArray.push(mat);\n }else\n this.matMap.set(mat.formula_reduced, [mat]);\n });\n\n }else this.matMap.clear(); // Right query - results not found\n }\n\n}\n\n\n\nclass PaginationControl{\n\n constructor(){\n this.element = document.createElement('div');\n //this.element.className = 'pag-header';\n this.element.innerHTML = `\n <div class=\"results-total\" >Results</div>\n\n <div class=\"pag-header\" >\n <span class=\"prevButton\">\n <img src=\"img/prev.svg\" style=\"display: inline;\" width=\"7px\"/> &nbsp; prev\n </span> &nbsp;&nbsp;\n <span class=\"page\"> X </span> &nbsp;&nbsp;\n <span class=\"nextButton\"> next &nbsp;\n <img src=\"img/next.svg\" width=\"7px\" />\n </span>\n </div>\n `;\n\n this.titleBox = this.element.querySelector('.results-total');\n\n this.prevButton = this.element.querySelector('.prevButton');\n this.pageElement = this.element.querySelector('.page'); \n this.nextButton = this.element.querySelector('.nextButton');\n\n this.prevButton.addEventListener('click', e => {\n if (this.pagesData.page === 1) return;\n this.prevPageListener(this.pagesData.page-1);\n });\n\n this.nextButton.addEventListener('click', e => {\n console.log('nextButton')\n if (this.pagesData.page === this.pagesData.pages) return;\n this.nextPageListener(this.pagesData.page+1);\n });\n\n this.pagesData;\n\n }\n\n\n set(pagesData){\n this.pagesData = pagesData;\n this.titleBox.innerHTML= 'Results (total: '+pagesData.total+')';\n this.pageElement.innerHTML= 'page '+pagesData.page+' / '+pagesData.pages;\n }\n\n\n setPrevPageListener(listener){\n this.prevPageListener = listener;\n }\n\n\n setNextPageListener(listener){\n this.nextPageListener = listener;\n }\n}\n\n\n\nclass MatListContainer{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'mat-list-container';\n this.element.innerHTML = `\n <table>\n <thead> <tr>\n <th style=\"width: 24%;\"></th>\n <th style=\"width: 16%;\">\n <span info-sys-data=\"space-group\">Space group</span>\n </th>\n <th style=\"width: 20%;\">\n <span >Space gr. int. symbol</span>\n </th>\n\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\n <tbody> </tbody> \n </table>\n `;\n\n this.tbody = this.element.querySelector('tbody');\n\n this.tbody.addEventListener('click', e => {\n\n let materialRow = event.target.closest('tr.mat-row');\n\n if (materialRow) \n util.setBrowserHashPath('material', materialRow.getAttribute('data-mat-id'));\n\n e.stopPropagation();\n });\n\n }\n\n\n updateList(matMap){\n //console.log('matMap', matMap);\n if (matMap.size === 0){ this.tbody.innerHTML = ''; return }\n\n let html = ''\n 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 ? mat.material_name : rFormula);\n //console.log(\"MATERIAL \", mat.formula, mat.material_name, rFormula, label);\n html +=\n `<tr class=\"mat-row\" data-mat-id=\"${mat.material_id}\">\n <td > ${label} [${mat.formula}] </td>\n <td style=\"text-align:center\" >\n ${mat.space_group_number ? mat.space_group_number : '' }\n </td>\n <td>\n ${mat.space_group_international_short_symbol ? \n mat.space_group_international_short_symbol : '' }\n </td>\n <td> ${mat.structure_type ? mat.structure_type : '' } </td>\n <td style=\"text-align:center\" > ${mat.n_calculations ? mat.n_calculations : ''} </td>\n </tr>`;\n });\n });\n\n this.tbody.innerHTML = html;\n\n InfoSys.addToInfoSystem(this.element);\n }\n\n\n}\n\n\nmodule.exports = MaterialList;\n\n\n//# sourceURL=webpack:///./src/search-mod/MaterialList.view.js?");
/***/ }),
/***/ "./src/search-mod/MaterialName.view.js":
/*!*********************************************!*\
!*** ./src/search-mod/MaterialName.view.js ***!
\*********************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\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\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet AutocompleteTextfield = __webpack_require__(/*! ./AutocompleteTextfield.js */ \"./src/search-mod/AutocompleteTextfield.js\");\n\nclass MaterialNameBox{\n\n constructor() {\n this.element = document.createElement('div');\n this.element.className = 'MaterialNameBox';\n this.element.innerHTML = \n ` <!-- the autocomplete text field goes here -->\n <button class=\"adding-name-btn\" disabled>Add to query</button>\n `;\n\n this.materialNameField = new AutocompleteTextfield('material-name', 'Start typing a material name');\n this.element.prepend(this.materialNameField.element);\n\n this.button = this.element.querySelector('.adding-name-btn');\n\n this.button.addEventListener( \"click\", (e) => {\n this.listener(this.materialNameField.getValue());\n this.materialNameField.resetValue();\n });\n\n /* enable button when a material was selected from the autocomplete list */\n this.materialNameField.setSelectListener( () => {\n this.button.disabled = false;\n this.button.focus();\n });\n\n this.materialNamesLoaded = false;\n }\n\n setAddMaterialNameListener(listener) {\n this.listener = listener;\n }\n\n setAutocomplete() {\n // load material names only once\n if (!this.materialNamesLoaded){\n let r1 = util.serverReq(util.getSuggestionURL('material_name'), (e) => {\n let names = JSON.parse(r1.response).material_name;\n this.materialNameField.setAutocompleteList(names);\n });\n this.materialNamesLoaded = true;\n }\n }\n\n disableInput() {\n this.materialNameField.disable(true);\n this.button.disabled = true;\n }\n\n enableInput() {\n this.materialNameField.disable(false);\n this.button.disabled = false;\n }\n}\n\n// EXPORTS\nmodule.exports = MaterialNameBox;\n\n\n//# sourceURL=webpack:///./src/search-mod/MaterialName.view.js?");
/***/ }), /***/ }),
...@@ -447,7 +425,7 @@ eval("/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed unde ...@@ -447,7 +425,7 @@ eval("/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed unde
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "use strict";
eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\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 SearchBox = __webpack_require__(/*! ./SearchBox.view.js */ \"./src/search-mod/SearchBox.view.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 MaterialNameBox = __webpack_require__(/*! ./MaterialName.view.js */ \"./src/search-mod/MaterialName.view.js\");\nlet FilterPanel = __webpack_require__(/*! ./FilterPanel.view.js */ \"./src/search-mod/FilterPanel.view.js\");\nlet SwitchComponent = __webpack_require__(/*! ../common/SwitchComponent.js */ \"./src/common/SwitchComponent.js\");\n\n\nconst REACTIVE_SEARCH_RIGHT = false; // Whether the right panel uses reactive search\nconst REACTIVE_SEARCH_LEFT = true; // Whether the left panel uses reactive search\nconst INVALIDATE_RESULTS = false; // Whether the search results are invalidated upon changing search criteria\n\nfunction replaceDashes(s){\n return s.split('-').join('_');\n}\n\nclass NewSearchMod {\n\n constructor() {\n\n this.userGuidance = true; // can enabled/disabled\n this.searchFilters = [];\n this.isVisible = true;\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'search-module');\n this.element.innerHTML=\n `\n <div class=\"search-filter-side\">\n </div>\n <div class=\"search-main-side\">\n <div class=\"composition\">\n <div class=\"search-box-placeholder\" > </div>\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<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button><button class=\"formula-add-btn\">Formula<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button><button class=\"name-add-btn\">Name<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button>\n </div>\n <div class=\"bool-buttons\" style=\"width: 28%; display: inline-block\" >\n OR <span id=\"and-or-switch\" ></span> AND\n <button class=\"not-button\">NOT</button>\n <button class=\"open-parentheses\" >(</button>\n <button class=\"close-parentheses\">)</button>\n </div>\n </div>\n </div>\n <div class=\"add-box\">\n <div class=\"add-panel\">\n </div>\n </div>\n <div class=\"results-panel\">\n </div>\n </div>\n `;\n\n this.filterSidePanel = this.element.querySelector('.search-filter-side');\n this.addBox = this.element.querySelector('.add-box');\n\n this.searchBox = new SearchBox();\n this.searchBox.setBoolOperator('AND');\n this.element.querySelector('.search-box-placeholder').append(this.searchBox.element);\n this.searchBox.setRemoveElementListener( (element) => {\n this.elementTable.deselectElement(element);\n });\n // When the clear all button is clicked on and \n // when the search query gets blank by removing query items\n this.searchBox.setCleanSearchQueryListener( () => { \n this.addFormulaButton.disabled = false;\n this.addMatNameButton.disabled = false;\n this.addElementButton.disabled = false;\n this.formulaBox.enableInput();\n this.materialNameBox.enableInput();\n this.elementTable.deselectAllElements();\n });\n\n \n this.searchBox.setSearchQueryChangeListener( () => {\n REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n }); \n\n /* This button could be out of the search box because its functionality\n is not part of it. It can also be removed eventually, if considered not necessary */\n this.searchButton = this.searchBox.getSearchButtonElement();\n this.searchButton.addEventListener( 'click', e => {\n this.sendQuery();\n this.addBox.style.display = 'none';\n this.isVisible = false;\n const icon = this.currentTabElement.querySelector('.search-fold-icon')\n icon.src = \"img/folded.png\";\n });\n\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 this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn');\n this.currentTabElement = this.addElementButton;\n this.currentTab = 'element';\n\n this.addPanel= this.element.querySelector('.add-panel');\n\n let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch_new');\n this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element);\n andOrSwitch.setListener( e => {\n this.searchBox.setBoolOperator( e ? 'AND' : 'OR');\n });\n\n this.notButton = this.element.querySelector('.not-button');\n this.notButton.addEventListener( 'click', e => {\n this.searchBox.addNOT();\n });\n\n this.openParenthButton = this.element.querySelector('.open-parentheses');\n this.closeParenthButton = this.element.querySelector('.close-parentheses');\n this.openParenthButton.addEventListener( 'click', e => {\n this.searchBox.addParentheses(true);\n });\n this.closeParenthButton.addEventListener( 'click', e => {\n this.searchBox.addParentheses(false);\n });\n\n this.elementTable = new ElementTable();\n this.elementTable.setClickListener(elementArray => {\n this.searchBox.addElements(elementArray);\n this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code\n this.addFormulaButton.disabled = true;\n });\n this.elementTable.setDeselectListener(e => {\n this.searchBox.removeElementORFormulaInSearchQuery(e)\n });\n\n this.formulaBox = new FormulaBox();\n this.formulaBox.setAddFormulaListener(formula => {\n if (formula.trim() !== ''){\n this.searchBox.addTag(formula, 'formula');\n this.addElementButton.disabled = true;\n this.addMatNameButton.disabled = true;\n }\n });\n\n this.materialNameBox = new MaterialNameBox();\n this.materialNameBox.setAddMaterialNameListener( name => {\n if (name.trim() !== ''){\n this.searchBox.addTag(name, 'material');\n this.addElementButton.disabled = true;\n this.addFormulaButton.disabled = true;\n }\n });\n\n this.filterPanel = new FilterPanel();\n this.filterSidePanel.appendChild(this.filterPanel.element);\n\n this.filterPanel.setPropsChangeListener( propsMap => {\n REACTIVE_SEARCH_LEFT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n })\n\n this.materialList= new MaterialList();\n this.resultsPage = this.element.querySelector('.results-panel');\n this.materialList.attachAndSetEvents(this.resultsPage);\n\n this.addPanel.appendChild(this.elementTable.element);\n\n this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');\n this.allowOtherElementsCheckbox.addEventListener( 'change', e => {\n REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n }); \n this._events();\n }\n\n\n _events() {\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 === 'name') {\n selectingElement = this.materialNameBox.element;\n // add autocomplete functionality,\n // but load data not before the name tab is activated\n this.materialNameBox.setAutocomplete();\n } else if (selectingTab === 'formula') {\n selectingElement = this.formulaBox.element;\n }\n\n this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);\n\n // Hide or show the current tab\n const icon = e.target.querySelector('.search-fold-icon')\n if (this.currentTab == selectingTab) {\n if (this.isVisible) {\n this.addBox.style.display = 'none';\n icon.src = \"img/folded.png\";\n } else {\n this.addBox.style.display = 'block';\n icon.src = \"img/unfolded.png\";\n }\n this.isVisible = !this.isVisible;\n } else {\n this.addBox.style.display = 'block';\n icon.src = \"img/unfolded.png\";\n this.isVisible = true;\n }\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 this.currentTab = selectingTab;\n this.currentTabElement = selEl;\n\n // Disable/enable bool operation buttons on the material name tab\n //this.element.querySelectorAll('.bool-buttons button').forEach( button => {\n //button.disabled = (selectingTab === 'name');\n //});\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 });\n }\n\n\n sendQuery() {\n \n //**** The optimade query must be formed from the search box and the properties selected\n const elementCheckbox = document.getElementById('allow-other-elements');\n let allowOtherElements = true\n if (elementCheckbox) {\n allowOtherElements = elementCheckbox.checked\n }\n const searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(allowOtherElements);\n const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());\n const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');\n\n // If one of them is empty, it and sep variable are ''\n this.materialList.initSearch(searchBoxOptimadeQuery+sep+propsOptimadeQuery);\n // this.materialList.invalidateSearch();\n \n function getOptimadeQueryFromProps(propsValuesMap){\n let query = '';\n propsValuesMap.forEach( (values, prop) => {\n let subquery = '';\n values.forEach( v => { // values should be and array, sometimes with just one value\n let val = ( v === true ? 'TRUE' : '\"'+v+'\"')\n subquery += (subquery === '' ? '' : ' OR ')+prop+'='+val;\n })\n query += (query === '' ? '' : ' AND ')+`(${subquery})`\n })\n return query\n }\n }\n\n _addFiltersInSearchQuery(filterMap, searchExpressionQuery){\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.must = [];\n rootQueryObj.bool.must.push( searchExpressionQuery );\n\n\n filterMap.forEach((values/*Array*/, filterName) => {\n\n let filterNameDef = replaceDashes(filterName);\n\n if (filterName === 'mass-density' || filterName === 'band-gap'){\n //***** util.eV2J() apply?\n rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) );\n\n }else if (filterName === 'band-gap-type'){ // special case\n if ( values !== 'both')\n rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct',\n ( values === 'direct' ? true : false ) ) );\n\n }else if (filterName.startsWith('has')){ // has- filters\n rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) );\n\n }else{ // normal case\n //rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) );\n rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) );\n\n //console.log(this._getESOperatorMatch(filterNameDef, values, false) );\n }\n\n });\n\n return rootQueryObj;\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 _sortElements(elements){\n let numbers = [];\n let sortedElements = [];\n elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) );\n numbers.sort( (a, b) => a - b ); // atomic number-1\n numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) );\n //console.log('_sortElements ',numbers, elString);\n return sortedElements;\n }\n\n _reduceFormula(formula, getTokens = true){\n let index = 0;\n let map = new Map();\n let key;\n while ( index < formula.length ){\n let el2 = formula.substring(index, index+2);\n let el1 = formula.substring(index, index+1);\n\n if (util.ELEMENTS.indexOf(el2) >= 0){\n map.set(el2, 1); // 1 default value\n index += 2;\n key = el2;\n //console.log('eleemnt 2chars', key);\n }else if (util.ELEMENTS.indexOf(el1) >= 0){\n map.set(el1, 1); // 1 default value\n index++;\n key = el1;\n //console.log('eleemnt 1chars', key);\n }else{ // It's a number\n let num = parseInt(el2);\n if (num >= 10) index += 2; // 2 figures number\n else index++;// 1 figure number\n //console.log('number ', num, key);\n map.set(key, num);\n }\n // console.log('FINAL LOOP', map, index);\n }\n\n let counter = 0;\n while ( !checkIfReduced(map) ){ // console.log('Reducing', map);\n let div = 1;\n if (isDivisibleBy(map, 2)) div = 2;\n else if (isDivisibleBy(map, 3)) div = 3;\n else if (isDivisibleBy(map, 5)) div = 5;\n else if (isDivisibleBy(map, 7)) div = 7;\n else if (isDivisibleBy(map, 11)) div = 11;\n\n map.forEach( (value, key) => {\n map.set(key, (value/div));\n });\n //console.log('Reducing DIV', map);\n counter++;\n if (counter > 5) break;\n }\n\n function checkIfReduced(formulaMap){\n let min = 100;\n formulaMap.forEach( (value, key) => {\n if (value < min) min = value;\n });\n return min === 1;\n }\n\n function isDivisibleBy(formulaMap, n){\n let div = true;\n formulaMap.forEach( (value, key) => {\n if (value % n !== 0) div = false;\n });\n return div;\n }\n\n let tokens = [];\n let canonicalFormula = '';\n if (getTokens){\n map.forEach( (value, key) => tokens.push(key+value) );\n }else{\n let sortedElements = this._sortElements( Array.from( map.keys() ) );\n sortedElements.forEach( element => {\n canonicalFormula += element+map.get(element);\n //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));\n });\n }\n\n\n console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula);\n return (getTokens ? tokens : canonicalFormula);\n }\n\n\n _processFormula(formula, type){\n let index = 0;\n let map = new Map();\n let key;\n while ( index < formula.length ){\n let el2 = formula.substring(index, index+2);\n let el1 = formula.substring(index, index+1);\n\n if (util.ELEMENTS.indexOf(el2) >= 0){\n map.set(el2, 1); // 1 default value\n index += 2;\n key = el2;\n //console.log('eleemnt 2chars', key);\n }else if (util.ELEMENTS.indexOf(el1) >= 0){\n map.set(el1, 1); // 1 default value\n index++;\n key = el1;\n //console.log('eleemnt 1chars', key);\n }else{ // It's a number\n let num = parseInt(el2);\n if (num >= 10) index += 2; // 2 figures number\n else index++;// 1 figure number\n //console.log('number ', num, key);\n map.set(key, num);\n }\n // console.log('FINAL LOOP', map, index);\n }\n\n if (type === 'tokens'){\n let tokens = [];\n map.forEach( (value, key) => tokens.push(key+value) );\n console.log('_processFormula RETURN: ', map, tokens);\n return tokens;\n }else{\n let sortedElements = this._sortElements( Array.from( map.keys() ) );\n if (type === 'canonical-formula'){\n let formulaString = '';\n sortedElements.forEach( element => {\n formulaString += element+map.get(element);\n });\n console.log('_processFormula RETURN: ', map, formulaString);\n return formulaString;\n }else{ // elements-string\n let elementsString = '';\n let elementsInFormulas = [];\n sortedElements.forEach( element => {\n elementsString += element;\n let val = map.get(element);\n if (val !== 0) elementsInFormulas.push(element+val);\n });\n console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]);\n return [elementsInFormulas ,elementsString];\n }\n }\n\n }\n\n}\n\n\nclass FormulaBox {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.className = 'FormulaBox';// this.element.setAttribute(\"id\",'formula-box');\n this.element.innerHTML=\n `\n <div style=\"display: flex; flex-direction: column\">\n <div style=\"display: flex; flex-direction: row\">\n <input type=\"text\" placeholder=\"Add formula to the search query above\">\n <button class=\"adding-formula-btn\" disabled> Add to query </button>\n </div>\n <div class=\"perm-tooltip search-option\" style=\"margin-right: 10px\">\n <input id=\"allow-other-elements\" name=\"allow-other-elements\" type=\"checkbox\" checked>\n <label for=\"allow-other-elements\" class=\"perm-tooltip\">Allow other elements</label>\n <span class=\"tooltiptext\">If selected, the returned materials may also contain other elements.</span>\n </div>\n </div>\n `;\n this.formulaTextField = this.element.querySelector('input');\n this.formulaButton = this.element.querySelector('.adding-formula-btn');\n\n this.formulaButton.addEventListener( \"click\", (e) => {\n this.addFormulaListener(this.formulaTextField.value);\n this.formulaTextField.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 }\n\n\n setAddFormulaListener(listener) {\n this.addFormulaListener= listener;\n }\n\n\n disableInput() {\n this.formulaTextField.disabled = true;\n this.formulaButton.disabled = true;\n }\n\n enableInput() {\n this.formulaTextField.disabled = false;\n this.formulaButton.disabled = false;\n }\n\n/*\n getAllowOtherElements(){\n return this.element.querySelector('.allow-other-elements').checked;\n }\n\n\n getMultiplesOfFormula(){\n return this.element.querySelector('.multiples-of-formula').checked;\n }*/\n\n}\n\n// EXPORTS\nmodule.exports = NewSearchMod;\n\n\n//# sourceURL=webpack:///./src/search-mod/NewSearchMod.js?"); eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\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 SearchBox = __webpack_require__(/*! ./SearchBox.view.js */ \"./src/search-mod/SearchBox.view.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 FilterPanel = __webpack_require__(/*! ./FilterPanel.view.js */ \"./src/search-mod/FilterPanel.view.js\");\nlet SwitchComponent = __webpack_require__(/*! ../common/SwitchComponent.js */ \"./src/common/SwitchComponent.js\");\n\n\nconst REACTIVE_SEARCH_RIGHT = false; // Whether the right panel uses reactive search\nconst REACTIVE_SEARCH_LEFT = true; // Whether the left panel uses reactive search\nconst INVALIDATE_RESULTS = false; // Whether the search results are invalidated upon changing search criteria\n\nfunction replaceDashes(s){\n return s.split('-').join('_');\n}\n\nclass NewSearchMod {\n\n constructor() {\n\n this.userGuidance = true; // can enabled/disabled\n this.searchFilters = [];\n this.isVisible = true;\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'search-module');\n this.element.innerHTML=\n `\n <div class=\"search-filter-side\">\n </div>\n <div class=\"search-main-side\">\n <div class=\"composition\">\n <div class=\"search-box-placeholder\" > </div>\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<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button><button class=\"formula-add-btn\">Formula<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button><button class=\"name-add-btn\">Name<img class=\"search-fold-icon\" src=\"img/unfolded.png\"></button>\n </div>\n <div class=\"bool-buttons\" style=\"width: 28%; display: inline-block\" >\n OR <span id=\"and-or-switch\" ></span> AND\n <button class=\"not-button\">NOT</button>\n <button class=\"open-parentheses\" >(</button>\n <button class=\"close-parentheses\">)</button>\n </div>\n </div>\n </div>\n <div class=\"add-box\">\n <div class=\"add-panel\">\n </div>\n </div>\n <div class=\"results-panel\">\n </div>\n </div>\n `;\n\n this.filterSidePanel = this.element.querySelector('.search-filter-side');\n this.addBox = this.element.querySelector('.add-box');\n\n this.searchBox = new SearchBox();\n this.searchBox.setBoolOperator('AND');\n this.element.querySelector('.search-box-placeholder').append(this.searchBox.element);\n this.searchBox.setRemoveElementListener( (element) => {\n this.elementTable.deselectElement(element);\n });\n // When the clear all button is clicked on and \n // when the search query gets blank by removing query items\n this.searchBox.setCleanSearchQueryListener( () => { \n this.addFormulaButton.disabled = false;\n this.addMatNameButton.disabled = false;\n this.addElementButton.disabled = false;\n this.formulaBox.enableInput();\n this.materialNameBox.enableInput();\n this.elementTable.deselectAllElements();\n });\n\n \n this.searchBox.setSearchQueryChangeListener( () => {\n REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n }); \n\n /* This button could be out of the search box because its functionality\n is not part of it. It can also be removed eventually, if considered not necessary */\n this.searchButton = this.searchBox.getSearchButtonElement();\n this.searchButton.addEventListener( 'click', e => {\n this.sendQuery();\n this.addBox.style.display = 'none';\n this.isVisible = false;\n const icon = this.currentTabElement.querySelector('.search-fold-icon')\n icon.src = \"img/folded.png\";\n });\n\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 this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn');\n this.currentTabElement = this.addElementButton;\n this.currentTab = 'element';\n\n this.addPanel= this.element.querySelector('.add-panel');\n\n let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch_new');\n this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element);\n andOrSwitch.setListener( e => {\n this.searchBox.setBoolOperator( e ? 'AND' : 'OR');\n });\n\n this.notButton = this.element.querySelector('.not-button');\n this.notButton.addEventListener( 'click', e => {\n this.searchBox.addNOT();\n });\n\n this.openParenthButton = this.element.querySelector('.open-parentheses');\n this.closeParenthButton = this.element.querySelector('.close-parentheses');\n this.openParenthButton.addEventListener( 'click', e => {\n this.searchBox.addParentheses(true);\n });\n this.closeParenthButton.addEventListener( 'click', e => {\n this.searchBox.addParentheses(false);\n });\n\n this.elementTable = new ElementTable();\n this.elementTable.setClickListener(elementArray => {\n this.searchBox.addElements(elementArray);\n this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code\n this.addFormulaButton.disabled = true;\n });\n this.elementTable.setDeselectListener(e => {\n this.searchBox.removeElementORFormulaInSearchQuery(e)\n });\n\n this.formulaBox = new FormulaBox(\"Add formula to the search query above\");\n this.formulaBox.setAddListener(formula => {\n if (formula.trim() !== ''){\n this.searchBox.addTag(formula, 'formula');\n this.addElementButton.disabled = true;\n this.addMatNameButton.disabled = true;\n }\n });\n\n this.materialNameBox = new TextBox(\"Add material name to the search query above\");\n this.materialNameBox.setAddListener(name => {\n if (name.trim() !== ''){\n this.searchBox.addTag(name, 'material');\n this.addElementButton.disabled = true;\n this.addFormulaButton.disabled = true;\n }\n });\n\n this.filterPanel = new FilterPanel();\n this.filterSidePanel.appendChild(this.filterPanel.element);\n\n this.filterPanel.setPropsChangeListener( propsMap => {\n REACTIVE_SEARCH_LEFT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n })\n\n this.materialList= new MaterialList();\n this.resultsPage = this.element.querySelector('.results-panel');\n this.materialList.attachAndSetEvents(this.resultsPage);\n\n this.addPanel.appendChild(this.elementTable.element);\n\n this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');\n this.allowOtherElementsCheckbox.addEventListener( 'change', e => {\n REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();\n }); \n this._events();\n }\n\n\n _events() {\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 === 'name') {\n selectingElement = this.materialNameBox.element;\n } else if (selectingTab === 'formula') {\n selectingElement = this.formulaBox.element;\n }\n\n this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);\n\n // Hide or show the current tab\n const icon = e.target.querySelector('.search-fold-icon')\n if (this.currentTab == selectingTab) {\n if (this.isVisible) {\n this.addBox.style.display = 'none';\n icon.src = \"img/folded.png\";\n } else {\n this.addBox.style.display = 'block';\n icon.src = \"img/unfolded.png\";\n }\n this.isVisible = !this.isVisible;\n } else {\n this.addBox.style.display = 'block';\n icon.src = \"img/unfolded.png\";\n this.isVisible = true;\n }\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 this.currentTab = selectingTab;\n this.currentTabElement = selEl;\n\n // Disable/enable bool operation buttons on the material name tab\n //this.element.querySelectorAll('.bool-buttons button').forEach( button => {\n //button.disabled = (selectingTab === 'name');\n //});\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 });\n }\n\n\n sendQuery() {\n \n //**** The optimade query must be formed from the search box and the properties selected\n const elementCheckbox = document.getElementById('allow-other-elements');\n let allowOtherElements = true\n if (elementCheckbox) {\n allowOtherElements = elementCheckbox.checked\n }\n const searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(allowOtherElements);\n const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());\n const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');\n\n // If one of them is empty, it and sep variable are ''\n this.materialList.initSearch(searchBoxOptimadeQuery+sep+propsOptimadeQuery);\n // this.materialList.invalidateSearch();\n \n function getOptimadeQueryFromProps(propsValuesMap){\n let query = '';\n propsValuesMap.forEach( (values, prop) => {\n let subquery = '';\n values.forEach( v => { // values should be and array, sometimes with just one value\n let val = ( v === true ? 'TRUE' : '\"'+v+'\"')\n subquery += (subquery === '' ? '' : ' OR ')+prop+'='+val;\n })\n query += (query === '' ? '' : ' AND ')+`(${subquery})`\n })\n return query\n }\n }\n\n _addFiltersInSearchQuery(filterMap, searchExpressionQuery){\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.must = [];\n rootQueryObj.bool.must.push( searchExpressionQuery );\n\n\n filterMap.forEach((values/*Array*/, filterName) => {\n\n let filterNameDef = replaceDashes(filterName);\n\n if (filterName === 'mass-density' || filterName === 'band-gap'){\n //***** util.eV2J() apply?\n rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) );\n\n }else if (filterName === 'band-gap-type'){ // special case\n if ( values !== 'both')\n rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct',\n ( values === 'direct' ? true : false ) ) );\n\n }else if (filterName.startsWith('has')){ // has- filters\n rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) );\n\n }else{ // normal case\n //rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) );\n rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) );\n\n //console.log(this._getESOperatorMatch(filterNameDef, values, false) );\n }\n\n });\n\n return rootQueryObj;\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 _sortElements(elements){\n let numbers = [];\n let sortedElements = [];\n elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) );\n numbers.sort( (a, b) => a - b ); // atomic number-1\n numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) );\n //console.log('_sortElements ',numbers, elString);\n return sortedElements;\n }\n\n _reduceFormula(formula, getTokens = true){\n let index = 0;\n let map = new Map();\n let key;\n while ( index < formula.length ){\n let el2 = formula.substring(index, index+2);\n let el1 = formula.substring(index, index+1);\n\n if (util.ELEMENTS.indexOf(el2) >= 0){\n map.set(el2, 1); // 1 default value\n index += 2;\n key = el2;\n //console.log('eleemnt 2chars', key);\n }else if (util.ELEMENTS.indexOf(el1) >= 0){\n map.set(el1, 1); // 1 default value\n index++;\n key = el1;\n //console.log('eleemnt 1chars', key);\n }else{ // It's a number\n let num = parseInt(el2);\n if (num >= 10) index += 2; // 2 figures number\n else index++;// 1 figure number\n //console.log('number ', num, key);\n map.set(key, num);\n }\n // console.log('FINAL LOOP', map, index);\n }\n\n let counter = 0;\n while ( !checkIfReduced(map) ){ // console.log('Reducing', map);\n let div = 1;\n if (isDivisibleBy(map, 2)) div = 2;\n else if (isDivisibleBy(map, 3)) div = 3;\n else if (isDivisibleBy(map, 5)) div = 5;\n else if (isDivisibleBy(map, 7)) div = 7;\n else if (isDivisibleBy(map, 11)) div = 11;\n\n map.forEach( (value, key) => {\n map.set(key, (value/div));\n });\n //console.log('Reducing DIV', map);\n counter++;\n if (counter > 5) break;\n }\n\n function checkIfReduced(formulaMap){\n let min = 100;\n formulaMap.forEach( (value, key) => {\n if (value < min) min = value;\n });\n return min === 1;\n }\n\n function isDivisibleBy(formulaMap, n){\n let div = true;\n formulaMap.forEach( (value, key) => {\n if (value % n !== 0) div = false;\n });\n return div;\n }\n\n let tokens = [];\n let canonicalFormula = '';\n if (getTokens){\n map.forEach( (value, key) => tokens.push(key+value) );\n }else{\n let sortedElements = this._sortElements( Array.from( map.keys() ) );\n sortedElements.forEach( element => {\n canonicalFormula += element+map.get(element);\n //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));\n });\n }\n\n\n console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula);\n return (getTokens ? tokens : canonicalFormula);\n }\n\n\n _processFormula(formula, type){\n let index = 0;\n let map = new Map();\n let key;\n while ( index < formula.length ){\n let el2 = formula.substring(index, index+2);\n let el1 = formula.substring(index, index+1);\n\n if (util.ELEMENTS.indexOf(el2) >= 0){\n map.set(el2, 1); // 1 default value\n index += 2;\n key = el2;\n //console.log('eleemnt 2chars', key);\n }else if (util.ELEMENTS.indexOf(el1) >= 0){\n map.set(el1, 1); // 1 default value\n index++;\n key = el1;\n //console.log('eleemnt 1chars', key);\n }else{ // It's a number\n let num = parseInt(el2);\n if (num >= 10) index += 2; // 2 figures number\n else index++;// 1 figure number\n //console.log('number ', num, key);\n map.set(key, num);\n }\n // console.log('FINAL LOOP', map, index);\n }\n\n if (type === 'tokens'){\n let tokens = [];\n map.forEach( (value, key) => tokens.push(key+value) );\n console.log('_processFormula RETURN: ', map, tokens);\n return tokens;\n }else{\n let sortedElements = this._sortElements( Array.from( map.keys() ) );\n if (type === 'canonical-formula'){\n let formulaString = '';\n sortedElements.forEach( element => {\n formulaString += element+map.get(element);\n });\n console.log('_processFormula RETURN: ', map, formulaString);\n return formulaString;\n }else{ // elements-string\n let elementsString = '';\n let elementsInFormulas = [];\n sortedElements.forEach( element => {\n elementsString += element;\n let val = map.get(element);\n if (val !== 0) elementsInFormulas.push(element+val);\n });\n console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]);\n return [elementsInFormulas ,elementsString];\n }\n }\n\n }\n\n}\n\nclass TextBox {\n constructor(placeholder) {\n this.element = document.createElement('div');\n this.element.className = 'TextBox';\n const column = document.createElement('div');\n column.style.display = 'flex';\n column.style.flexDirection = 'column';\n const row = document.createElement('div');\n row.style.display = 'flex';\n row.style.flexDirection = 'row';\n this.textField = document.createElement('input');\n this.textField.type = 'text';\n this.textField.placeholder = placeholder;\n row.append(this.textField);\n this.button = document.createElement('button');\n this.button.className = 'adding-btn';\n this.button.disabled = true;\n this.button.textContent = \"Add to query\";\n row.append(this.button);\n column.append(row);\n this.element.append(column);\n\n this.button.addEventListener( \"click\", (e) => {\n this.addListener(this.textField.value);\n this.textField.value = '';\n });\n\n // React to enter key \n this.textField.addEventListener(\"keypress\", e => {\n if (e.keyCode == 13) { // ENTER\n this.addListener(this.textField.value);\n this.textField.value = '';\n }\n });\n\n this.textField.addEventListener( 'input', e => {\n this.button.disabled = (this.textField.value === '');\n });\n }\n\n setAddListener(listener) {\n this.addListener = listener;\n }\n\n disableInput() {\n this.textField.disabled = true;\n this.button.disabled = true;\n }\n\n enableInput() {\n this.textField.disabled = false;\n this.button.disabled = false;\n }\n}\n\nclass FormulaBox extends TextBox {\n constructor(placeholder) {\n super(placeholder);\n const checkbox = document.createElement(\"div\");\n checkbox.className = \"perm-tooltip search-option\";\n checkbox.style.marginRight = \"10px\";\n const input = document.createElement(\"input\");\n input.id = \"allow-other-elements\";\n input.name = \"allow-other-elements\";\n input.type = \"checkbox\";\n input.checked = true;\n checkbox.append(input)\n const label = document.createElement(\"label\");\n label[\"for\"] = \"allow-other-elements\";\n label.className = \"perm-tooltip\";\n label.textContent = \"Allow other elements\";\n checkbox.append(label)\n const span = document.createElement(\"span\");\n span.className = \"tooltiptext\";\n span.textContent = \"If selected, the returned materials may also contain other elements.\";\n checkbox.append(span);\n this.element.children[0].append(checkbox);\n }\n}\n\n// EXPORTS\nmodule.exports = NewSearchMod;\n\n\n//# sourceURL=webpack:///./src/search-mod/NewSearchMod.js?");
/***/ }), /***/ }),
......
window.nomadEnv = { window.nomadEnv = {
apiRoot: "https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/", //apiRoot: "https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/", apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/", //apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/",
//apiRoot: "http://localhost:8000/fairdi/nomad/latest/api/encyclopedia/", //apiRoot: "http://localhost:8000/fairdi/nomad/latest/api/encyclopedia/",
......
...@@ -511,26 +511,17 @@ div.title span.unfolded::before{ ...@@ -511,26 +511,17 @@ div.title span.unfolded::before{
/* Formula and Material name boxes */ /* Formula and Material name boxes */
.FormulaBox, .TextBox {
.MaterialNameBox{
display: flex; display: flex;
justify-content: center; justify-content: center;
text-align: left; text-align: left;
background-color: #DDD; background-color: #DDD;
} }
.FormulaBox input, .MaterialNameBox input{ .TextBox input[type=text] {
padding: 10px; padding: 10px;
border: 0; border: 0;
font-size: 0.9em; font-size: 0.9em;
}
.MaterialNameBox input{
box-sizing: border-box;
width: 100%;
}
.FormulaBox input[type=text]{
box-sizing: border-box; box-sizing: border-box;
width: 580px; width: 580px;
} }
...@@ -884,7 +875,7 @@ div#specialRows{margin: 20px 40px;} ...@@ -884,7 +875,7 @@ div#specialRows{margin: 20px 40px;}
} }
.MaterialList{ /*width: 690px; margin: 0 auto 80px;*/ .MaterialList{ /*width: 690px; margin: 0 auto 80px;*/
margin-top: 30px; margin-top: 25px;
} }
/* #paginationWg erased*/ /* #paginationWg erased*/
......
...@@ -30,9 +30,6 @@ let loadingPopup = document.querySelector('#loading-popup'); ...@@ -30,9 +30,6 @@ let loadingPopup = document.querySelector('#loading-popup');
let loadSet = new Set(); let loadSet = new Set();
function show(id) { function show(id) {
if (!window.allowNewLoadPopup) {
return;
}
loadSet.add(id, true); loadSet.add(id, true);
let ttRect = loadingPopup.getBoundingClientRect(); let ttRect = loadingPopup.getBoundingClientRect();
let leftPos = (window.innerWidth - ttRect.width)/2; let leftPos = (window.innerWidth - ttRect.width)/2;
......
...@@ -45,14 +45,10 @@ function route() { ...@@ -45,14 +45,10 @@ function route() {
if (hashHistory.length && historyLength == length) { if (hashHistory.length && historyLength == length) {
if (hashHistory[hashHistory.length - 2] == hash) { if (hashHistory[hashHistory.length - 2] == hash) {
hashHistory = hashHistory.slice(0, -1); hashHistory = hashHistory.slice(0, -1);
LoadingPopup.reset();
window.allowNewLoadPopup = false;
} else { } else {
window.allowNewLoadPopup = true;
hashHistory.push(hash); hashHistory.push(hash);
} }
} else { } else {
window.allowNewLoadPopup = true;
hashHistory.push(hash); hashHistory.push(hash);
historyLength = length; historyLength = length;
} }
......
...@@ -40,7 +40,6 @@ let DataStore = require('./material-mod/DataStore.js'); ...@@ -40,7 +40,6 @@ let DataStore = require('./material-mod/DataStore.js');
// main DOM elements // main DOM elements
let contentElement = document.getElementById('content'); let contentElement = document.getElementById('content');
let titleElement = document.querySelector('title'); let titleElement = document.querySelector('title');
window.allowNewLoadPopup = true;
var getUrl = window.location; var getUrl = window.location;
var guiRoot = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname; var guiRoot = getUrl.protocol + "//" + getUrl.host + "/" + getUrl.pathname;
......
...@@ -532,7 +532,7 @@ class Overview { ...@@ -532,7 +532,7 @@ class Overview {
this.heatCalcIdBox.innerHTML = ''; this.heatCalcIdBox.innerHTML = '';
} else { } else {
let url = util.getMaterialCalcURL(this.materialId, calcWithHeat.calc_id); let url = util.getMaterialCalcURL(this.materialId, calcWithHeat.calc_id);
LoadingPopup.show(); LoadingPopup.show("overview_thermodynamical");
let query = JSON.stringify({properties: ["thermodynamical_properties"]}); let query = JSON.stringify({properties: ["thermodynamical_properties"]});
util.serverReqPOST(url, query, e => { util.serverReqPOST(url, query, e => {
if (e.target.status === 200) { if (e.target.status === 200) {
...@@ -545,7 +545,7 @@ class Overview { ...@@ -545,7 +545,7 @@ class Overview {
this.phononLoaded = true; this.phononLoaded = true;
document.getElementById('thermal-props-ov').style.visibility = 'visible'; document.getElementById('thermal-props-ov').style.visibility = 'visible';
} }
LoadingPopup.hide(); LoadingPopup.hide("overview_thermodynamical");
}); });
} }
} }
......
...@@ -39,6 +39,7 @@ class AutocompleteTextfield { ...@@ -39,6 +39,7 @@ class AutocompleteTextfield {
this.listContainer = this.element.querySelector('div'); this.listContainer = this.element.querySelector('div');
this.selectListener; this.selectListener;
this.index = 0;
// state // state
this.valueList; // List of autocomplete (possible) values this.valueList; // List of autocomplete (possible) values
...@@ -46,17 +47,27 @@ class AutocompleteTextfield { ...@@ -46,17 +47,27 @@ class AutocompleteTextfield {
// event management // event management
this.input.addEventListener("input", (e) => { this.input.addEventListener("input", (e) => {
this.index = 0;
this._processInput(); this._processInput();
}); });
// react to keyboard navigation // react to keyboard navigation
this.input.addEventListener("keydown", (e) => { this.input.addEventListener("keydown", (e) => {
const nItems = this.listContainer.children.length;
if (e.keyCode == 40) { // arrow DOWN if (e.keyCode == 40) { // arrow DOWN
if (this._getActiveListItem()) this.index = (index+1) % nItems;
this._setActiveListItem(this._getActiveListItem().nextSibling); this._setActiveListItem(this.listContainer.children[this.index]);
//if (this._getActiveListItem()) {
//this._setActiveListItem(this._getActiveListItem().nextSibling);
//} else {
//this._setActiveListItem(this.listContainer.children[0]);
//}
} else if (e.keyCode == 38) { // arrow UP } else if (e.keyCode == 38) { // arrow UP
if (this._getActiveListItem()) this.index = (index-1) % nItems;
this._setActiveListItem(this._getActiveListItem().previousSibling); this._setActiveListItem(this.listContainer.children[this.index]);
//if (this._getActiveListItem()) {
//this._setActiveListItem(this._getActiveListItem().previousSibling);
//}
} }
}); });
...@@ -138,9 +149,10 @@ class AutocompleteTextfield { ...@@ -138,9 +149,10 @@ class AutocompleteTextfield {
this.listContainer.innerHTML = ''; this.listContainer.innerHTML = '';
matchingValues.forEach( value => { matchingValues.forEach( (value, i) => {
const listItem = generateListItem(value, currentInput); const listItem = generateListItem(value, currentInput);
this.listContainer.append(listItem); this.listContainer.append(listItem);
// check if a valid option was completely entered. if so, set the focus to the element corresponding to the input // check if a valid option was completely entered. if so, set the focus to the element corresponding to the input