Commit 05b297ee authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Restyled the composition search.

parent a8f851c5
......@@ -393,7 +393,7 @@ eval("/**\n * Copyright 2019-2019 Georg Huhs\n *\n * Licensed under the Apache L
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is the UI component implementing the interactive Element Table\n in the search front. It need to communicate to other UI components\n so it exposes several listeners\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\n\n// constans\n\nconst GROUPS = new Map([\n [1,['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n [2,['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n [3,['Sc', 'Y']],\n [4,['Ti', 'Zr', 'Hf', 'Rf']],\n [5,['V', 'Nb', 'Ta', 'Ha']],\n [6,['Cr', 'Mo', 'W', 'Sg']],\n [7,['Mn', 'Tc', 'Re', 'Ns']],\n [8,['Fe', 'Ru', 'Os', 'Hs']],\n [9,['Co', 'Rh', 'Ir', 'Mt']],\n [10,['Ni', 'Pd', 'Pt', 'Ds']],\n [11,['Cu', 'Ag', 'Au', 'Rg']],\n [12,['Zn', 'Cd', 'Hg', 'Cn']],\n [13,['B', 'Al', 'Ga', 'In', 'Tl', 'Nh']],\n [14,['C', 'Si', 'Ge', 'Sn', 'Pb', 'Fl']],\n [15,['N', 'P', 'As', 'Sb', 'Bi', 'Mc']],\n [16,['O', 'S', 'Se', 'Te', 'Po', 'Lv']],\n [17,['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n [18,['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n [19,['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu']],\n [20,['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr']]\n]);\n\nconst BLOCKS = new Map([\n ['metalloids',['B', 'Si', 'Ge', 'As', 'Sb', 'Te', 'Po']],\n ['other-non-metals',['H', 'C', 'N', 'O', 'P', 'S', 'Se']],\n ['halogens',['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n ['noble-gases',['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n ['alkali-metals',['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n ['alkaline-earth-metals',['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n ['lanthanoids',['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er'\n ,'Tm','Yb','Lu']],\n ['actinoids',['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm'\n ,'Md','No','Lr']],\n ['transition-metals', ['Sc', 'Y','Ti', 'Zr', 'Hf', 'Rf','V','Nb','Ta','Ha'\n ,'Cr','Mo','W','Sg','Mn','Tc','Re','Ns','Fe','Ru','Os','Hs','Co','Rh','Ir'\n , 'Mt','Ni','Pd','Pt', 'Ds', 'Cu','Ag','Au', 'Rg', 'Zn','Cd','Hg', 'Cn']],\n ['post-transition-metals', ['Al','Ga', 'In', 'Tl', 'Nh', 'Sn', 'Pb', 'Fl', 'Bi', 'Mc', 'Lv']]\n]);\n\nconst BLOCKS_COLORS = new Map([\n ['metalloids','#F9E298'],\n ['other-non-metals','#F2B01D'],\n ['halogens','#85ADC1'],\n ['noble-gases','#F7D660'],\n ['alkali-metals','#D04629'],\n ['alkaline-earth-metals','#F7B57D'],\n ['transition-metals', '#F58737'],\n ['post-transition-metals', '#AE4747'],\n ['lanthanoids','#3B91AE'],\n ['actinoids','#E97147']\n]);\n\nlet elementNames = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium',\n 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine',\n 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon',\n 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium',\n 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium',\n 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper',\n 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium',\n 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium',\n 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium',\n 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium',\n 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon',\n 'Cesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium',\n 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium',\n 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium',\n 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten',\n 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold',\n 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium',\n 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium',\n 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium',\n 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium',\n 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium',\n 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium',\n 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium',\n 'Livermorium', 'Tennessine', 'Oganesson'\n];\n\n\n// utility functions\n\nfunction getElementBlock(elSymbol){\n let block;\n BLOCKS.forEach(function(value, key) {\n //console.log(key + \" \" + value);\n if (value.indexOf(elSymbol) >= 0)\n block= key;\n });\n return block;\n}\n\n\nlet CELL_WIDTH= 36;\n\nfunction getCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cell '+getElementBlock(elSymbol)+'\" data-el=\"el-'+elSymbol+'\">'+\n '<b>'+elSymbol+'</b> <div>'+elNum+'</div> </td>';\n}\n\nfunction getOddCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cellpad '+getElementBlock(elSymbol)+'\" data-el=\"el-X\">'+\n '<b>&nbsp;</b> <div>&nbsp;</div> </td>';\n}\n\n\n/** Group selection not implemented\n\nfunction getGroupSelectorHtml(num){\n return '<th class=\"group-sel\" data-group=\"'+num+'\"><div></div></th>';\n}\n\nfunction getGroupSelectorHtmlLAcAc(num){\n return '<th class=\"group-sel-la-ac\" data-group=\"'+num+'\"><div></div></th>';\n}*/\n\n/************************/\n\n\nclass ElemenTable{\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','elementable');\n\n // header with dropdown\n let tempHtml = '<div class=\"element-info\"></div>';\n\n tempHtml+= '<div class=\"ptWrapper\">';\n\n tempHtml+= '<table id=\"pt-main\">'; // table zone div\n\n /* Deactivated for the moment\n // header with group selectors\n tempHtml+= '<thead><tr>';//'<div id=\"group-selectors\">';\n for (let i= 1; i<=18; i++){\n tempHtml+= getGroupSelectorHtml(i);\n }\n tempHtml+= '</tr></thead>';// selectors\n */\n\n tempHtml+= '<tbody>';\n // row 1\n tempHtml+= '<tr>'+getCellHtml(1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"16\"></td>';\n tempHtml+= getCellHtml(2)+'</tr>';\n\n let get8ElementRowHtml= (initPos) => {\n tempHtml+= '<tr>'+getCellHtml(initPos)+getCellHtml(initPos+1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"10\"></td>';\n for (let i= initPos+2; i< initPos+8; i++) tempHtml+= getCellHtml(i);\n tempHtml+= '</tr>';//div.row\n }\n\n // row 2 and 3\n get8ElementRowHtml(3);\n get8ElementRowHtml(11);\n\n // row 4,5, 6\n let counter= 19;\n for (let i= 0; i<4; i++){\n tempHtml+= '<tr>';\n for (let j= 0; j<18; j++){\n if (counter === 57 || counter === 89){\n tempHtml += getOddCellHtml(counter);\n counter += 15;\n }else{\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n }\n tempHtml+= '</tr>';//div.row\n }\n\n\n tempHtml+= '</tbody></table>';\n\n // Lanthanides and Actinides\n tempHtml+= '<div id=\"specialRows\"><table id=\"pt-laac\">';\n for (let i= 0; i<2; i++){\n tempHtml+= '<tr>';\n counter = (i === 0 ? 57 : 89);\n //tempHtml+= getGroupSelectorHtmlLAcAc((i === 0 ? 19 : 20));\n for (let j= 0; j<15; j++){\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n tempHtml+= '</tr>';//div.row\n }\n tempHtml+= '</table></div>'; //div#specialRows\n\n\n // Block labels\n tempHtml+= `<div class=\"legend\">\n <div class=\"alkali-metals\">Alkali metals</div>\n <div class=\"alkaline-earth-metals\">Alkaline earth metals</div>\n <div class=\"transition-metals\">Transition metals</div>\n <div class=\"post-transition-metals\">Post-transition metals</div>\n <div class=\"metalloids\">Metalloids</div>\n <div class=\"other-non-metals\">Other nonmetals</div>\n <div class=\"halogens\">Halogens</div>\n <div class=\"noble-gases\">Noble gases</div>\n <div class=\"lanthanoids\">Lanthanoids</div>\n <div class=\"actinoids\">Actinoids</div>\n </div>`;//'<div id=\"group-selectors\">';\n\n\n tempHtml+= '</div>'; // ptWrapper\n\n this.element.innerHTML= tempHtml;\n this.elementInfo= this.element.getElementsByClassName('element-info')[0];\n this.tableZone= this.element.getElementsByClassName('ptWrapper')[0];\n //this.specialRows= this.element.querySelector('#specialRows');\n\n this._events();\n }\n\n\n _events() {\n\n // One listener for all diferent clicks (simple element, group)\n var adhocListener= (e) => {\n\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let element = e.target;\n if (className === ''){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (className.indexOf('cellpad') >= 0) return; // structural empty table cells\n\n if (className.indexOf('group-sel') >= 0){ // group selector cells\n //*** Not working at the moment - group selection deactivated\n let elements= GROUPS.get(parseInt(element.getAttribute('data-group')));\n //console.log(\"group-sel \"+elements);\n this.clickListener(elements);//let done =\n //if (done)\n for (var i = 0; i < elements.length; i++)\n this.selectElement(elements[i]);\n\n }else if (className.indexOf('cell') >= 0){ // element cells\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === '&nbsp;') return; // blank cells\n //console.log(\"elSymbol-sel \"+elSymbol);\n if (className.indexOf('el-selected') >= 0){ // If selected\n this.deselectListener(elSymbol);//this.deselectElement(elSymbol);\n }else{ // If not selected\n this.clickListener([elSymbol]);//let done = this.clickListener([elSymbol]);\n this.selectElement(elSymbol);//if (done) this.selectElement(elSymbol);\n }\n }\n }\n //e.stopPropagation();\n };\n\n // Event listener set in the root div element\n this.tableZone.addEventListener('click',adhocListener,true);\n\n this.tableZone.addEventListener('mouseover',e => {\n\n let elSymbol= getElement(e);\n //console.log(\"ENTERIG elSymbol-sel \"+elSymbol);\n if (elSymbol !== null){\n //console.log(\"elSymbol-sel \"+elSymbol);\n this.elementInfo.style.display = 'block';\n let borderColor= BLOCKS_COLORS.get(getElementBlock(elSymbol));\n this.elementInfo.style.borderColor = borderColor;\n let number= util.ELEMENTS.indexOf(elSymbol)+1;\n this.elementInfo.innerHTML= `\n <div>\n <div style=\"float: right; padding: 3px 4px;border-left: 3px solid ${borderColor};\n border-bottom: 3px solid ${borderColor}\" > ${number} </div>\n <div style=\"clear: right;\"></div>\n </div>\n <div class=\"symbol\">${elSymbol} </div>\n <div class=\"\">${elementNames[number-1]} </div>\n `\n }\n });\n\n this.tableZone.addEventListener('mouseout',e => {\n let element= getElement(e);\n if (element !== null) this.elementInfo.style.display = 'none';\n });\n\n }\n\n // Observer pattern\n setClickListener(listener) {\n this.clickListener= listener;\n }\n\n\n setDeselectListener(listener) {\n this.deselectListener= listener;\n }\n\n\n selectElement(elSymbol) {\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell el-selected';\n }\n\n\n deselectElement(elSymbol) {\n //document.getElementById('el-'+elSymbol).className= 'cell '+getElementBlock(elSymbol);\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell '+getElementBlock(elSymbol);\n }\n\n\n deselectAllElements(){\n let selectedElements = this.element.querySelectorAll('td.el-selected');\n\n //selectedElements.forEach( element => {\n for (let i = 0; i < selectedElements.length; ++i){\n let elSymbol = selectedElements[i].getAttribute('data-el').substring(3);\n selectedElements[i].className= 'cell '+getElementBlock(elSymbol);\n }\n }\n\n} // class ElemenTable\n\n\nfunction getElement(e){\n\n let element = null;\n let className = null;\n //console.log(\"TARGET \" +e.target.className+' '+e.target.innerHTML+' ');\n if (e.target.className.indexOf('cell ') >= 0){\n element= e.target;\n className = e.target.className;\n\n }else if (e.target.parentElement.className.indexOf('cell ') >= 0){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (element === null) return null;\n else{\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === '&nbsp;') return null; // blank cells\n else return elSymbol;\n }\n}\n\n// EXPORTS\nmodule.exports = ElemenTable;\n\n\n//# sourceURL=webpack:///./src/search-mod/ElemenTable.view.js?");
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is the UI component implementing the interactive Element Table\n in the search front. It need to communicate to other UI components\n so it exposes several listeners\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\n\n\n// constans\n\nconst GROUPS = new Map([\n [1,['H', 'Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n [2,['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n [3,['Sc', 'Y']],\n [4,['Ti', 'Zr', 'Hf', 'Rf']],\n [5,['V', 'Nb', 'Ta', 'Ha']],\n [6,['Cr', 'Mo', 'W', 'Sg']],\n [7,['Mn', 'Tc', 'Re', 'Ns']],\n [8,['Fe', 'Ru', 'Os', 'Hs']],\n [9,['Co', 'Rh', 'Ir', 'Mt']],\n [10,['Ni', 'Pd', 'Pt', 'Ds']],\n [11,['Cu', 'Ag', 'Au', 'Rg']],\n [12,['Zn', 'Cd', 'Hg', 'Cn']],\n [13,['B', 'Al', 'Ga', 'In', 'Tl', 'Nh']],\n [14,['C', 'Si', 'Ge', 'Sn', 'Pb', 'Fl']],\n [15,['N', 'P', 'As', 'Sb', 'Bi', 'Mc']],\n [16,['O', 'S', 'Se', 'Te', 'Po', 'Lv']],\n [17,['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n [18,['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n [19,['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er','Tm','Yb','Lu']],\n [20,['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm','Md','No','Lr']]\n]);\n\nconst BLOCKS = new Map([\n ['metalloids',['B', 'Si', 'Ge', 'As', 'Sb', 'Te', 'Po']],\n ['other-non-metals',['H', 'C', 'N', 'O', 'P', 'S', 'Se']],\n ['halogens',['F', 'Cl', 'Br', 'I', 'At', 'Ts']],\n ['noble-gases',['He', 'Ne', 'Ar', 'Kr', 'Xe','Rn', 'Og']],\n ['alkali-metals',['Li', 'Na', 'K', 'Rb', 'Cs', 'Fr']],\n ['alkaline-earth-metals',['Be', 'Mg', 'Ca', 'Sr', 'Ba', 'Ra']],\n ['lanthanoids',['La','Ce','Pr','Nd','Pm','Sm','Eu','Gd','Tb','Dy','Ho','Er'\n ,'Tm','Yb','Lu']],\n ['actinoids',['Ac','Th','Pa','U','Np','Pu','Am','Cm','Bk','Cf','Es','Fm'\n ,'Md','No','Lr']],\n ['transition-metals', ['Sc', 'Y','Ti', 'Zr', 'Hf', 'Rf','V','Nb','Ta','Ha'\n ,'Cr','Mo','W','Sg','Mn','Tc','Re','Ns','Fe','Ru','Os','Hs','Co','Rh','Ir'\n , 'Mt','Ni','Pd','Pt', 'Ds', 'Cu','Ag','Au', 'Rg', 'Zn','Cd','Hg', 'Cn']],\n ['post-transition-metals', ['Al','Ga', 'In', 'Tl', 'Nh', 'Sn', 'Pb', 'Fl', 'Bi', 'Mc', 'Lv']]\n]);\n\nconst BLOCKS_COLORS = new Map([\n ['metalloids','#F9E298'],\n ['other-non-metals','#F2B01D'],\n ['halogens','#85ADC1'],\n ['noble-gases','#F7D660'],\n ['alkali-metals','#D04629'],\n ['alkaline-earth-metals','#F7B57D'],\n ['transition-metals', '#F58737'],\n ['post-transition-metals', '#AE4747'],\n ['lanthanoids','#3B91AE'],\n ['actinoids','#E97147']\n]);\n\nlet elementNames = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium',\n 'Boron', 'Carbon', 'Nitrogen', 'Oxygen', 'Fluorine',\n 'Neon', 'Sodium', 'Magnesium', 'Aluminum', 'Silicon',\n 'Phosphorus', 'Sulfur', 'Chlorine', 'Argon', 'Potassium',\n 'Calcium', 'Scandium', 'Titanium', 'Vanadium', 'Chromium',\n 'Manganese', 'Iron', 'Cobalt', 'Nickel', 'Copper',\n 'Zinc', 'Gallium', 'Germanium', 'Arsenic', 'Selenium',\n 'Bromine', 'Krypton', 'Rubidium', 'Strontium', 'Yttrium',\n 'Zirconium', 'Niobium', 'Molybdenum', 'Technetium', 'Ruthenium',\n 'Rhodium', 'Palladium', 'Silver', 'Cadmium', 'Indium',\n 'Tin', 'Antimony', 'Tellurium', 'Iodine', 'Xenon',\n 'Cesium', 'Barium', 'Lanthanum', 'Cerium', 'Praseodymium',\n 'Neodymium', 'Promethium', 'Samarium', 'Europium', 'Gadolinium',\n 'Terbium', 'Dysprosium', 'Holmium', 'Erbium', 'Thulium',\n 'Ytterbium', 'Lutetium', 'Hafnium', 'Tantalum', 'Tungsten',\n 'Rhenium', 'Osmium', 'Iridium', 'Platinum', 'Gold',\n 'Mercury', 'Thallium', 'Lead', 'Bismuth', 'Polonium',\n 'Astatine', 'Radon', 'Francium', 'Radium', 'Actinium',\n 'Thorium', 'Protactinium', 'Uranium', 'Neptunium', 'Plutonium',\n 'Americium', 'Curium', 'Berkelium', 'Californium', 'Einsteinium',\n 'Fermium', 'Mendelevium', 'Nobelium', 'Lawrencium', 'Rutherfordium',\n 'Dubnium', 'Seaborgium', 'Bohrium', 'Hassium', 'Meitnerium', 'Darmstadtium',\n 'Roentgenium', 'Copernicium', 'Nihonium', 'Flerovium', 'Moscovium',\n 'Livermorium', 'Tennessine', 'Oganesson'\n];\n\n\n// utility functions\n\nfunction getElementBlock(elSymbol){\n let block;\n BLOCKS.forEach(function(value, key) {\n //console.log(key + \" \" + value);\n if (value.indexOf(elSymbol) >= 0)\n block= key;\n });\n return block;\n}\n\n\nlet CELL_WIDTH= 36;\n\nfunction getCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cell '+getElementBlock(elSymbol)+'\" data-el=\"el-'+elSymbol+'\">'+\n '<b>'+elSymbol+'</b> <div>'+elNum+'</div> </td>';\n}\n\nfunction getOddCellHtml(elNum){\n let elSymbol= util.ELEMENTS[elNum-1];\n return '<td class=\"cellpad '+getElementBlock(elSymbol)+'\" data-el=\"el-X\">'+\n '<b>&nbsp;</b> <div>&nbsp;</div> </td>';\n}\n\n\n/** Group selection not implemented\n\nfunction getGroupSelectorHtml(num){\n return '<th class=\"group-sel\" data-group=\"'+num+'\"><div></div></th>';\n}\n\nfunction getGroupSelectorHtmlLAcAc(num){\n return '<th class=\"group-sel-la-ac\" data-group=\"'+num+'\"><div></div></th>';\n}*/\n\n/************************/\n\n\nclass ElemenTable{\n\n constructor() {\n\n this.element = document.createElement('div');\n this.element.setAttribute('id','elementable');\n\n // header with dropdown\n let tempHtml = '<div class=\"element-info\"></div>';\n\n tempHtml+= '<div class=\"ptWrapper\">';\n\n tempHtml+= '<table id=\"pt-main\">'; // table zone div\n\n /* Deactivated for the moment\n // header with group selectors\n tempHtml+= '<thead><tr>';//'<div id=\"group-selectors\">';\n for (let i= 1; i<=18; i++){\n tempHtml+= getGroupSelectorHtml(i);\n }\n tempHtml+= '</tr></thead>';// selectors\n */\n\n tempHtml+= '<tbody>';\n // row 1\n tempHtml+= '<tr>'+getCellHtml(1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"16\"></td>';\n tempHtml+= getCellHtml(2)+'</tr>';\n\n let get8ElementRowHtml= (initPos) => {\n tempHtml+= '<tr>'+getCellHtml(initPos)+getCellHtml(initPos+1);\n tempHtml+= '<td class=\"cellpad\" colspan=\"10\"></td>';\n for (let i= initPos+2; i< initPos+8; i++) tempHtml+= getCellHtml(i);\n tempHtml+= '</tr>';//div.row\n }\n\n // row 2 and 3\n get8ElementRowHtml(3);\n get8ElementRowHtml(11);\n\n // row 4,5, 6\n let counter= 19;\n for (let i= 0; i<4; i++){\n tempHtml+= '<tr>';\n for (let j= 0; j<18; j++){\n if (counter === 57 || counter === 89){\n tempHtml += getOddCellHtml(counter);\n counter += 15;\n }else{\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n }\n tempHtml+= '</tr>';//div.row\n }\n\n\n tempHtml+= '</tbody></table>';\n\n // Lanthanides and Actinides\n tempHtml+= '<div id=\"specialRows\"><table id=\"pt-laac\">';\n for (let i= 0; i<2; i++){\n tempHtml+= '<tr>';\n counter = (i === 0 ? 57 : 89);\n //tempHtml+= getGroupSelectorHtmlLAcAc((i === 0 ? 19 : 20));\n for (let j= 0; j<15; j++){\n tempHtml+= getCellHtml(counter);\n counter++;\n }\n tempHtml+= '</tr>';//div.row\n }\n tempHtml+= '</table></div>'; //div#specialRows\n\n\n // Block labels\n tempHtml+= `<div class=\"legend\">\n <div class=\"alkali-metals\">Alkali metals</div>\n <div class=\"alkaline-earth-metals\">Alkaline earth metals</div>\n <div class=\"transition-metals\">Transition metals</div>\n <div class=\"post-transition-metals\">Post-transition metals</div>\n <div class=\"metalloids\">Metalloids</div>\n <div class=\"other-non-metals\">Other nonmetals</div>\n <div class=\"halogens\">Halogens</div>\n <div class=\"noble-gases\">Noble gases</div>\n <div class=\"lanthanoids\">Lanthanoids</div>\n <div class=\"actinoids\">Actinoids</div>\n </div>`;//'<div id=\"group-selectors\">';\n\n\n tempHtml += `<div 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 tempHtml+= '</div>'; // ptWrapper\n\n this.element.innerHTML= tempHtml;\n this.elementInfo= this.element.getElementsByClassName('element-info')[0];\n this.tableZone= this.element.getElementsByClassName('ptWrapper')[0];\n //this.specialRows= this.element.querySelector('#specialRows');\n\n this._events();\n }\n\n\n _events() {\n\n // One listener for all diferent clicks (simple element, group)\n var adhocListener= (e) => {\n\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let element = e.target;\n if (className === ''){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (className.indexOf('cellpad') >= 0) return; // structural empty table cells\n\n if (className.indexOf('group-sel') >= 0){ // group selector cells\n //*** Not working at the moment - group selection deactivated\n let elements= GROUPS.get(parseInt(element.getAttribute('data-group')));\n //console.log(\"group-sel \"+elements);\n this.clickListener(elements);//let done =\n //if (done)\n for (var i = 0; i < elements.length; i++)\n this.selectElement(elements[i]);\n\n }else if (className.indexOf('cell') >= 0){ // element cells\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === '&nbsp;') return; // blank cells\n //console.log(\"elSymbol-sel \"+elSymbol);\n if (className.indexOf('el-selected') >= 0){ // If selected\n this.deselectListener(elSymbol);//this.deselectElement(elSymbol);\n }else{ // If not selected\n this.clickListener([elSymbol]);//let done = this.clickListener([elSymbol]);\n this.selectElement(elSymbol);//if (done) this.selectElement(elSymbol);\n }\n }\n }\n //e.stopPropagation();\n };\n\n // Event listener set in the root div element\n this.tableZone.addEventListener('click',adhocListener,true);\n\n this.tableZone.addEventListener('mouseover',e => {\n\n let elSymbol= getElement(e);\n //console.log(\"ENTERIG elSymbol-sel \"+elSymbol);\n if (elSymbol !== null){\n //console.log(\"elSymbol-sel \"+elSymbol);\n this.elementInfo.style.display = 'block';\n let borderColor= BLOCKS_COLORS.get(getElementBlock(elSymbol));\n this.elementInfo.style.borderColor = borderColor;\n let number= util.ELEMENTS.indexOf(elSymbol)+1;\n this.elementInfo.innerHTML= `\n <div>\n <div style=\"float: right; padding: 3px 4px;border-left: 3px solid ${borderColor};\n border-bottom: 3px solid ${borderColor}\" > ${number} </div>\n <div style=\"clear: right;\"></div>\n </div>\n <div class=\"symbol\">${elSymbol} </div>\n <div class=\"\">${elementNames[number-1]} </div>\n `\n }\n });\n\n this.tableZone.addEventListener('mouseout',e => {\n let element= getElement(e);\n if (element !== null) this.elementInfo.style.display = 'none';\n });\n\n }\n\n // Observer pattern\n setClickListener(listener) {\n this.clickListener= listener;\n }\n\n\n setDeselectListener(listener) {\n this.deselectListener= listener;\n }\n\n\n selectElement(elSymbol) {\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell el-selected';\n }\n\n\n deselectElement(elSymbol) {\n //document.getElementById('el-'+elSymbol).className= 'cell '+getElementBlock(elSymbol);\n this.element.querySelector('td[data-el=\"el-'+elSymbol+'\"]')\n .className= 'cell '+getElementBlock(elSymbol);\n }\n\n\n deselectAllElements(){\n let selectedElements = this.element.querySelectorAll('td.el-selected');\n\n //selectedElements.forEach( element => {\n for (let i = 0; i < selectedElements.length; ++i){\n let elSymbol = selectedElements[i].getAttribute('data-el').substring(3);\n selectedElements[i].className= 'cell '+getElementBlock(elSymbol);\n }\n }\n\n} // class ElemenTable\n\n\nfunction getElement(e){\n\n let element = null;\n let className = null;\n //console.log(\"TARGET \" +e.target.className+' '+e.target.innerHTML+' ');\n if (e.target.className.indexOf('cell ') >= 0){\n element= e.target;\n className = e.target.className;\n\n }else if (e.target.parentElement.className.indexOf('cell ') >= 0){\n element= e.target.parentElement;\n className = e.target.parentElement.className;\n }\n\n if (element === null) return null;\n else{\n let html= element.innerHTML;\n let elSymbol= html.substring(3,html.indexOf('<',3));\n if (elSymbol === '&nbsp;') return null; // blank cells\n else return elSymbol;\n }\n}\n\n// EXPORTS\nmodule.exports = ElemenTable;\n\n\n//# sourceURL=webpack:///./src/search-mod/ElemenTable.view.js?");
/***/ }),
......@@ -404,7 +404,7 @@ eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apac
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("\n\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\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 AutocompleteMultiselectTextfield = __webpack_require__(/*! ./AutocompleteMultiselectTextfield.js */ \"./src/search-mod/AutocompleteMultiselectTextfield.js\");\n\n\nconst NOT_SELECTED_OPACITY = 0.2\n\nclass FilterPanel {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'filter-panel-placeholder');\n\n this.fields = [];\n\n let structureGroupBox = this.createPropsGroupBox('Structure')\n this.element.append(structureGroupBox);\n\n const systemTypeField = new CheckboxesField('System type', 'material_type', [\n {value: 'bulk', text: 'Bulk'},\n {value: '2D', text: '2D'}, \n {value: '1D', text: '1D'}\n ]);\n this.fields.push(systemTypeField)\n structureGroupBox.append(systemTypeField.element); /// By looping*****\n\n const crystalSystemField = new CheckboxesField('Crystal system', 'crystal_system', [\n {value: 'cubic', text: 'Cubic'},\n {value: 'hexagonal', text: 'Hexagonal'},\n {value: 'trigonal', text: 'Trigonal'},\n {value: 'tetragonal', text: 'Tetragonal'},\n {value: 'orthorhombic', text: 'Orthorhombic'},\n {value: 'monoclinic', text: 'Monoclinic'},\n {value: 'triclinic', text: 'Triclinic'},\n ]);\n this.fields.push(crystalSystemField)\n structureGroupBox.append(crystalSystemField.element); \n\n\n/* Old version\n const spaceGroupField = new SpaceGroupField();\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \n */\n \n const spaceGroupField = new TextField('Space group number', 'space_group_number');\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \n\n\n const structureTypeField = new AutocompleteField('Structure type', 'structure_type');\n this.fields.push(structureTypeField)\n structureGroupBox.append(structureTypeField.element);\n\n // const structureTypeField = new AutocompleteField('Strukturbericht designation', \"strukturbericht\", 'strukturbericht_designation'));\n\n let propertiesGroupBox = this.createPropsGroupBox('Properties')\n this.element.append(propertiesGroupBox);\n\n\n /* The mass density field is disabled for now\n const massDensityField = new MassDensityField();\n this.fields.push(massDensityField)\n propertiesGroupBox.append(massDensityField.element); \n */\n\n const boolValueFields = new CheckboxesField('Results containing...', undefined, [\n {value: 'Band structure', text: 'Band structure', id: 'has_band_structure'},\n {value: 'DOS', text: 'DOS', id: 'has_dos'}, \n {value: 'Thermal properties', text: 'Thermal properties', id: 'has_thermal_properties'}, \n ]); \n this.fields.push(boolValueFields)\n propertiesGroupBox.append(boolValueFields.element); \n\n // Method \n let methodGroupBox = this.createPropsGroupBox('Method')\n this.element.append(methodGroupBox);\n const basisSetField = new AutocompleteField('Basis set', 'basis_set');\n this.fields.push(basisSetField)\n methodGroupBox.append(basisSetField.element);\n const functionalTypeField = new AutocompleteField('Functional type', 'functional_type');\n this.fields.push(functionalTypeField)\n methodGroupBox.append(functionalTypeField.element);\n const codeField = new AutocompleteField('Code', 'code_name');\n this.fields.push(codeField)\n methodGroupBox.append(codeField.element);\n\n InfoSys.addToInfoSystem(this.element);\n\n // The value change event in fields is handled at container level (event delegation pattern) \n this.element.addEventListener('change', e => {\n if (this.addPropsChangeListener) this.addPropsChangeListener(this.getValues())\n });\n\n/* code for the MaxMinSlider component testing\n this.testSlider = this.element.querySelector('.test-slider');\n console.log(\"TAB: \",this.testSlider);\n this.slider = new MaxMinSlider();\n this.slider.setRange(0,10000);\n this.testSlider.appendChild(this.slider.element);\n*/\n }\n\n\n createPropsGroupBox(title){\n const element = document.createElement('div');\n element.className = 'filter-section-box';\n element.innerHTML = '<div class=\"filter-section-title\">'+title+'</div>';\n \n return element;\n }\n\n\n getValues(){\n\n let filterMap = new Map();\n\n this.fields.forEach( field => {\n const values = field.getValues();\n if (Array.isArray(values)){\n values.forEach( value => filterMap.set(value.fieldId, value.value))\n }else{\n if (values && values.value.length > 0) \n filterMap.set(values.fieldId, values.value)\n }\n });\n //console.log('FilterPanel getValues:', filterMap);\n return filterMap;\n }\n\n\n setPropsChangeListener(listener) {\n this.addPropsChangeListener = listener;\n }\n\n\n showSelectedProps(show){\n\n this.fields.forEach( field => {\n field.highlightSelected(show)\n })\n /*\n let selectedPropBox = this.element.querySelector('.selected-props-view')\n let values = this.getValues()\n let html = ''\n values.forEach( (value, key) => {\n html += ` <h3>${key}</h3> <p>${value}</p>\n `\n } )\n selectedPropBox.innerHTML = html\n selectedPropBox.style.display = (show ? '' : 'none') \n this.element.querySelector('.regular-view').style.display = (show ? 'none' : '') \n */\n }\n\n\n}\n\n\nclass CheckboxesField{\n\n constructor(name, commonId, valuesDesc ){\n\n this.commonId = commonId;\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `<div class=\"field-title\">\n <span info-sys-data=\"${commonId}\">${name}</span>\n </div>`;\n\n valuesDesc.forEach( valueDesc => {\n let fieldId = commonId ? commonId : valueDesc.id; // commonId -> checkboxes related: only one field / no commonId -> every checkbox is and independent field\n let infoSysValue = commonId ? (fieldId+'.value:'+valueDesc.value) : fieldId;\n let valueElement = document.createElement('div');\n valueElement.innerHTML = `\n <input type=\"checkbox\" class=\"${fieldId}-field\" value=\"${valueDesc.value}\">\n <span info-sys-data=\"${infoSysValue}\">${valueDesc.text}</span>\n `;\n this.element.append(valueElement);\n });\n\n this.checkboxes = this.element.querySelectorAll('input');\n }\n\n getValues(){\n\n if (this.commonId){\n let values = [];\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked) values.push(checkbox.value);\n });\n return {fieldId: this.commonId, value: values};\n }else{\n let values = [];\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked) {\n let s = checkbox.className;\n values.push({fieldId: s.substring(0, s.indexOf('-field')), value: [true]});\n }\n });\n return values;\n }\n }\n\n highlightSelected(bool){\n\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked){\n //checkbox.parentElement.style.backgroundColor = 'red'\n checkbox.parentElement.style.opacity = ''//(bool ? '' : '')\n }else{\n checkbox.parentElement.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n } \n });\n }\n\n}\n\n\n\nclass TextField{\n\n constructor(name, id){\n this.fieldId = id;\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\"> <span info-sys-data=\"${id}\">${name}</span></div>\n <input type=\"text\" class=\"${id}-textfield\" style=\"\">\n `;\n this.input = this.element.querySelector('input');\n }\n\n\n getValues(){\n let value = this.input.value;\n return ( value.trim() === '' ? null : { fieldId: this.fieldId, value: [value] } );\n }\n\n\n highlightSelected(bool){\n if (this.getValues() === null)\n this.input.style.opacity = (bool ? NOT_SELECTED_OPACITY : '');\n }\n}\n\n\n/* Not being used temporarily -> simple textfield instead \nclass SpaceGroupField{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\"> <span info-sys-data=\"space-group\">Space group</span></div>\n <select id=\"space-group-dropdown-list\" disabled >\n <option value=\"by-number\">by number</option>\n <option value=\"by-symbol\">by short symbol</option>\n </select>\n <input type=\"text\" class=\"space-group-textfield\" style=\"width: 60px\">\n `;\n this.select = this.element.querySelector('select');\n this.input = this.element.querySelector('input')\n }\n\n\n getValues(){\n \n let type = this.select.options[this.select.selectedIndex].value;\n let propName = (type === 'by-number' ? 'space_group_number' \n : 'space_group_international_short_symbol');\n\n let value = this.input.value;\n return ( value.trim() === '' ? null : { fieldId: propName, value: [value] } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null){\n this.select.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n this.input.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n \n }\n}*/\n\n\nclass AutocompleteField{\n\n constructor(name, id){\n this.fieldId = id\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `<div class=\"field-title\">\n <span info-sys-data=\"${id}\">${name}</span>\n </div>`;\n\n\n this.autocomplete = new AutocompleteMultiselectTextfield(id, 'Search and select options');\n this.element.append(this.autocomplete.element)\n \n let r1 = util.serverReq(util.getSuggestionURL(this.fieldId), (e) => {\n let names = JSON.parse(r1.response)[this.fieldId];\n this.autocomplete.setAutocompleteList(names);\n });\n \n }\n\n getValues(){\n const values = this.autocomplete.getValues();//getSelected();\n return ( values.length === 0 ? null : { fieldId: this.fieldId, value: values } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null) \n this.autocomplete.element.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n}\n\n\n/* Not being used \nclass MassDensityField{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\">\n <span info-sys-data=\"mass-density\">Mass density</span> \n <span style=\"font-weight: normal;\">(kg/m<sup>3</sup>)</span>\n </div>\n <div>\n Min: <input type=\"text\" class=\"mass-density-min-field\">&nbsp;&nbsp;\n Max: <input type=\"text\" class=\"mass-density-max-field\">\n </div>\n `;\n this.inputs = this.element.querySelectorAll('input');\n }\n\n getValues(){\n \n let value = ':';\n if (this.inputs[0].value.trim() !== ''){ // Min value\n value = this.inputs[0].value+value;\n }\n if (this.inputs[1].value.trim() !== ''){ // Max value\n value = value+this.inputs[1].value;\n }\n return ( value === ':' ? null : { fieldId: 'mass-density', value: value } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null) \n this.inputs[0].parentElement.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n} */\n\n// EXPORTS\nmodule.exports = FilterPanel;\n\n\n\n/* To be implemented in the future\n\nclass MaxMinSlider{\n\n constructor(){\n\n this.element = document.createElement('div');\n this.element.innerHTML = `\n <svg class=\"maxminslider\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100px\" height=\"40px\"\n viewBox=\"0 0 100 40\" >\n <line class=\"slider-bar\" x1=\"10\" x2=\"90\" y1=\"30\" y2=\"30\" stroke=\"blue\"\n stroke-width=\"4\"/>\n <circle class=\"min-btn\" cx=\"10\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"min-text maxminslider-text\" x=\"10\" y=\"10\" text-anchor=\"start\"></text>\n <circle class=\"max-btn\" cx=\"90\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"max-text maxminslider-text\" x=\"90\" y=\"10\" text-anchor=\"end\"></text>\n </svg>\n `;\n\n //this.bar = this.element.querySelector('.slider-bar');\n this.svg = this.element.querySelector('svg');\n this.minButton = this.element.querySelector('.min-btn');\n this.minText = this.element.querySelector('.min-text');\n this.maxButton = this.element.querySelector('.max-btn');\n this.maxText = this.element.querySelector('.max-text');\n\n this.BUTTON_R = 6;\n\n this.minButtonDown = false;\n this.minButtonInitX = null;\n\n this.MIN_BUTTON_INIT_X = 10;\n this.minX = 0;\n\n this.maxButtonDown = false;\n this.maxButtonInitX = null;\n this.MAX_VALUE = 80;//this.MAX_BUTTON_INIT_X = 90;\n this.maxX = this.MAX_VALUE;\n\n console.log('minButton', this.minButton.getBoundingClientRect());\n\n this._events();\n }\n\n\n _events() {\n\n this.minButton.addEventListener( \"mousedown\", e => this.minButtonDown = true );\n this.minButton.addEventListener( \"mouseup\", e => this.minButtonDown = false );\n this.minButton.addEventListener( \"mouseleave\", e => this.minButtonDown = false );\n\n this.minButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n if (this.minButtonInitX === null){\n //this.minButtonInitX = this.svg.getBoundingClientRect().left;\n this.minButtonInitX = this.minButton.getBoundingClientRect().left+this.BUTTON_R;//\n //console.log('left', this.minButtonInitX);\n }\n\n if (this.minButtonDown){\n this.minX = e.clientX-this.minButtonInitX ;\n if (this.minX > 0 && this.minX < this.maxX-this.BUTTON_R){\n this.minButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.minX);\n this.minText.textContent = this.minX*this.factor-250;\n }\n\n }\n });\n\n\n this.maxButton.addEventListener( \"mousedown\", e => this.maxButtonDown = true );\n this.maxButton.addEventListener( \"mouseup\", e => this.maxButtonDown = false );\n this.maxButton.addEventListener( \"mouseleave\", e => this.maxButtonDown = false );\n\n this.maxButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n\n if (this.maxButtonInitX === null)\n this.maxButtonInitX = this.maxButton.getBoundingClientRect().left+this.BUTTON_R;//\n\n if (this.maxButtonDown){\n\n this.maxX = e.clientX - this.minButtonInitX;\n //console.log('maxButton', e.clientX, this.maxButtonInitX, this.maxX);\n if (this.maxX < this.MAX_VALUE && this.minX+this.BUTTON_R < this.maxX){\n this.maxButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.maxX);\n this.maxText.textContent = this.maxX*this.factor;\n }\n\n }\n\n });\n\n\n }\n\n\n setRange(min, max){\n this.factor = (max-min)/80;\n }\n\n}\n\n*/\n\n\n//# sourceURL=webpack:///./src/search-mod/FilterPanel.view.js?");
eval("\n\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\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 AutocompleteMultiselectTextfield = __webpack_require__(/*! ./AutocompleteMultiselectTextfield.js */ \"./src/search-mod/AutocompleteMultiselectTextfield.js\");\n\n\nconst NOT_SELECTED_OPACITY = 0.2\n\nclass FilterPanel {\n\n constructor() {\n this.element = document.createElement('div');\n this.element.setAttribute(\"id\",'filter-panel-placeholder');\n\n this.fields = [];\n\n let structureGroupBox = this.createPropsGroupBox('Structure')\n this.element.append(structureGroupBox);\n\n const systemTypeField = new CheckboxesField('System type', 'material_type', [\n {value: 'bulk', text: 'Bulk'},\n {value: '2D', text: '2D'}, \n {value: '1D', text: '1D'}\n ]);\n this.fields.push(systemTypeField)\n structureGroupBox.append(systemTypeField.element); /// By looping*****\n\n const crystalSystemField = new CheckboxesField('Crystal system', 'crystal_system', [\n {value: 'cubic', text: 'Cubic'},\n {value: 'hexagonal', text: 'Hexagonal'},\n {value: 'trigonal', text: 'Trigonal'},\n {value: 'tetragonal', text: 'Tetragonal'},\n {value: 'orthorhombic', text: 'Orthorhombic'},\n {value: 'monoclinic', text: 'Monoclinic'},\n {value: 'triclinic', text: 'Triclinic'},\n ]);\n this.fields.push(crystalSystemField)\n structureGroupBox.append(crystalSystemField.element); \n\n\n/* Old version\n const spaceGroupField = new SpaceGroupField();\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \n */\n \n const spaceGroupField = new TextField('Space group number', 'space_group_number');\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \n\n\n const structureTypeField = new AutocompleteField('Structure type', 'structure_type');\n this.fields.push(structureTypeField)\n structureGroupBox.append(structureTypeField.element);\n\n // const structureTypeField = new AutocompleteField('Strukturbericht designation', \"strukturbericht\", 'strukturbericht_designation'));\n\n let propertiesGroupBox = this.createPropsGroupBox('Properties')\n this.element.append(propertiesGroupBox);\n\n\n /* The mass density field is disabled for now\n const massDensityField = new MassDensityField();\n this.fields.push(massDensityField)\n propertiesGroupBox.append(massDensityField.element); \n */\n\n const boolValueFields = new CheckboxesField('Results containing...', undefined, [\n {value: 'Band structure', text: 'Band structure', id: 'has_band_structure'},\n {value: 'DOS', text: 'DOS', id: 'has_dos'}, \n {value: 'Thermal properties', text: 'Thermal properties', id: 'has_thermal_properties'}, \n ]); \n this.fields.push(boolValueFields)\n propertiesGroupBox.append(boolValueFields.element); \n\n // Method \n let methodGroupBox = this.createPropsGroupBox('Method')\n this.element.append(methodGroupBox);\n const basisSetField = new AutocompleteField('Basis set', 'basis_set');\n this.fields.push(basisSetField)\n methodGroupBox.append(basisSetField.element);\n const functionalTypeField = new AutocompleteField('Functional type', 'functional_type');\n this.fields.push(functionalTypeField)\n methodGroupBox.append(functionalTypeField.element);\n const codeField = new AutocompleteField('Code', 'code_name');\n this.fields.push(codeField);\n methodGroupBox.append(codeField.element);\n\n // Restricted search checkbox: cannot be set as innerHTML\n const container = document.createElement('div');\n container.className = \"filter-quantity-box\";\n const a = document.createElement('div');\n a.className = \"perm-tooltip search-option restricted-search-option\";\n const b = document.createElement('input');\n b.id = \"restricted-search\";\n b.name = \"restricted-search\";\n b.type = \"checkbox\";\n const c = document.createElement('label');\n c[\"for\"] = \"restricted-search\";\n c.className = \"perm-tooltip\";\n c.textContent = \"Restrict to individual calculations\";\n const d = document.createElement('span');\n d.className = \"tooltiptext\";\n d.textContent = \"If selected, the query will return materials that have individual calculations simultaneously matching your methodology and properties criteria.\"\n a.append(b);\n a.append(c);\n a.append(d);\n container.append(a);\n this.element.append(container);\n\n InfoSys.addToInfoSystem(this.element);\n\n // The value change event in fields is handled at container level (event delegation pattern) \n this.element.addEventListener('change', e => {\n if (this.addPropsChangeListener) this.addPropsChangeListener(this.getValues())\n });\n\n/* code for the MaxMinSlider component testing\n this.testSlider = this.element.querySelector('.test-slider');\n console.log(\"TAB: \",this.testSlider);\n this.slider = new MaxMinSlider();\n this.slider.setRange(0,10000);\n this.testSlider.appendChild(this.slider.element);\n*/\n }\n\n\n createPropsGroupBox(title){\n const element = document.createElement('div');\n element.className = 'filter-section-box';\n element.innerHTML = '<div class=\"filter-section-title\"><div>'+title+'</div></div>';\n return element;\n }\n\n\n getValues(){\n\n let filterMap = new Map();\n\n this.fields.forEach( field => {\n const values = field.getValues();\n console.log(\"===============\")\n console.log(values)\n if (Array.isArray(values) && values.length > 0){\n values.forEach( value => filterMap.set(value.fieldId, value.value))\n } else if (values && values.value && values.value.length > 0) {\n filterMap.set(values.fieldId, values.value);\n }\n });\n console.log(\"FILTERMAP\")\n console.log(filterMap)\n return filterMap;\n }\n\n\n setPropsChangeListener(listener) {\n this.addPropsChangeListener = listener;\n }\n\n\n showSelectedProps(show){\n\n this.fields.forEach( field => {\n field.highlightSelected(show)\n })\n /*\n let selectedPropBox = this.element.querySelector('.selected-props-view')\n let values = this.getValues()\n let html = ''\n values.forEach( (value, key) => {\n html += ` <h3>${key}</h3> <p>${value}</p>\n `\n } )\n selectedPropBox.innerHTML = html\n selectedPropBox.style.display = (show ? '' : 'none') \n this.element.querySelector('.regular-view').style.display = (show ? 'none' : '') \n */\n }\n\n\n}\n\n\nclass CheckboxesField{\n\n constructor(name, commonId, valuesDesc ){\n\n this.commonId = commonId;\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `<div class=\"field-title\">\n <span info-sys-data=\"${commonId}\">${name}</span>\n </div>`;\n\n valuesDesc.forEach( valueDesc => {\n let fieldId = commonId ? commonId : valueDesc.id; // commonId -> checkboxes related: only one field / no commonId -> every checkbox is and independent field\n let infoSysValue = commonId ? (fieldId+'.value:'+valueDesc.value) : fieldId;\n let valueElement = document.createElement('div');\n valueElement.innerHTML = `\n <input type=\"checkbox\" class=\"${fieldId}-field\" value=\"${valueDesc.value}\">\n <span info-sys-data=\"${infoSysValue}\">${valueDesc.text}</span>\n `;\n this.element.append(valueElement);\n });\n\n this.checkboxes = this.element.querySelectorAll('input');\n }\n\n getValues(){\n\n if (this.commonId){\n let values = [];\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked) values.push(checkbox.value);\n });\n return {fieldId: this.commonId, value: values};\n }else{\n let values = [];\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked) {\n let s = checkbox.className;\n values.push({fieldId: s.substring(0, s.indexOf('-field')), value: [true]});\n }\n });\n return values;\n }\n }\n\n highlightSelected(bool){\n\n this.checkboxes.forEach( checkbox => {\n if (checkbox.checked){\n //checkbox.parentElement.style.backgroundColor = 'red'\n checkbox.parentElement.style.opacity = ''//(bool ? '' : '')\n }else{\n checkbox.parentElement.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n } \n });\n }\n\n}\n\n\n\nclass TextField{\n\n constructor(name, id){\n this.fieldId = id;\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\"> <span info-sys-data=\"${id}\">${name}</span></div>\n <input type=\"text\" class=\"${id}-textfield\" style=\"\">\n `;\n this.input = this.element.querySelector('input');\n }\n\n\n getValues(){\n let value = this.input.value;\n return ( value.trim() === '' ? null : { fieldId: this.fieldId, value: [value] } );\n }\n\n\n highlightSelected(bool){\n if (this.getValues() === null)\n this.input.style.opacity = (bool ? NOT_SELECTED_OPACITY : '');\n }\n}\n\n\n/* Not being used temporarily -> simple textfield instead \nclass SpaceGroupField{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\"> <span info-sys-data=\"space-group\">Space group</span></div>\n <select id=\"space-group-dropdown-list\" disabled >\n <option value=\"by-number\">by number</option>\n <option value=\"by-symbol\">by short symbol</option>\n </select>\n <input type=\"text\" class=\"space-group-textfield\" style=\"width: 60px\">\n `;\n this.select = this.element.querySelector('select');\n this.input = this.element.querySelector('input')\n }\n\n\n getValues(){\n \n let type = this.select.options[this.select.selectedIndex].value;\n let propName = (type === 'by-number' ? 'space_group_number' \n : 'space_group_international_short_symbol');\n\n let value = this.input.value;\n return ( value.trim() === '' ? null : { fieldId: propName, value: [value] } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null){\n this.select.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n this.input.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n \n }\n}*/\n\n\nclass AutocompleteField{\n\n constructor(name, id){\n this.fieldId = id\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `<div class=\"field-title\">\n <span info-sys-data=\"${id}\">${name}</span>\n </div>`;\n\n\n this.autocomplete = new AutocompleteMultiselectTextfield(id, 'Search and select options');\n this.element.append(this.autocomplete.element)\n \n let r1 = util.serverReq(util.getSuggestionURL(this.fieldId), (e) => {\n let names = JSON.parse(r1.response)[this.fieldId];\n this.autocomplete.setAutocompleteList(names);\n });\n \n }\n\n getValues(){\n const values = this.autocomplete.getValues();//getSelected();\n return ( values.length === 0 ? null : { fieldId: this.fieldId, value: values } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null) \n this.autocomplete.element.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n}\n\n\n/* Not being used \nclass MassDensityField{\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'filter-quantity-box';\n this.element.innerHTML = `\n <div class=\"field-title\">\n <span info-sys-data=\"mass-density\">Mass density</span> \n <span style=\"font-weight: normal;\">(kg/m<sup>3</sup>)</span>\n </div>\n <div>\n Min: <input type=\"text\" class=\"mass-density-min-field\">&nbsp;&nbsp;\n Max: <input type=\"text\" class=\"mass-density-max-field\">\n </div>\n `;\n this.inputs = this.element.querySelectorAll('input');\n }\n\n getValues(){\n \n let value = ':';\n if (this.inputs[0].value.trim() !== ''){ // Min value\n value = this.inputs[0].value+value;\n }\n if (this.inputs[1].value.trim() !== ''){ // Max value\n value = value+this.inputs[1].value;\n }\n return ( value === ':' ? null : { fieldId: 'mass-density', value: value } );\n }\n\n highlightSelected(bool){\n if (this.getValues() === null) \n this.inputs[0].parentElement.style.opacity = (bool ? NOT_SELECTED_OPACITY : '')\n }\n} */\n\n// EXPORTS\nmodule.exports = FilterPanel;\n\n\n\n/* To be implemented in the future\n\nclass MaxMinSlider{\n\n constructor(){\n\n this.element = document.createElement('div');\n this.element.innerHTML = `\n <svg class=\"maxminslider\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100px\" height=\"40px\"\n viewBox=\"0 0 100 40\" >\n <line class=\"slider-bar\" x1=\"10\" x2=\"90\" y1=\"30\" y2=\"30\" stroke=\"blue\"\n stroke-width=\"4\"/>\n <circle class=\"min-btn\" cx=\"10\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"min-text maxminslider-text\" x=\"10\" y=\"10\" text-anchor=\"start\"></text>\n <circle class=\"max-btn\" cx=\"90\" cy=\"30\" r=\"6\" fill=\"black\"/>\n <text class=\"max-text maxminslider-text\" x=\"90\" y=\"10\" text-anchor=\"end\"></text>\n </svg>\n `;\n\n //this.bar = this.element.querySelector('.slider-bar');\n this.svg = this.element.querySelector('svg');\n this.minButton = this.element.querySelector('.min-btn');\n this.minText = this.element.querySelector('.min-text');\n this.maxButton = this.element.querySelector('.max-btn');\n this.maxText = this.element.querySelector('.max-text');\n\n this.BUTTON_R = 6;\n\n this.minButtonDown = false;\n this.minButtonInitX = null;\n\n this.MIN_BUTTON_INIT_X = 10;\n this.minX = 0;\n\n this.maxButtonDown = false;\n this.maxButtonInitX = null;\n this.MAX_VALUE = 80;//this.MAX_BUTTON_INIT_X = 90;\n this.maxX = this.MAX_VALUE;\n\n console.log('minButton', this.minButton.getBoundingClientRect());\n\n this._events();\n }\n\n\n _events() {\n\n this.minButton.addEventListener( \"mousedown\", e => this.minButtonDown = true );\n this.minButton.addEventListener( \"mouseup\", e => this.minButtonDown = false );\n this.minButton.addEventListener( \"mouseleave\", e => this.minButtonDown = false );\n\n this.minButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n if (this.minButtonInitX === null){\n //this.minButtonInitX = this.svg.getBoundingClientRect().left;\n this.minButtonInitX = this.minButton.getBoundingClientRect().left+this.BUTTON_R;//\n //console.log('left', this.minButtonInitX);\n }\n\n if (this.minButtonDown){\n this.minX = e.clientX-this.minButtonInitX ;\n if (this.minX > 0 && this.minX < this.maxX-this.BUTTON_R){\n this.minButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.minX);\n this.minText.textContent = this.minX*this.factor-250;\n }\n\n }\n });\n\n\n this.maxButton.addEventListener( \"mousedown\", e => this.maxButtonDown = true );\n this.maxButton.addEventListener( \"mouseup\", e => this.maxButtonDown = false );\n this.maxButton.addEventListener( \"mouseleave\", e => this.maxButtonDown = false );\n\n this.maxButton.addEventListener( \"mousemove\", e => {\n //e.preventDefault();\n\n if (this.maxButtonInitX === null)\n this.maxButtonInitX = this.maxButton.getBoundingClientRect().left+this.BUTTON_R;//\n\n if (this.maxButtonDown){\n\n this.maxX = e.clientX - this.minButtonInitX;\n //console.log('maxButton', e.clientX, this.maxButtonInitX, this.maxX);\n if (this.maxX < this.MAX_VALUE && this.minX+this.BUTTON_R < this.maxX){\n this.maxButton.setAttribute('cx', this.MIN_BUTTON_INIT_X + this.maxX);\n this.maxText.textContent = this.maxX*this.factor;\n }\n\n }\n\n });\n\n\n }\n\n\n setRange(min, max){\n this.factor = (max-min)/80;\n }\n\n}\n\n*/\n\n\n//# sourceURL=webpack:///./src/search-mod/FilterPanel.view.js?");
/***/ }),
......@@ -425,7 +425,7 @@ eval("let util = __webpack_require__(/*! ../common/util.js */ \"./src/common/uti
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component 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 \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\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\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 // 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 .catch(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\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 \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\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 reqJson.search_by.restricted = '0';\n console.log('SEARCHING: ', reqJson );\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 // 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 .catch(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\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?");
/***/ }),
......@@ -447,7 +447,7 @@ eval("/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed unde
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"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=\"search-box-placeholder\" > </div>\n <div style=\"display: flex\">\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 class=\"perm-tooltip search-option\">\n <input id=\"restricted-search\" name=\"restricted-search\" type=\"checkbox\" >\n <label for=\"restricted-search\" class=\"perm-tooltip\">Restrict to individual calculations</label>\n <span class=\"tooltiptext\">If selected, the query will return materials that have individual calculations matching all your search criteria.</span>\n </div>\n </div>\n <div 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 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 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.restrictedCheckbox = this.element.querySelector('#restricted-search');\n this.restrictedCheckbox.addEventListener( 'change', e => {\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');\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._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 searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(this.allowOtherElementsCheckbox.checked);\n const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());\n const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');\n //console.log(searchBoxOptimadeQuery)\n //console.log(sep)\n //console.log(propsOptimadeQuery)\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 <input type=\"text\" placeholder=\"Add formula to the search query above\">\n <button class=\"adding-formula-btn\" disabled> Add to query </button>\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 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 allowOtherElements = document.getElementById('allow-other-elements').checked;\n console.log(allowOtherElements)\n const searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(allowOtherElements);\n const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());\n const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');\n //console.log(searchBoxOptimadeQuery)\n //console.log(sep)\n //console.log(propsOptimadeQuery)\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?");
/***/ }),
......@@ -468,7 +468,7 @@ eval("let Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/F
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("/**\n * Copyright 2016-2020 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is the UI component implementing the search box.\n It represents the search query for the user and enables its edition.\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet OptimadeTranslator = __webpack_require__(/*! ./OptimadeTranslator.js */ \"./src/search-mod/OptimadeTranslator.js\");\nlet Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n//\n//\n//// local utility functions\nfunction getTagHtml(tag, isFormula){\n return `<span class=\"search-label\" data-el=\"${tag}\" >\n <img src=\"img/tag.svg\" height=\"16px\" class=\"remove-label\"\n style=\"vertical-align: bottom\"/>\n ${isFormula ? util.getSubscriptedFormula(tag) : tag}\n <img src=\"img/cross.svg\" height=\"6px\" class=\"remove-label\"\n style=\"vertical-align: middle; padding: 4px 3px 6px 5px;\" />\n </span>`;\n}\n\n\nclass SearchBox{\n\n constructor() {\n\n //this.searchQuery = [];\n // this.queryTypes = []; //**** Types associated to query elements\n // Types: element (E), formula (F), symbol (S) and prop names\n // \n // \n this.currentConnector;\n this.notItem; // It signals next item is preceding for a NOT\n this.inSubquery; // the current query pointer is in a subquery, paretheses open \n this.rootQuery; // Root query representation. Root item object (type=\"query\")\n this._resetState();\n /* Two type of object representing the expresions:\n - item (node): {type: 'element'|'formula'|'query', value: \"ElementName\"|\"Formula\"| Another (sub)query structure, not: true|false }\n - boolean connector: { type: 'connector', value: 'AND'|'OR'}\n\n */\n\n this.element = document.createElement('div');\n this.element.className = 'search-box';\n \n this.element.innerHTML = ` \n <div class=\"search-query-wrapper\" >\n <div id=\"syntax-error\">\n <div>Invalid query syntax</div>\n </div>\n <div class=\"search-query-box\" style=\"float: left;\"></div>\n <button class=\"clean-btn\" style=\"float: right;\">Clear all</button>\n </div>\n\n <!-- this button should be out of the search box because its functionality\n is not part of the search box, it's general for the search module\n and it's probably being removed eventually --> \n <button class=\"search-btn\" >Search</button>\n `;\n\n this.cleanButton = this.element.querySelector('.clean-btn');\n this.cleanButton.addEventListener( \"click\", (e) => {\n // init search query\n this._resetState();\n this.renderQuery();\n this.cleanSearchQueryListener();\n });\n\n this.searchQueryBox = this.element.querySelector('.search-query-box');\n this.searchQueryBox.addEventListener( \"click\", e => {\n let className = e.target.className;\n if (className === 'remove-label'){\n let elSymbol = e.target.parentElement.getAttribute('data-el');\n this.removeElementORFormulaInSearchQuery(elSymbol);\n }\n });\n \n }\n\n _resetState(){\n this.notItem = false;\n this.inSubquery = false;\n this.rootQuery = { type: 'query', value: [], not: false }; // no prop open indicates that is the first\n }\n \n getOldQueryFormat(){\n const oldFormat = [];\n this.rootQuery.value.forEach( item => {\n oldFormat.push(...getItemInOldFormat(item));\n })\n\n const searchQuery = [];\n const queryTypes = [];\n oldFormat.forEach( item => {\n searchQuery.push(item.split(':')[0]);\n queryTypes.push(item.split(':')[1]);\n })\n console.log('OLD F:', oldFormat, searchQuery, queryTypes);\n\n return [searchQuery, queryTypes]\n\n function getItemInOldFormat(item){\n const oldFormat = [];\n if (item.not){\n oldFormat.push('NOT:S');\n }\n if (item.type === 'query'){\n oldFormat.push('(:S');\n item.value.forEach( i => {\n oldFormat.push(...getItemInOldFormat(i));\n })\n oldFormat.push('):S');\n }else{ // types: element, formula, material and connector\n let itemValue = item.value+':';\n if (item.type === 'element') itemValue += 'E';\n else if (item.type === 'formula') itemValue += 'F';\n else if (item.type === 'material') itemValue += 'MN';\n else itemValue += 'S'; // connector\n oldFormat.push(itemValue);\n }\n return oldFormat;\n }\n\n }\n\n getOptimadeQuery(allowOtherElements) {\n // Empty query\n if (this.rootQuery.value.length === 0) {\n return '';\n }\n\n // Translate element/formula queries into OptimadeSyntax\n const translator = new OptimadeTranslator();\n const [searchQuery, queryTypes] = this.getOldQueryFormat();\n return translator.translate(searchQuery, queryTypes, allowOtherElements);\n }\n\n setBoolOperator(operator){\n this.currentConnector = operator;\n }\n\n renderQuery() {\n document.querySelector('#syntax-error').style.visibility = 'hidden';\n this.searchQueryBox.innerHTML = getQueryStructHTML(this.rootQuery);\n \n if (this.searchQueryChangeListener) this.searchQueryChangeListener();\n\n function getQueryStructHTML(queryObj){\n\n if (queryObj.type === 'connector')\n return getSymbolHTML(queryObj.value);\n\n else if (!queryObj.type && queryObj.not) // NOT item wrapper, the item is not defined yet\n return getSymbolHTML(' NOT');\n\n else if (queryObj.type === 'element' || queryObj.type === 'formula' || queryObj.type === 'material') // element and formula\n return (queryObj.not ? getSymbolHTML(' NOT') : '')+\n getTagHtml(queryObj.value, (queryObj.type === 'formula' ? true : false));\n else{ // the object represents a query\n let html= '';\n queryObj.value.forEach( subqueryObj => {\n html += getQueryStructHTML(subqueryObj);\n });\n //console.log('getQueryStructHTML', queryObj, queryObj.value[queryObj.length-1])\n const notHTML = (queryObj.not ? getSymbolHTML(' NOT') : '')\n const isOpen = queryObj.open;\n return notHTML+(isOpen === undefined ? html : \n getSymbolHTML(' (')+html+( isOpen ? '' : getSymbolHTML(') ')));\n } \n }\n\n function getSymbolHTML(s){\n return `<span class=\"search-query-symbol\">${s}</span>`;\n }\n }\n\n\n // Add an item (with a preceding connector if needed) to the query \n // The item could be an NOT wrapper for a tag or subquery\n _addItem(item){ // It can be a tag item or a NOT (wrapping an item, added later) \n const queryContent = this.rootQuery.value;\n const tempQueryContent = this.inSubquery ?\n queryContent[queryContent.length-1].value : queryContent;\n\n // If the item is not at the beginning of the query (or subquery) and\n if ( tempQueryContent.length > 0 && \n // the last item is not a connector (AND|OR)\n tempQueryContent[tempQueryContent.length-1].type !== 'connector'){ \n // Add a connector (before the item)\n tempQueryContent.push( {type: 'connector', value: this.currentConnector} );\n }\n\n if (item === 'NOT') // Add an item wrapper with the NOT prop to true \n tempQueryContent.push({ not: true }); // type and value props are undefined\n else if (item === 'SUBQUERY')\n queryContent.push({ type: 'query', value: [], open: true, not: false}); \n else{ // Regular tags: element|formula|material\n const [type, tag] = item.split(':');\n tempQueryContent.push({type: type, value: tag, not: false});\n }\n console.log('ROOT QUERY ', this.rootQuery)\n this.renderQuery();\n }\n\n\n // Public method - type = element|formula|material \n addTag(tag, type){ // add an element, formula or material\n\n if (this.notItem){ // The tag to add is preceded for a NOT, the item object is already created\n const queryContent = this.rootQuery.value;\n const tempQueryContent = this.inSubquery ?\n queryContent[queryContent.length-1].value : queryContent;\n tempQueryContent[tempQueryContent.length-1].type = type;\n tempQueryContent[tempQueryContent.length-1].value = tag;\n this.notItem = false;\n this.renderQuery();\n }else\n this._addItem(type+':'+tag);\n }\n\n\n addElements(elementArray){\n let index = elementArray.length;\n while (index--) {\n this.addTag(elementArray[index], 'element');\n }\n return true;\n }\n\n\n addParentheses(isOpen){\n const queryContent = this.rootQuery.value; // ** Not checking if the it's inside a subquery\n \n if (isOpen){\n if (this.notItem){\n queryContent[queryContent.length-1].type = 'query';\n queryContent[queryContent.length-1].value = [];\n this.notItem = false;\n }else\n this._addItem('SUBQUERY');\n }\n queryContent[queryContent.length-1].open = isOpen;\n this.renderQuery();\n this.inSubquery = isOpen;\n }\n\n\n addNOT(){\n this._addItem('NOT');\n this.notItem = true; // The next item will be in a not expression\n }\n\n\n removeElementORFormulaInSearchQuery(item){\n // spot the item and remove the item and the bool operator(s) related\n removeItem(this.rootQuery, item);\n \n function removeItem(queryObj, item){\n const queryContent = queryObj.value;\n for (let i = 0; i < queryContent.length; i++) {\n if (queryContent[i].value === item) {\n if (queryContent[i+1] && queryContent[i+1].type === 'connector'){\n queryContent.splice(i, 2);\n }else\n queryContent.splice(i, 1);\n break;\n\n }else if (queryContent[i].type === 'query'){\n removeItem(queryContent[i], item);\n if (queryContent[i].value.length == 0){\n if (queryContent[i+1] && queryContent[i+1].type === 'connector'){\n queryContent.splice(i, 2);\n }else\n queryContent.splice(i, 1);\n }\n }\n }\n }\n\n this.renderQuery();\n\n console.log('ROOT QUERY REMOVING ', this.rootQuery)\n\n if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)\n this.removeElementListener(item);\n }\n\n if (this.rootQuery.value.length === 0){ // The search query gets blank\n this.cleanSearchQueryListener();\n }\n\n return true;\n }\n\n \n setCleanSearchQueryListener(listener){\n this.cleanSearchQueryListener = listener;\n }\n\n\n setRemoveElementListener(listener) {\n this.removeElementListener = listener;\n }\n\n setSearchQueryChangeListener(listener) {\n this.searchQueryChangeListener = listener;\n }\n\n getSearchButtonElement(){\n return this.element.querySelector('.search-btn');\n }\n}\n\n// EXPORTS\nmodule.exports = SearchBox;\n\n\n//# sourceURL=webpack:///./src/search-mod/SearchBox.view.js?");
eval("/**\n * Copyright 2016-2020 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This is the UI component implementing the search box.\n It represents the search query for the user and enables its edition.\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet OptimadeTranslator = __webpack_require__(/*! ./OptimadeTranslator.js */ \"./src/search-mod/OptimadeTranslator.js\");\nlet Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n//\n//\n//// local utility functions\nfunction getTagHtml(tag, isFormula){\n return `<span class=\"search-label\" data-el=\"${tag}\" >\n ${isFormula ? util.getSubscriptedFormula(tag) : tag}\n <img src=\"img/cross.svg\" height=\"6px\" class=\"remove-label\"\n style=\"vertical-align: middle; padding: 4px 3px 6px 5px;\" />\n </span>`;\n}\n\n\nclass SearchBox{\n\n constructor() {\n\n //this.searchQuery = [];\n // this.queryTypes = []; //**** Types associated to query elements\n // Types: element (E), formula (F), symbol (S) and prop names\n // \n // \n this.currentConnector;\n this.notItem; // It signals next item is preceding for a NOT\n this.inSubquery; // the current query pointer is in a subquery, paretheses open \n this.rootQuery; // Root query representation. Root item object (type=\"query\")\n this._resetState();\n /* Two type of object representing the expresions:\n - item (node): {type: 'element'|'formula'|'query', value: \"ElementName\"|\"Formula\"| Another (sub)query structure, not: true|false }\n - boolean connector: { type: 'connector', value: 'AND'|'OR'}\n\n */\n\n this.element = document.createElement('div');\n this.element.className = 'search-box';\n \n this.element.innerHTML = ` \n <div id=\"composition-title\" class=\"filter-section-title\" style>\n <div>Composition</div>\n </div>\n <div class=\"search-query-wrapper\" >\n <div id=\"syntax-error\">\n <div>Invalid query syntax</div>\n </div>\n <div class=\"search-query-box\" style=\"float: left;\"></div>\n <button class=\"clean-btn\" style=\"float: right;\">Clear</button>\n </div>\n <!-- this button should be out of the search box because its functionality\n is not part of the search box, it's general for the search module\n and it's probably being removed eventually --> \n <button class=\"search-btn\" >Search</button>\n `;\n\n this.cleanButton = this.element.querySelector('.clean-btn');\n this.cleanButton.addEventListener( \"click\", (e) => {\n // init search query\n this._resetState();\n this.renderQuery();\n this.cleanSearchQueryListener();\n });\n\n this.searchQueryBox = this.element.querySelector('.search-query-box');\n this.searchQueryBox.addEventListener( \"click\", e => {\n let className = e.target.className;\n if (className === 'remove-label'){\n let elSymbol = e.target.parentElement.getAttribute('data-el');\n this.removeElementORFormulaInSearchQuery(elSymbol);\n }\n });\n \n }\n\n _resetState(){\n this.notItem = false;\n this.inSubquery = false;\n this.rootQuery = { type: 'query', value: [], not: false }; // no prop open indicates that is the first\n }\n \n getOldQueryFormat(){\n const oldFormat = [];\n this.rootQuery.value.forEach( item => {\n oldFormat.push(...getItemInOldFormat(item));\n })\n\n const searchQuery = [];\n const queryTypes = [];\n oldFormat.forEach( item => {\n searchQuery.push(item.split(':')[0]);\n queryTypes.push(item.split(':')[1]);\n })\n console.log('OLD F:', oldFormat, searchQuery, queryTypes);\n\n return [searchQuery, queryTypes]\n\n function getItemInOldFormat(item){\n const oldFormat = [];\n if (item.not){\n oldFormat.push('NOT:S');\n }\n if (item.type === 'query'){\n oldFormat.push('(:S');\n item.value.forEach( i => {\n oldFormat.push(...getItemInOldFormat(i));\n })\n oldFormat.push('):S');\n }else{ // types: element, formula, material and connector\n let itemValue = item.value+':';\n if (item.type === 'element') itemValue += 'E';\n else if (item.type === 'formula') itemValue += 'F';\n else if (item.type === 'material') itemValue += 'MN';\n else itemValue += 'S'; // connector\n oldFormat.push(itemValue);\n }\n return oldFormat;\n }\n\n }\n\n getOptimadeQuery(allowOtherElements) {\n // Empty query\n if (this.rootQuery.value.length === 0) {\n return '';\n }\n\n // Translate element/formula queries into OptimadeSyntax\n const translator = new OptimadeTranslator();\n const [searchQuery, queryTypes] = this.getOldQueryFormat();\n return translator.translate(searchQuery, queryTypes, allowOtherElements);\n }\n\n setBoolOperator(operator){\n this.currentConnector = operator;\n }\n\n renderQuery() {\n document.querySelector('#syntax-error').style.visibility = 'hidden';\n this.searchQueryBox.innerHTML = getQueryStructHTML(this.rootQuery);\n \n if (this.searchQueryChangeListener) this.searchQueryChangeListener();\n\n function getQueryStructHTML(queryObj){\n\n if (queryObj.type === 'connector')\n return getSymbolHTML(queryObj.value);\n\n else if (!queryObj.type && queryObj.not) // NOT item wrapper, the item is not defined yet\n return getSymbolHTML(' NOT');\n\n else if (queryObj.type === 'element' || queryObj.type === 'formula' || queryObj.type === 'material') // element and formula\n return (queryObj.not ? getSymbolHTML(' NOT') : '')+\n getTagHtml(queryObj.value, (queryObj.type === 'formula' ? true : false));\n else{ // the object represents a query\n let html= '';\n queryObj.value.forEach( subqueryObj => {\n html += getQueryStructHTML(subqueryObj);\n });\n //console.log('getQueryStructHTML', queryObj, queryObj.value[queryObj.length-1])\n const notHTML = (queryObj.not ? getSymbolHTML(' NOT') : '')\n const isOpen = queryObj.open;\n return notHTML+(isOpen === undefined ? html : \n getSymbolHTML(' (')+html+( isOpen ? '' : getSymbolHTML(') ')));\n } \n }\n\n function getSymbolHTML(s){\n return `<span class=\"search-query-symbol\">${s}</span>`;\n }\n }\n\n\n // Add an item (with a preceding connector if needed) to the query \n // The item could be an NOT wrapper for a tag or subquery\n _addItem(item){ // It can be a tag item or a NOT (wrapping an item, added later) \n const queryContent = this.rootQuery.value;\n const tempQueryContent = this.inSubquery ?\n queryContent[queryContent.length-1].value : queryContent;\n\n // If the item is not at the beginning of the query (or subquery) and\n if ( tempQueryContent.length > 0 && \n // the last item is not a connector (AND|OR)\n tempQueryContent[tempQueryContent.length-1].type !== 'connector'){ \n // Add a connector (before the item)\n tempQueryContent.push( {type: 'connector', value: this.currentConnector} );\n }\n\n if (item === 'NOT') // Add an item wrapper with the NOT prop to true \n tempQueryContent.push({ not: true }); // type and value props are undefined\n else if (item === 'SUBQUERY')\n queryContent.push({ type: 'query', value: [], open: true, not: false}); \n else{ // Regular tags: element|formula|material\n const [type, tag] = item.split(':');\n tempQueryContent.push({type: type, value: tag, not: false});\n }\n console.log('ROOT QUERY ', this.rootQuery)\n this.renderQuery();\n }\n\n\n // Public method - type = element|formula|material \n addTag(tag, type){ // add an element, formula or material\n\n if (this.notItem){ // The tag to add is preceded for a NOT, the item object is already created\n const queryContent = this.rootQuery.value;\n const tempQueryContent = this.inSubquery ?\n queryContent[queryContent.length-1].value : queryContent;\n tempQueryContent[tempQueryContent.length-1].type = type;\n tempQueryContent[tempQueryContent.length-1].value = tag;\n this.notItem = false;\n this.renderQuery();\n }else\n this._addItem(type+':'+tag);\n }\n\n\n addElements(elementArray){\n let index = elementArray.length;\n while (index--) {\n this.addTag(elementArray[index], 'element');\n }\n return true;\n }\n\n\n addParentheses(isOpen){\n const queryContent = this.rootQuery.value; // ** Not checking if the it's inside a subquery\n \n if (isOpen){\n if (this.notItem){\n queryContent[queryContent.length-1].type = 'query';\n queryContent[queryContent.length-1].value = [];\n this.notItem = false;\n }else\n this._addItem('SUBQUERY');\n }\n queryContent[queryContent.length-1].open = isOpen;\n this.renderQuery();\n this.inSubquery = isOpen;\n }\n\n\n addNOT(){\n this._addItem('NOT');\n this.notItem = true; // The next item will be in a not expression\n }\n\n\n removeElementORFormulaInSearchQuery(item){\n // spot the item and remove the item and the bool operator(s) related\n removeItem(this.rootQuery, item);\n \n function removeItem(queryObj, item){\n const queryContent = queryObj.value;\n for (let i = 0; i < queryContent.length; i++) {\n if (queryContent[i].value === item) {\n if (queryContent[i+1] && queryContent[i+1].type === 'connector'){\n queryContent.splice(i, 2);\n }else\n queryContent.splice(i, 1);\n break;\n\n }else if (queryContent[i].type === 'query'){\n removeItem(queryContent[i], item);\n if (queryContent[i].value.length == 0){\n if (queryContent[i+1] && queryContent[i+1].type === 'connector'){\n queryContent.splice(i, 2);\n }else\n queryContent.splice(i, 1);\n }\n }\n }\n }\n\n this.renderQuery();\n\n console.log('ROOT QUERY REMOVING ', this.rootQuery)\n\n if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)\n this.removeElementListener(item);\n }\n\n if (this.rootQuery.value.length === 0){ // The search query gets blank\n this.cleanSearchQueryListener();\n }\n\n return true;\n }\n\n \n setCleanSearchQueryListener(listener){\n this.cleanSearchQueryListener = listener;\n }\n\n\n setRemoveElementListener(listener) {\n this.removeElementListener = listener;\n }\n\n setSearchQueryChangeListener(listener) {\n this.searchQueryChangeListener = listener;\n }\n\n getSearchButtonElement(){\n return this.element.querySelector('.search-btn');\n }\n}\n\n// EXPORTS\nmodule.exports = SearchBox;\n\n\n//# sourceURL=webpack:///./src/search-mod/SearchBox.view.js?");
/***/ })
......
......@@ -377,8 +377,8 @@ div.title span.unfolded::before{
.search-filter-side{
background-color: white;/* background-color: #DDD;*/
width: 220px;
padding: 16px;
width: 260px;
/*padding: 16px;*/
}
......@@ -386,10 +386,12 @@ div.title span.unfolded::before{
transition: opacity 0.8s;
}
.search-box{
.search-box {
width: 742px;
display: flex;
justify-content: center; /*space-between;*/
align-items: flex-start; /*space-between;*/
/*justify-content: center; [>space-between;<]*/
}
.search-query-wrapper{
position: relative;
......@@ -397,6 +399,10 @@ div.title span.unfolded::before{
background-color: white;
border: 2px solid #E56400;
padding: 8px 6px;
overflow-y: hidden;
overflow-x: hidden;
box-sizing: border-box;
min-height: 50px;
}
.clean-btn{
......@@ -409,29 +415,33 @@ div.title span.unfolded::before{
color: #E56400;
}
.search-btn{
/*width: 94px;*/
.search-btn {
display: block;
flex-grow: 1;
background-color: #E56400;
color: white;
padding: 16px;
padding: 10px;
flex: 0 0 100px;
box-sizing: border-box;
height: 50px;
}
.search-query-symbol{
font-size: 1.3em;
font-size: 1.0em;
font-weight: bold;
}
.add-buttons{
display: inline-block;
width: 100%;
margin: 20px 0 0;
margin: 0px 0 0;
background-color: #CFCFCF;
}
.bool-buttons {
color: #555;
}
.bool-buttons button{
color: #777;
border: 1px solid #777;
background-color: #EEE;
text-align: center;
......@@ -447,13 +457,13 @@ div.title span.unfolded::before{
width: 140px;
padding: 15px 20px;
font-size: .9em;
color: #666;
color: #555;
text-align: center;
}
.tab-buttons button:last-child {
border-right: none;
}
/*.tab-buttons button:last-child {*/
/*border-right: none;*/
/*}*/
.tab-buttons button#add-tab-selected{
background-color: #DDD;
......@@ -484,6 +494,11 @@ div.title span.unfolded::before{
border-bottom: 25px solid #DDD;
}
.add-panel {
padding: 20px 10px;
background-color: #DDD;
}
.add-panel button{
background-color: #EEE;
color: #777;
......@@ -498,12 +513,12 @@ div.title span.unfolded::before{
/* Formula and Material name boxes */
.FormulaBox,.MaterialNameBox{
.FormulaBox,
.MaterialNameBox{
display: flex;
justify-content: center;
text-align: left;
background-color: #DDD;
padding: 20px 0;
}
.FormulaBox input, .MaterialNameBox input{
......@@ -517,7 +532,7 @@ div.title span.unfolded::before{
width: 100%;
}
.FormulaBox input{
.FormulaBox input[type=text]{
box-sizing: border-box;
width: 580px;
}
......@@ -630,8 +645,7 @@ div.title span.unfolded::before{
/* equivalent to view-box */
.filter-section-box{
margin-bottom: 30px;
/*background-color: white;*/
margin-bottom: 20px;
}
.filter-section-box input[type="text"]{
box-sizing: border-box;
......@@ -641,16 +655,18 @@ div.title span.unfolded::before{
}
.filter-section-title{
display: flex;
align-items: center;
justify-content: center;
color: white; padding: 10px 0;
font-size: 12pt;
text-align: center;
background-color: #E56400;
padding: 10px;
}
.filter-quantity-box{
.filter-quantity-box {
margin-bottom: 12px;
padding: 0px 0px 4px 4px ;
padding: 5px 15px;
}
.filter-quantity-box > * {
......@@ -710,7 +726,8 @@ background-image: -webkit-linear-gradient(top, #777, #AAA, #777);
#searchbar #searchline{ text-align: center; }
.search-option {
padding: 10px 0px;
padding-top: 10px;
padding-bottom: 0;
font-size: 0.85em;
}
......@@ -759,9 +776,7 @@ img.remove-label{ cursor: pointer; }
#elementable{width:690px; position: relative;
padding: 0 26px;
margin: 0 auto 0;
background-color: #DDD;
}
.element-info{position: absolute;
......@@ -780,8 +795,8 @@ img.remove-label{ cursor: pointer; }
}
#elementable table#pt-main{
padding-top: 20px;
color: #333; border: 0;
color: #333;
border: 0;
}
#elementable table#pt-laac{
......@@ -1416,6 +1431,20 @@ text.structure-viewer-legend-labels{
visibility: visible;
}
.composition {
}
#composition-title {
box-sizing: border-box;
height: 50px;
width: 120px;
}
.restricted-search-option {
margin-top: -20px;
margin-bottom: 10px;
}
/*
.tooltip {
visibility: hidden;
......
......@@ -240,6 +240,11 @@ class ElemenTable{
</div>`;//'<div id="group-selectors">';
tempHtml += `<div class="perm-tooltip search-option" style="margin-right: 10px">
<input id="allow-other-elements" name="allow-other-elements" type="checkbox" checked>
<label for="allow-other-elements" class="perm-tooltip">Allow other elements</label>
<span class="tooltiptext">If selected, the returned materials may also contain other elements.</span>
</div>`;
tempHtml+= '</div>'; // ptWrapper
this.element.innerHTML= tempHtml;
......
......@@ -109,9 +109,31 @@ class FilterPanel {
this.fields.push(functionalTypeField)
methodGroupBox.append(functionalTypeField.element);
const codeField = new AutocompleteField('Code', 'code_name');
this.fields.push(codeField)
this.fields.push(codeField);
methodGroupBox.append(codeField.element);
// Restricted search checkbox: cannot be set as innerHTML
const container = document.createElement('div');
container.className = "filter-quantity-box";
const a = document.createElement('div');
a.className = "perm-tooltip search-option restricted-search-option";
const b = document.createElement('input');
b.id = "restricted-search";
b.name = "restricted-search";
b.type = "checkbox";
const c = document.createElement('label');
c["for"] = "restricted-search";
c.className = "perm-tooltip";
c.textContent = "Restrict to individual calculations";
const d = document.createElement('span');
d.className = "tooltiptext";
d.textContent = "If selected, the query will return materials that have individual calculations simultaneously matching your methodology and properties criteria."
a.append(b);
a.append(c);
a.append(d);
container.append(a);
this.element.append(container);
InfoSys.addToInfoSystem(this.element);
// The value change event in fields is handled at container level (event delegation pattern)
......@@ -132,8 +154,7 @@ class FilterPanel {
createPropsGroupBox(title){
const element = document.createElement('div');
element.className = 'filter-section-box';
element.innerHTML = '<div class="filter-section-title">'+title+'</div>';
element.innerHTML = '<div class="filter-section-title"><div>'+title+'</div></div>';
return element;
}
......@@ -144,14 +165,16 @@ class FilterPanel {
this.fields.forEach( field => {
const values = field.getValues();
if (Array.isArray(values)){
console.log("===============")
console.log(values)
if (Array.isArray(values) && values.length > 0){
values.forEach( value => filterMap.set(value.fieldId, value.value))
}else{
if (values && values.value.length > 0)
filterMap.set(values.fieldId, values.value)
} else if (values && values.value && values.value.length > 0) {
filterMap.set(values.fieldId, values.value);
}
});
//console.log('FilterPanel getValues:', filterMap);
console.log("FILTERMAP")
console.log(filterMap)
return filterMap;
}
......
......@@ -95,8 +95,9 @@ class MaterialList {
if (page) reqJson.search_by = { page: page}
// Add the restricted option from the checkbox
let restrictedEl = document.getElementById('restricted-search');
reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');
//let restrictedEl = document.getElementById('restricted-search');
//reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');
reqJson.search_by.restricted = '0';
console.log('SEARCHING: ', reqJson );