Commit d86abfde authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Trying out new optimade query translation.

parent ce4e8cab
......@@ -382,7 +382,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 AutocompleteMultiselectList = __webpack_require__(/*! ./Autocomplete.view.js */ \"./src/search-mod/Autocomplete.view.js\").AutocompleteMultiselectList;\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 const spaceGroupField = new SpaceGroupField();\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \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 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 \n if (e.target.id !== 'space-group-dropdown-list') {\n console.log('change event', e);\n this.addPropsChangeListener(this.getValues())\n }\n\n \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 \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/* Temporarily the select field is disabled \n so that the only option to look for is \"by number\" */\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 this.autocomplete = new AutocompleteMultiselectList(id);\n this.autocomplete.element.placeholder = \"Search and select options\";\n this.autocomplete.element.classList.add('textfield-filter');\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.autocomplete(names);\n });\n }\n\n getValues(){\n const values = this.autocomplete.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\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 AutocompleteMultiselectList = __webpack_require__(/*! ./Autocomplete.view.js */ \"./src/search-mod/Autocomplete.view.js\").AutocompleteMultiselectList;\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 const spaceGroupField = new SpaceGroupField();\n this.fields.push(spaceGroupField)\n structureGroupBox.append(spaceGroupField.element); \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 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 \n if (e.target.id !== 'space-group-dropdown-list') {\n // console.log('change event', e);\n if (this.addPropsChangeListener) this.addPropsChangeListener(this.getValues())\n }\n\n \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 \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/* Temporarily the select field is disabled \n so that the only option to look for is \"by number\" */\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 this.autocomplete = new AutocompleteMultiselectList(id);\n this.autocomplete.element.placeholder = \"Search and select options\";\n this.autocomplete.element.classList.add('textfield-filter');\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.autocomplete(names);\n });\n }\n\n getValues(){\n const values = this.autocomplete.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\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?");
/***/ }),
......@@ -393,7 +393,7 @@ eval("\n\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed
/***/ ((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.setAttribute(\"id\",'matlist');\n\n this.matMap = new Map();\n //this.currentSystemType = 'bulk';\n this.optimadeQuery = null;\n \n this._hide();\n\n this.pagControl = new PaginationControl();\n this.element.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.element.append(this.matListContainer.element);\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n }\n\n\n _hide() {\n this.element.style.display = 'none';\n }\n\n\n _show() {\n this.element.style.display = '';\n }\n\n\n invalidateSearch(){\n this._hide();\n }\n\n\n initSearch(optimadeQuery){\n\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show();\n\n let reqJson = { query: this.optimadeQuery };\n if (page) reqJson.search_by = { page: page}\n\n console.log('SEARCHING: ', reqJson );\n\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\n this._setMatList(result.results);\n this.pagControl.set(result.pages);\n\n if (result.results.length === 0){\n this._hide();\n\n }else{\n if (result.results.length === 1){\n const onlyMat = this.matMap.values().next().value[0];\n util.setBrowserHashPath('material', onlyMat.material_id);\n }\n //else{\n this.matListContainer.updateList(this.matMap);\n this._show();\n //} \n }\n \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 _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.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._hide();\n }*/\n\n\n initSearch(optimadeQuery){\n\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show();\n\n let reqJson = { query: this.optimadeQuery };\n if (page) reqJson.search_by = { page: page}\n\n console.log('SEARCHING: ', reqJson );\n\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 /* Go to the material page straightaway - Not wanted fro now\n if (result.results.length === 1){\n const onlyMat = this.matMap.values().next().value[0];\n util.setBrowserHashPath('material', onlyMat.material_id);\n } */\n \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\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 }\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?");
/***/ }),
......@@ -415,7 +415,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\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\n this.searchFilters = [];\n\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\n <!-- <div class=\"search-title\">Properties</div>-->\n\n <!-- <div id=\"filter-panel-placeholder\"> </div> -->\n\n </div>\n\n <div class=\"search-main-side\">\n\n <!-- <div class=\"search-title\">Composition</div> -->\n\n <div class=\"search-box-placeholder\" > </div>\n \n <input type=\"checkbox\" id=\"multiples-of-formula\" value=\"\">\n Include multiples of formula\n <br>\n <input type=\"checkbox\" id=\"allow-other-elements\" value=\"\">\n Allow other elements\n <br>\n\n\n <div class=\"add-buttons\" >\n <div class=\"tab-buttons\" style=\"width: 70%; display: inline-block\">\n <button class=\"element-add-btn\" id=\"add-tab-selected\" style=\"margin-left: 50px\">Element</button>\n <button class=\"formula-add-btn\" style=\"padding: 10px 20px;\" >Formula</button>\n <button class=\"name-add-btn\" >Name</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 <!--<input type=\"checkbox\" name=\"and-or\" class=\"not-symbol-btn\" />NOT-->\n </div>\n </div>\n\n\n <div class=\"add-box\">\n <div style=\"width: 70%; display: inline-block;\">\n <div class=\"triangle-container\" style=\"margin-left: 50px;\">\n <div class=\"triangle element-tri\" style=\"visibility: visible\"></div>\n </div>\n <div class=\"triangle-container\" >\n <div class=\"triangle formula-tri\" style=\"visibility: hidden\"></div>\n </div>\n <div class=\"triangle-container\" >\n <div class=\"triangle name-tri\" style=\"visibility: hidden\"></div>\n </div>\n </div>\n\n <div class=\"add-panel\">\n </div>\n </div>\n\n <div class=\"results-panel\"> <!-- style=\"display: none\"-->\n </div>\n\n </div> <!-- search-main-side -->\n `;\n\n this.filterSidePanel = this.element.querySelector('.search-filter-side');\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 this.searchBox.setSearchQueryChangeListener( () => {\n this.sendQuery();\n });\n\n this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');\n this.allowOtherElementsCheckbox.addEventListener( 'change', e => {\n this.sendQuery();\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 });\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\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 => 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, 'F');\n //this.formulaBox.disable(true);\n this.addElementButton.disabled = true;\n this.addMatNameButton.disabled = true;\n // this.materialNameBox.disableInput(); It doesn't seem needed\n }\n });\n\n\n this.materialNameBox = new MaterialNameBox();\n this.materialNameBox.setAddMaterialNameListener( name => {\n if (name.trim() !== ''){\n this.searchBox.addTag(name, 'MN');// this.addTag(name, 'MN');\n this.addElementButton.disabled = true;\n this.addFormulaButton.disabled = true;\n // this.formulaBox.disableInput(); It doesn't seem needed\n this.materialNameBox.disableInput();\n }\n });\n // add autocomplete functionality to the textfield\n// this.materialNameBox.setAutocomplete();\n\n this.filterPanel = new FilterPanel();\n this.filterSidePanel.appendChild(this.filterPanel.element);\n\n this.filterPanel.setPropsChangeListener( propsMap => {\n console.log('filterPanel.change Event propsMap: ', propsMap);\n this.sendQuery();\n })\n\n this.materialList= new MaterialList();\n this.resultsPage = this.element.querySelector('.results-panel');\n this.materialList.attachAndSetEvents(this.resultsPage);\n\n this.currentTab = 'element';\n this.addPanel.appendChild(this.elementTable.element);\n\n this._events();\n }\n\n\n _events() {\n\n /*\n // External event - Search button press\n this.mainButton.addEventListener( \"click\", (e) => {\n //console.log(\"this.labels: \"+JSON.stringify(this.labels));\n\n if (this.searchQuery.lenght === 0){\n util.showUserMsg('No query');\n }else{\n let searchExpressionQuery;\n\n // Search by Material name\n if (this.queryTypes[0] === 'MN'){\n //queryObj.push(this._getESSimpleMatch('material_name', item));\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.must = [];\n rootQueryObj.bool.must.push( this._getESSimpleMatch('material_name', this.searchQuery[0]) );\n this.materialList.initSearch( rootQueryObj );\n\n }else{ // Search by complex search expression\n\n searchExpressionQuery = this.getOptimadeQueryFromSearchQuery(this.searchQuery, this.queryTypes);\n\n this.materialList.initSearch(searchExpressionQuery);//this._addFiltersInSearchQuery( this.filterPanel.getValues(), searchExpressionQuery));\n\n \n if (this.element.querySelector('#allow-other-elements').checked)\n searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements(\n this.searchQuery, this.queryTypes);\n // Regular case: search containing only the elements in the search expression\n else searchExpressionQuery =\n this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes);\n\n this.materialList.initSearch(\n this._addFiltersInSearchQuery( this.filterPanel.getValues(),\n searchExpressionQuery));\n //util.setBrowserHashPath('search','results');\n\n //TODO: why not identifying by ID???\n this.element.querySelector('.add-box').style.display = 'none';\n \n }\n\n }\n\n });\n*/\n\n \n\n this.addButtonsBox.addEventListener( \"click\", (e) => {\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let index = className.indexOf('add-btn');\n\n if (index > 0){\n let selectingElement;\n let selectingTab = className.substring(0, index-1);\n if (selectingTab === 'element')\n selectingElement = this.elementTable.element;\n else if (selectingTab === '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 }\n else if (selectingTab === 'formula')\n selectingElement = this.formulaBox.element;\n\n this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);\n\n this.element.querySelector('.add-box').style.display = 'block';\n\n // Change the styles of the buttons\n let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn');\n this._setTabSelectedStyles(selEl, false);\n this._setTabSelectedStyles(e.target, true);\n\n // Change the triangle\n this.element.querySelector('.'+this.currentTab+'-tri').style.visibility = 'hidden';\n this.element.querySelector('.'+selectingTab+'-tri').style.visibility = 'visible';\n\n this.currentTab = selectingTab;\n\n/*\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 // gray-out events for the Properties panel and Composition panel \n \n let mainSideElem = this.element.querySelector('.search-main-side');\n\n this.filterSidePanel.addEventListener( \"mouseenter\", event => {\n mainSideElem.style.opacity = 0.3\n });\n this.filterSidePanel.addEventListener( \"mouseleave\", event => {\n mainSideElem.style.opacity = ''\n });\n\n mainSideElem.addEventListener( \"mouseenter\", event => {\n // console.log('filters:', this.filterPanel.getValues())\n this.filterPanel.showSelectedProps(true)\n });\n mainSideElem.addEventListener( \"mouseleave\", event => {\n this.filterPanel.showSelectedProps(false)\n });\n\n }\n\n\n sendQuery(){\n \n //**** The optimade query must be formed from the search box and the properties selected\n let optimadeQuery = this.searchBox.getOptimadeQuery(this.allowOtherElementsCheckbox.checked);\n let optimadeQueryForProps = getOptimadeQueryFromProps(this.filterPanel.getValues());\n if (optimadeQuery !== ''){ // if there are items in the serach box\n optimadeQuery += (optimadeQueryForProps.length > 0 ? ' AND '+optimadeQueryForProps : '');\n console.log('sendQuery optimadeQuery: ', optimadeQuery );\n this.materialList.initSearch(optimadeQuery);\n }else\n this.materialList.invalidateSearch();\n\n \n function getOptimadeQueryFromProps(propsValuesMap){\n\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\n\n _getESQueryFromSearchQuery(searchQuery, queryTypes){\n let formulas = [];\n let parFormulas = [];\n let parOperator = null; // operator just before the opening parenthesis\n let openIndex = -1;\n searchQuery.forEach( (item, i) => {\n if (queryTypes[i] === 'F' || queryTypes[i] === 'E'){\n addItem( (openIndex >= 0 ? parFormulas : formulas), i,\n item+(queryTypes[i] === 'E' ? '0' : ''));\n\n }else if ( searchQuery[i] === '(' ){\n if (i-1 >= 0 ) parOperator = searchQuery[i-1];\n openIndex = i;\n }\n else if ( searchQuery[i] === ')' ){\n if (parOperator === null){ // The starting ( was the first symbol of the expression\n formulas = parFormulas;\n }else if (parOperator === 'OR'){ // OR (...\n formulas = formulas.concat(parFormulas);\n }else{ // AND (...\n let rFormulas = [];\n formulas.forEach( formula => {\n parFormulas.forEach( parFormula => rFormulas.push(formula+parFormula) );\n });\n formulas = rFormulas;\n }\n parFormulas = []; // reset the array formulas inside the parentheses\n\n openIndex = -1;\n }\n });\n console.log('_getESQueryFromSearchQuery: ',formulas, parFormulas);\n\n //*********** Get the elastic search expression from the search expression\n // the elements inserted must be sorted for this to work\n // queryObj.bool.must.push(this._getESSimpleMatch('atom_labels_keyword', this._sortElements(elements) )); ///elements.join('')));\n // if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.should = [];\n let queryObj = rootQueryObj.bool.should;\n let reduced = this.element.querySelector('#multiples-of-formula').checked;\n\n formulas.forEach( formula => {\n let pFormula;\n let searchByElement = (formula.indexOf('0') >= 0);\n if (searchByElement){ // There are some element in the search expression\n /** TDO**/\n let f = formula;\n if (reduced) f = this._reduceFormula(formula, false);\n pFormula = this._processFormula(f, 'element-string');\n let tempQueryObj = { 'bool' : { 'must' : [] } };\n\n if (pFormula[0].length > 0) // length === 0 No formula, only elements\n tempQueryObj.bool.must.push(\n this._getESOperatorMatch(\n (reduced ? 'formula_reduced_terms' : 'formula_cell_terms'), pFormula[0]));\n tempQueryObj.bool.must.push(\n this._getESSimpleMatch('atom_labels_keyword', pFormula[1]));\n\n queryObj.push(tempQueryObj);\n\n }else{ // Only formulas\n if ( reduced ){ // reduced search\n pFormula = this._reduceFormula(formula, false);\n queryObj.push(this._getESSimpleMatch('formula_reduced_keyword', pFormula));\n }else{\n pFormula = this._processFormula(formula, 'canonical-formula');\n queryObj.push(this._getESSimpleMatch('formula_cell_keyword', pFormula));\n }\n }\n\n });\n\n return rootQueryObj;\n\n function addItem(formulas, i, item){\n if (i === 0 || searchQuery[i-1] === '('){\n formulas.push(item);\n }else{\n if (searchQuery[i-1] === 'OR') formulas.push(item);\n else if (searchQuery[i-1] === 'AND')\n formulas.push(formulas.pop()+item);\n }\n }\n\n } // _getESQueryFromSearchQuery()\n\n\n _getESQueryFromSearchQuery_otherElements(searchQuery, queryTypes){\n // Query structure analysis - looking for parentheses (only one level supported)\n if ( searchQuery.indexOf('(') >= 0){ // Recursion\n\n let openIndex = -1;\n let prodQuery = [];\n let prodTypes = [];\n //let prodQueryIndex = 0;\n for (let i = 0; i < searchQuery.length; i++) {\n if ( searchQuery[i] === '(' ) openIndex = i;\n else if ( searchQuery[i] === ')' ){\n prodQuery.push(this._getESQueryFromSearchQuery_otherElements(\n searchQuery.slice(openIndex+1, i), queryTypes.slice(openIndex+1, i)));\n prodTypes.push('Q');\n openIndex = -1;\n }else if (openIndex < 0){ // outside a parentheses\n prodQuery.push(searchQuery[i]);\n prodTypes.push(queryTypes[i]);\n }\n }\n //console.log('prodQuery', prodQuery, prodTypes);\n return this._getESQueryFromSearchQuery_otherElements(prodQuery, prodTypes);\n\n }else{ // BASE CASE: there is no parentheses\n\n let boolOperator;\n searchQuery.forEach( (item, i) => {\n if (searchQuery[i] === 'AND' || searchQuery[i] === 'OR')\n boolOperator = item;\n });\n\n let rootQueryObj = { 'bool' : {} };\n let queryObj;\n if (boolOperator === 'AND'){\n rootQueryObj.bool.must = [];\n queryObj = rootQueryObj.bool.must;\n }else{ // OR\n rootQueryObj.bool.should = [];\n queryObj = rootQueryObj.bool.should;\n }\n\n searchQuery.forEach( (item, i) => {\n\n if (queryTypes[i] === 'F'){ // Formula case\n let esMatchQuery;\n if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search\n esMatchQuery = this._getESOperatorMatch('formula_reduced_terms', this._reduceFormula(item));\n }else\n esMatchQuery = this._getESOperatorMatch('formula_cell_terms', this._processFormula(item, 'tokens'));\n queryObj.push(esMatchQuery);\n\n }else if (queryTypes[i] === 'E'){ // Element case\n queryObj.push(this._getESSimpleMatch('atom_labels_terms', item));\n\n }else if (queryTypes[i] === 'Q'){\n queryObj.push(item);\n }\n });\n\n return rootQueryObj;\n } // else\n } // _getESQueryFromSearchQuery_otherElements()\n\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\n return rootQueryObj;\n }\n\n\n _getESSimpleMatch(field, value){\n return {\n \"match\": { [field] : value }\n };\n }\n\n _getESTermsArray(field, value){\n return {\n \"terms\": { [field] : value }\n };\n }\n\n\n _getESOperatorMatch(field, elements, and = true){\n let elementsString = '';\n if (elements.length > 0) elementsString = elements.join(' ');\n\n return {\n \"match\": {\n [field]: {\n \"operator\": (and ? \"and\" : 'or'),\n \"query\": elementsString\n }\n }\n };\n }\n\n\n _getFieldESRange(field, valuesString){\n let data = valuesString.split(':');\n console.log('_getFieldESRange data', data);\n\n return {\n \"range\": {\n [field]: { \"gte\" : parseInt(data[0]), \"lte\" : parseInt(data[1]) }\n }\n };\n }\n\n\n\n\n _setTabSelectedStyles(element, value){\n /*\n element.style.fontWeight = (value ? 'bold' : 'normal');\n element.style.color = (value ? '#E56400' : '#777');\n element.style.borderColor = (value ? '#E56400' : '#777');\n */\n element.id = (value ? 'add-tab-selected' : '');\n }\n\n\n/*\n _showSearchBox(){\n if (!this.showingSearchBox){\n this.showingSearchBox = true;\n this.searchLine.style.visibility = 'visible';\n\n if (this.userGuidance) UserGuidance.setFinal();\n }\n }*/\n\n \n\n\n \n\n ///********* DELETE?\n showSearchResults(){\n /*\n this.searchPage.style.display= 'none';\n this.resultsPage.style.display= 'block';\n */\n //if (this.userGuidance) UserGuidance.show(false);\n }\n\n\n showSearchPage(){\n /*\n this.searchPage.style.display= 'block';\n this.resultsPage.style.display= 'none';\n\n if (this.userGuidance){\n setTimeout(() => {\n UserGuidance.init(this.addButtonsBox, this.elementTable.element,\n this.searchBox, this.propertiesBox.tabsElement);\n UserGuidance.show(true, this.currentTab === 'element',\n this.currentTab === 'props');\n }, 400);\n }\n */\n }\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\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.setAttribute(\"id\",'formula-box');\n this.element.innerHTML=\n `\n <div style=\"padding-bottom: 20px;\">\n<!--\n <input type=\"checkbox\" class=\"allow-other-elements\" value=\"\">\n Allow other elements\n <br>\n\n\n <input type=\"checkbox\" class=\"multiples-of-formula\" value=\"\">\n Include multiples of formula\n <br> -->\n\n <input type=\"text\" class=\"textfield-composition\"\n placeholder=\"Add formula to the search query above\" >\n <button class=\"adding-formula-btn\" disabled>Add to query</button>\n </div>\n `;\n this.formulaTextField = this.element.querySelector('.textfield-composition');\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\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\n this.searchFilters = [];\n\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\n <!-- <div class=\"search-title\">Properties</div>-->\n\n <!-- <div id=\"filter-panel-placeholder\"> </div> -->\n\n </div>\n\n <div class=\"search-main-side\">\n\n <!-- <div class=\"search-title\">Composition</div> -->\n\n <div class=\"search-box-placeholder\" > </div>\n \n <input type=\"checkbox\" id=\"multiples-of-formula\" value=\"\">\n Include multiples of formula\n <br>\n <input type=\"checkbox\" id=\"allow-other-elements\" value=\"\" checked>\n Allow other elements\n <br>\n\n\n <div class=\"add-buttons\" >\n <div class=\"tab-buttons\" style=\"width: 70%; display: inline-block\">\n <button class=\"element-add-btn\" id=\"add-tab-selected\" style=\"margin-left: 50px\">Element</button>\n <button class=\"formula-add-btn\" style=\"padding: 10px 20px;\" >Formula</button>\n <button class=\"name-add-btn\" >Name</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 <!--<input type=\"checkbox\" name=\"and-or\" class=\"not-symbol-btn\" />NOT-->\n </div>\n </div>\n\n\n <div class=\"add-box\">\n <div style=\"width: 70%; display: inline-block;\">\n <div class=\"triangle-container\" style=\"margin-left: 50px;\">\n <div class=\"triangle element-tri\" style=\"visibility: visible\"></div>\n </div>\n <div class=\"triangle-container\" >\n <div class=\"triangle formula-tri\" style=\"visibility: hidden\"></div>\n </div>\n <div class=\"triangle-container\" >\n <div class=\"triangle name-tri\" style=\"visibility: hidden\"></div>\n </div>\n </div>\n\n <div class=\"add-panel\">\n </div>\n </div>\n\n <div class=\"results-panel\"> <!-- style=\"display: none\"-->\n </div>\n\n </div> <!-- search-main-side -->\n `;\n\n this.filterSidePanel = this.element.querySelector('.search-filter-side');\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 this.sendQuery();\n }); */\n\n this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');\n /*\n this.allowOtherElementsCheckbox.addEventListener( 'change', e => {\n this.sendQuery();\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 });\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\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 => 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, 'F');\n //this.formulaBox.disable(true);\n this.addElementButton.disabled = true;\n this.addMatNameButton.disabled = true;\n // this.materialNameBox.disableInput(); It doesn't seem needed\n }\n });\n\n\n this.materialNameBox = new MaterialNameBox();\n this.materialNameBox.setAddMaterialNameListener( name => {\n if (name.trim() !== ''){\n this.searchBox.addTag(name, 'MN');// this.addTag(name, 'MN');\n this.addElementButton.disabled = true;\n this.addFormulaButton.disabled = true;\n // this.formulaBox.disableInput(); It doesn't seem needed\n this.materialNameBox.disableInput();\n }\n });\n // add autocomplete functionality to the textfield\n// this.materialNameBox.setAutocomplete();\n\n this.filterPanel = new FilterPanel();\n this.filterSidePanel.appendChild(this.filterPanel.element);\n\n/*\n this.filterPanel.setPropsChangeListener( propsMap => {\n console.log('filterPanel.change Event propsMap: ', propsMap);\n this.sendQuery();\n })*/\n\n this.materialList= new MaterialList();\n this.resultsPage = this.element.querySelector('.results-panel');\n this.materialList.attachAndSetEvents(this.resultsPage);\n\n this.currentTab = 'element';\n this.addPanel.appendChild(this.elementTable.element);\n\n this._events();\n }\n\n\n _events() {\n\n /*\n // External event - Search button press\n this.mainButton.addEventListener( \"click\", (e) => {\n //console.log(\"this.labels: \"+JSON.stringify(this.labels));\n\n if (this.searchQuery.lenght === 0){\n util.showUserMsg('No query');\n }else{\n let searchExpressionQuery;\n\n // Search by Material name\n if (this.queryTypes[0] === 'MN'){\n //queryObj.push(this._getESSimpleMatch('material_name', item));\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.must = [];\n rootQueryObj.bool.must.push( this._getESSimpleMatch('material_name', this.searchQuery[0]) );\n this.materialList.initSearch( rootQueryObj );\n\n }else{ // Search by complex search expression\n\n searchExpressionQuery = this.getOptimadeQueryFromSearchQuery(this.searchQuery, this.queryTypes);\n\n this.materialList.initSearch(searchExpressionQuery);//this._addFiltersInSearchQuery( this.filterPanel.getValues(), searchExpressionQuery));\n\n \n if (this.element.querySelector('#allow-other-elements').checked)\n searchExpressionQuery = this._getESQueryFromSearchQuery_otherElements(\n this.searchQuery, this.queryTypes);\n // Regular case: search containing only the elements in the search expression\n else searchExpressionQuery =\n this._getESQueryFromSearchQuery(this.searchQuery, this.queryTypes);\n\n this.materialList.initSearch(\n this._addFiltersInSearchQuery( this.filterPanel.getValues(),\n searchExpressionQuery));\n //util.setBrowserHashPath('search','results');\n\n //TODO: why not identifying by ID???\n this.element.querySelector('.add-box').style.display = 'none';\n \n }\n\n }\n\n });\n*/\n\n \n\n this.addButtonsBox.addEventListener( \"click\", (e) => {\n if (e.target !== e.currentTarget) { // When the event source is a child\n let className = e.target.className;\n let index = className.indexOf('add-btn');\n\n if (index > 0){\n let selectingElement;\n let selectingTab = className.substring(0, index-1);\n if (selectingTab === 'element')\n selectingElement = this.elementTable.element;\n else if (selectingTab === '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 }\n else if (selectingTab === 'formula')\n selectingElement = this.formulaBox.element;\n\n this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);\n\n this.element.querySelector('.add-box').style.display = 'block';\n\n // Change the styles of the buttons\n let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn');\n this._setTabSelectedStyles(selEl, false);\n this._setTabSelectedStyles(e.target, true);\n\n // Change the triangle\n this.element.querySelector('.'+this.currentTab+'-tri').style.visibility = 'hidden';\n this.element.querySelector('.'+selectingTab+'-tri').style.visibility = 'visible';\n\n this.currentTab = selectingTab;\n\n/*\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 // gray-out events for the Properties panel and Composition panel \n \n let mainSideElem = this.element.querySelector('.search-main-side');\n\n this.filterSidePanel.addEventListener( \"mouseenter\", event => {\n mainSideElem.style.opacity = 0.3\n });\n this.filterSidePanel.addEventListener( \"mouseleave\", event => {\n mainSideElem.style.opacity = ''\n });\n\n mainSideElem.addEventListener( \"mouseenter\", event => {\n // console.log('filters:', this.filterPanel.getValues())\n this.filterPanel.showSelectedProps(true)\n });\n mainSideElem.addEventListener( \"mouseleave\", event => {\n this.filterPanel.showSelectedProps(false)\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\n const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');\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\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\n\n _getESQueryFromSearchQuery(searchQuery, queryTypes){\n let formulas = [];\n let parFormulas = [];\n let parOperator = null; // operator just before the opening parenthesis\n let openIndex = -1;\n searchQuery.forEach( (item, i) => {\n if (queryTypes[i] === 'F' || queryTypes[i] === 'E'){\n addItem( (openIndex >= 0 ? parFormulas : formulas), i,\n item+(queryTypes[i] === 'E' ? '0' : ''));\n\n }else if ( searchQuery[i] === '(' ){\n if (i-1 >= 0 ) parOperator = searchQuery[i-1];\n openIndex = i;\n }\n else if ( searchQuery[i] === ')' ){\n if (parOperator === null){ // The starting ( was the first symbol of the expression\n formulas = parFormulas;\n }else if (parOperator === 'OR'){ // OR (...\n formulas = formulas.concat(parFormulas);\n }else{ // AND (...\n let rFormulas = [];\n formulas.forEach( formula => {\n parFormulas.forEach( parFormula => rFormulas.push(formula+parFormula) );\n });\n formulas = rFormulas;\n }\n parFormulas = []; // reset the array formulas inside the parentheses\n\n openIndex = -1;\n }\n });\n console.log('_getESQueryFromSearchQuery: ',formulas, parFormulas);\n\n //*********** Get the elastic search expression from the search expression\n // the elements inserted must be sorted for this to work\n // queryObj.bool.must.push(this._getESSimpleMatch('atom_labels_keyword', this._sortElements(elements) )); ///elements.join('')));\n // if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search\n let rootQueryObj = { 'bool' : {} };\n rootQueryObj.bool.should = [];\n let queryObj = rootQueryObj.bool.should;\n let reduced = this.element.querySelector('#multiples-of-formula').checked;\n\n formulas.forEach( formula => {\n let pFormula;\n let searchByElement = (formula.indexOf('0') >= 0);\n if (searchByElement){ // There are some element in the search expression\n /** TDO**/\n let f = formula;\n if (reduced) f = this._reduceFormula(formula, false);\n pFormula = this._processFormula(f, 'element-string');\n let tempQueryObj = { 'bool' : { 'must' : [] } };\n\n if (pFormula[0].length > 0) // length === 0 No formula, only elements\n tempQueryObj.bool.must.push(\n this._getESOperatorMatch(\n (reduced ? 'formula_reduced_terms' : 'formula_cell_terms'), pFormula[0]));\n tempQueryObj.bool.must.push(\n this._getESSimpleMatch('atom_labels_keyword', pFormula[1]));\n\n queryObj.push(tempQueryObj);\n\n }else{ // Only formulas\n if ( reduced ){ // reduced search\n pFormula = this._reduceFormula(formula, false);\n queryObj.push(this._getESSimpleMatch('formula_reduced_keyword', pFormula));\n }else{\n pFormula = this._processFormula(formula, 'canonical-formula');\n queryObj.push(this._getESSimpleMatch('formula_cell_keyword', pFormula));\n }\n }\n\n });\n\n return rootQueryObj;\n\n function addItem(formulas, i, item){\n if (i === 0 || searchQuery[i-1] === '('){\n formulas.push(item);\n }else{\n if (searchQuery[i-1] === 'OR') formulas.push(item);\n else if (searchQuery[i-1] === 'AND')\n formulas.push(formulas.pop()+item);\n }\n }\n\n } // _getESQueryFromSearchQuery()\n\n\n _getESQueryFromSearchQuery_otherElements(searchQuery, queryTypes){\n // Query structure analysis - looking for parentheses (only one level supported)\n if ( searchQuery.indexOf('(') >= 0){ // Recursion\n\n let openIndex = -1;\n let prodQuery = [];\n let prodTypes = [];\n //let prodQueryIndex = 0;\n for (let i = 0; i < searchQuery.length; i++) {\n if ( searchQuery[i] === '(' ) openIndex = i;\n else if ( searchQuery[i] === ')' ){\n prodQuery.push(this._getESQueryFromSearchQuery_otherElements(\n searchQuery.slice(openIndex+1, i), queryTypes.slice(openIndex+1, i)));\n prodTypes.push('Q');\n openIndex = -1;\n }else if (openIndex < 0){ // outside a parentheses\n prodQuery.push(searchQuery[i]);\n prodTypes.push(queryTypes[i]);\n }\n }\n //console.log('prodQuery', prodQuery, prodTypes);\n return this._getESQueryFromSearchQuery_otherElements(prodQuery, prodTypes);\n\n }else{ // BASE CASE: there is no parentheses\n\n let boolOperator;\n searchQuery.forEach( (item, i) => {\n if (searchQuery[i] === 'AND' || searchQuery[i] === 'OR')\n boolOperator = item;\n });\n\n let rootQueryObj = { 'bool' : {} };\n let queryObj;\n if (boolOperator === 'AND'){\n rootQueryObj.bool.must = [];\n queryObj = rootQueryObj.bool.must;\n }else{ // OR\n rootQueryObj.bool.should = [];\n queryObj = rootQueryObj.bool.should;\n }\n\n searchQuery.forEach( (item, i) => {\n\n if (queryTypes[i] === 'F'){ // Formula case\n let esMatchQuery;\n if ( this.element.querySelector('#multiples-of-formula').checked ){ // reduced search\n esMatchQuery = this._getESOperatorMatch('formula_reduced_terms', this._reduceFormula(item));\n }else\n esMatchQuery = this._getESOperatorMatch('formula_cell_terms', this._processFormula(item, 'tokens'));\n queryObj.push(esMatchQuery);\n\n }else if (queryTypes[i] === 'E'){ // Element case\n queryObj.push(this._getESSimpleMatch('atom_labels_terms', item));\n\n }else if (queryTypes[i] === 'Q'){\n queryObj.push(item);\n }\n });\n\n return rootQueryObj;\n } // else\n } // _getESQueryFromSearchQuery_otherElements()\n\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\n return rootQueryObj;\n }\n\n\n _getESSimpleMatch(field, value){\n return {\n \"match\": { [field] : value }\n };\n }\n\n _getESTermsArray(field, value){\n return {\n \"terms\": { [field] : value }\n };\n }\n\n\n _getESOperatorMatch(field, elements, and = true){\n let elementsString = '';\n if (elements.length > 0) elementsString = elements.join(' ');\n\n return {\n \"match\": {\n [field]: {\n \"operator\": (and ? \"and\" : 'or'),\n \"query\": elementsString\n }\n }\n };\n }\n\n\n _getFieldESRange(field, valuesString){\n let data = valuesString.split(':');\n console.log('_getFieldESRange data', data);\n\n return {\n \"range\": {\n [field]: { \"gte\" : parseInt(data[0]), \"lte\" : parseInt(data[1]) }\n }\n };\n }\n\n\n\n\n _setTabSelectedStyles(element, value){\n /*\n element.style.fontWeight = (value ? 'bold' : 'normal');\n element.style.color = (value ? '#E56400' : '#777');\n element.style.borderColor = (value ? '#E56400' : '#777');\n */\n element.id = (value ? 'add-tab-selected' : '');\n }\n\n\n/*\n _showSearchBox(){\n if (!this.showingSearchBox){\n this.showingSearchBox = true;\n this.searchLine.style.visibility = 'visible';\n\n if (this.userGuidance) UserGuidance.setFinal();\n }\n }*/\n\n \n\n\n \n\n ///********* DELETE?\n showSearchResults(){\n /*\n this.searchPage.style.display= 'none';\n this.resultsPage.style.display= 'block';\n */\n //if (this.userGuidance) UserGuidance.show(false);\n }\n\n\n showSearchPage(){\n /*\n this.searchPage.style.display= 'block';\n this.resultsPage.style.display= 'none';\n\n if (this.userGuidance){\n setTimeout(() => {\n UserGuidance.init(this.addButtonsBox, this.elementTable.element,\n this.searchBox, this.propertiesBox.tabsElement);\n UserGuidance.show(true, this.currentTab === 'element',\n this.currentTab === 'props');\n }, 400);\n }\n */\n }\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\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.setAttribute(\"id\",'formula-box');\n this.element.innerHTML=\n `\n <div style=\"padding-bottom: 20px;\">\n<!--\n <input type=\"checkbox\" class=\"allow-other-elements\" value=\"\">\n Allow other elements\n <br>\n\n\n <input type=\"checkbox\" class=\"multiples-of-formula\" value=\"\">\n Include multiples of formula\n <br> -->\n\n <input type=\"text\" class=\"textfield-composition\"\n placeholder=\"Add formula to the search query above\" >\n <button class=\"adding-formula-btn\" disabled>Add to query</button>\n </div>\n `;\n this.formulaTextField = this.element.querySelector('.textfield-composition');\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?");
/***/ }),
......@@ -426,7 +426,7 @@ eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed un
/***/ ((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\");\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 this.currentOperator;\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 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 this.searchQuery = [];\n this.queryTypes = [];\n this.updateSearchQuery();\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\n getOptimadeQuery(allowOtherElements){\n\n console.log('getOptimadeQuery this.searchQuery', this.searchQuery)\n\n if (this.searchQuery.length === 0 || \n !isQueryWellFormed(this.searchQuery)) return '';\n\n else if (this.searchQuery.length === 1 && this.queryTypes[0] === 'MN'){ // material case\n return `material_name=\"${this.searchQuery[0]}\"`;\n\n }else{ // elements and formulas\n let searchQueryMod = [];\n let parOptimadeQueries = [];\n let openIndex = -1;\n let parCounter = 0;\n this.searchQuery.forEach( (item, i) => {\n\n if ( this.searchQuery[i] === '(' ){\n openIndex = i;\n }\n else if ( this.searchQuery[i] === ')' ){\n searchQueryMod.push('('+parCounter);\n const parSearchQuery = this.searchQuery.slice(openIndex+1, i);\n //console.log('parSearchQuery: ',i, parSearchQuery, getOptimadeFromSearchQuery(allowOtherElements, parSearchQuery));\n parOptimadeQueries.push(getOptimadeFromSearchQuery(allowOtherElements, parSearchQuery))\n openIndex = -1;\n parCounter++;\n\n }else if (openIndex < 0) searchQueryMod.push(item);\n });\n // console.log('FFFFFFFFF searchQueryMod: ', searchQueryMod, parOptimadeQueries, getOptimadeFromSearchQuery(allowOtherElements, searchQueryMod, parOptimadeQueries));\n\n\n //********* Not valid expressions\n\n // return getOptimadeFromSearchQuery(allowOtherElements, this.searchQuery);\n return getOptimadeFromSearchQuery(allowOtherElements, searchQueryMod, parOptimadeQueries);\n } \n\n\n function getOptimadeFromSearchQuery(allowOtherElements, searchQuery, optimadeSubqueries){\n\n let optimadeQuery = '';\n\n // Get the subqueries: parts of the query separated by ORs\n const subqueries = [];\n let currentSubquery = [];\n subqueries.push(currentSubquery);\n\n searchQuery.forEach( (item, i) => {\n\n if (searchQuery[i] === 'OR'){\n currentSubquery = []; // new subquery\n subqueries.push(currentSubquery);\n }else\n currentSubquery.push(item);\n\n });\n console.log(\"getOptimadeQuery subqueries: \", subqueries);\n\n // Subqueries are either elements or formulas separated by ANDs\n subqueries.forEach( (subquery, i) => {\n\n if (i > 0) optimadeQuery += ' OR '\n\n if (allowOtherElements){ // Inclusive search\n \n subquery.forEach( (item, i) => { // For every item in the subquery\n if (item.charAt(0) === '('){\n // console.log('OPPPPP', optimadeSubqueries[+item.charAt(1)])\n optimadeQuery += `(${optimadeSubqueries[+item.charAt(1)]})`\n }else if ( isElement(item) ){\n optimadeQuery += `elements HAS \"${item}\"`\n\n }else if ( item === 'AND' || item === 'NOT'){\n optimadeQuery += ' '+item+' ';\n \n }else{ // Formula\n let formula = new Formula(item);\n optimadeQuery += formula.getOptimadeSubquery(allowOtherElements);// optimadeQuery += ` formula=\"${this.searchQuery[i]}\"`\n }\n });\n\n }else{ // Exclusive search. For now only one formula or elements subqueries supported. Operator NOT doesn't make sense here\n\n // if (subquery.length === 1 && !isElement(subquery[0])){ // Only one formula\n if (!isElement(subquery[0])){ // formula(s)\n //optimadeQuery += `formula=\"${subquery[0]}\"`;\n let formula = new Formula(subquery[0]);\n optimadeQuery += formula.getOptimadeSubquery(allowOtherElements);\n\n }else{ // Several items, they all should be elements\n optimadeQuery += getOptimadeExclusiveANDSubquery(subquery);\n }\n }\n\n }); \n return optimadeQuery;\n }\n\n\n function isElement(item){\n return util.ELEMENTS.includes(item)\n }\n\n\n function getOptimadeExclusiveANDSubquery(subquery){ // this subquery is supposed to be form only for elements and ANDs separating\n let optSubquery = 'elements HAS ONLY ';\n subquery.forEach( (item) => {\n if (isElement(item)){//item !== 'AND'){ // check if element isntead ? \n optSubquery += `\"${item}\",`;\n }\n\n }); \n optSubquery = optSubquery.substring(0, optSubquery.length-1); \n return optSubquery;\n }\n \n\n function isQueryWellFormed(searchQuery){\n const openingParIndex = searchQuery.indexOf('(')\n const closingParIndex = searchQuery.indexOf(')')\n return searchQuery[searchQuery.length-1] !== 'NOT' && \n ((openingParIndex < 0) || // there are no paratheses or\n // they are in right position\n (openingParIndex >= 0 && openingParIndex < closingParIndex+1)) \n }\n\n }\n\n\n setBoolOperator(operator){\n this.currentOperator = operator;\n }\n\n\n updateSearchQuery(){\n let html= '';\n for (let i = 0; i < this.searchQuery.length; i++) {\n let type = this.queryTypes[i];\n\n if (type === 'S' || type === 'P')\n html+= `<span class=\"search-query-symbol\" > ${this.searchQuery[i]} </span>`;\n else\n html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false));\n }\n console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes);\n this.searchQueryBox.innerHTML = html;\n this.searchQueryChangeListener();\n }\n\n\n addItemInSearchQuery(item, type){\n this.searchQuery.push(item);\n this.queryTypes.push(type);\n }\n\n\n addTag(tag, type){\n // If the it's an element and is already in the query it's not inserted\n if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return;\n\n if ( this.searchQuery.length > 0\n && this.searchQuery[this.searchQuery.length-1] !== '('\n && this.searchQuery[this.searchQuery.length-1] !== 'NOT' )\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery(tag, type);\n this.updateSearchQuery();\n }\n\n\n addElements(elementArray){\n let index = elementArray.length;\n while (index--) {\n this.addTag(elementArray[index], 'E');\n }\n return true;\n }\n\n\n addParentheses(isOpen){\n\n if ( this.searchQuery.length > 0 && isOpen)\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery( (isOpen ? '(' : ')'), 'P');\n this.updateSearchQuery();\n }\n\n\n addNOT(){\n\n if ( this.searchQuery.length > 0)\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery( 'NOT', 'S');\n this.updateSearchQuery();\n }\n\n\n removeElementORFormulaInSearchQuery(item){\n //console.log(\" removeElementORFormulaInSearchQuery item: \",item, this.searchQuery.indexOf(item));\n \n // NOT being used let isMaterialName = (this.queryTypes[0] === 'MN');\n \n // spot the item and remove the item and the bool operator(s) related\n let itemIndex = this.searchQuery.indexOf(item);\n if (itemIndex >= 0){\n let i, elementsToRemove;\n /*\n if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the right\n console.log('itemIndex', itemIndex, this.searchQuery[itemIndex+1])\n if (this.searchQuery[itemIndex-1] === 'NOT'){\n i = itemIndex-1; elementsToRemove = 3;\n }else{ i = itemIndex; elementsToRemove = 2; }\n\n }else*/\n if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the left\n // console.log('itemIndex', itemIndex, this.searchQuery[itemIndex-1])\n if (this.searchQuery[itemIndex-1] === 'NOT'){\n i = itemIndex-2; elementsToRemove = 3;\n }else{ i = itemIndex-1; elementsToRemove = 2; }\n \n }else{ // case: (item)\n i = itemIndex; elementsToRemove = 1;\n }\n \n removeItemsFromSearchQuery(this, i, elementsToRemove); \n }\n\n // Remove the symbols before the first element/formula\n if (this.queryTypes[0] === 'S') { // AND , OR\n let elementsToRemove = 1;\n if (this.queryTypes[1] === 'S') elementsToRemove = 2; // NOT\n removeItemsFromSearchQuery(this, 0, elementsToRemove);\n }\n\n // Travese the array removing the unnecessary parethesis (only tested for one level nested)\n if ( this.searchQuery.indexOf('(') >= 0){ // Recursion\n\n for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed\n if ( this.searchQuery[i] === '(' ){\n if ( this.searchQuery[i+1] === ')'){ // '()' case\n removeItemsFromSearchQuery(this, i, 2);// this.searchQuery.splice(i, 2); this.queryTypes.splice(i, 2);\n }else if (this.searchQuery[i+2] === ')'){ // '(item)' case\n this.searchQuery.splice(i, 3, this.searchQuery[i+1]);\n this.queryTypes.splice(i, 3, this.queryTypes[i+1]);\n }\n }\n }\n }\n\n this.updateSearchQuery();\n\n if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)\n this.removeElementListener(item);\n }\n\n if (this.queryTypes.length === 0){ // The search query gets blank\n this.cleanSearchQueryListener();\n }\n\n return true;\n\n\n function removeItemsFromSearchQuery(self, start, delCount){\n self.searchQuery.splice(start, delCount);\n self.queryTypes.splice(start, delCount);\n }\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}\n\n\nclass Formula{\n\n constructor(formula) {\n\n this.formula = formula;\n this.formulaMap = this._parseFormula(formula);\n console.log('this.formulaMap: ', this.formulaMap);\n\n }\n\n\n _parseFormula(formula){\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 return map;\n }\n\n getOptimadeSubquery(allowOtherElements) {\n const fragments = []\n this.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n return 'formula HAS ' + (allowOtherElements ? 'ALL ' : 'ONLY ') + fragments.join(', ')\n }\n\n\n/*\n getReducedFormula(getTokens = true){\n let counter = 0;\n while ( !checkIfReduced(this.formulaMap) ){ // console.log('Reducing', this.formulaMap);\n let div = 1;\n if (isDivisibleBy(this.formulaMap, 2)) div = 2;\n else if (isDivisibleBy(this.formulaMap, 3)) div = 3;\n else if (isDivisibleBy(this.formulaMap, 5)) div = 5;\n else if (isDivisibleBy(this.formulaMap, 7)) div = 7;\n else if (isDivisibleBy(this.formulaMap, 11)) div = 11;\n\n this.formulaMap.forEach( (value, key) => {\n this.formulaMap.set(key, (value/div));\n });\n //console.log('Reducing DIV', this.formulaMap);\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 this.formulaMap.forEach( (value, key) => tokens.push(key+value) );\n }else{\n let sortedElements = this._sortElements( Array.from( this.formulaMap.keys() ) );\n sortedElements.forEach( element => {\n canonicalFormula += element+this.formulaMap.get(element);\n //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));\n });\n }\n\n console.log('_reduceFormula RETURN: ', this.formulaMap, tokens, canonicalFormula);\n return (getTokens ? tokens : canonicalFormula);\n }\n*/\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\");\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 this.currentOperator;\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 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 this.searchQuery = [];\n this.queryTypes = [];\n this.updateSearchQuery();\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 /**\n * Given the final query, this function uses boolean algebra to reduce it\n * into a flattened list of sets combined with AND or OR.\n */\n reduceQuery(query, types) {\n\n // For combining elements connected by AND into a list\n function combineANDIn(query, types) {\n const newQuery = []\n const newTypes = []\n for (let i=0; i < query.length;) {\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n \n if ((t1 === \"E\" || t1 === \"EL\" ) && (t3 === \"E\" || t3 === \"EL\" ) && q2 === \"AND\") {\n let active = true\n let elements = [q1, q3]\n i = i + 3\n while(active) {\n const q4 = query[i];\n const t4 = types[i];\n const q5 = query[i+1];\n const t5 = types[i+1];\n if (q4 === \"AND\" && (t5 === \"E\" || t5 === \"EL\" )) {\n elements.push(q5)\n i += 2\n } else {\n active = false\n newQuery.push(elements.join(\", \"))\n newTypes.push(\"EL\")\n }\n }\n } else {\n newQuery.push(q1)\n newTypes.push(t1)\n i += 1\n }\n }\n return [newQuery, newTypes]\n }\n\n // For removing unnecessary quotes\n function removeParenthesis(query, types) {\n //console.log(query)\n //console.log(types)\n let removed = []\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n if (q1 === \"(\") {\n //console.log(\"Start found!\")\n let index = 0;\n for (let j=i+1; j < query.length; ++j) {\n const q2 = query[j];\n const t2 = types[j];\n if (q2 === \"(\") {\n index += 1;\n }\n if (q2 === \")\") {\n //console.log(\"End found!\")\n if (index != 0) {\n index -= 1;\n } else {\n const contents = query.slice(i+1, j)\n //console.log(\"PARENTHESIS VALUES\")\n //console.log(contents)\n //console.log(i+1, j)\n const tp = types.slice(i+1, j)\n //console.log(\"PARENTHESIS TYPES\")\n //console.log(tp)\n let noop = true\n for (let t of tp) {\n if (t === \"S\") {\n noop = false\n break\n }\n }\n if (noop) {\n removed.push(j)\n removed.push(i)\n }\n break\n }\n }\n }\n }\n }\n\n // Remove found parentheses\n removed = removed.sort((a, b) => b - a);\n //console.log(query.join(\" \"));\n //console.log(removed);\n for (let j=0; j < removed.length; ++j) {\n query.splice(removed[j], 1);\n types.splice(removed[j], 1);\n }\n //console.log(query.join(\" \"));\n return [query, types]\n }\n\n // For combining elements connected by AND into a list\n function combineANDOut(query, types) {\n let newQuery = query\n let newTypes = types\n\n let combined = false\n for (let i=0; i < query.length; ++i) {\n\n // Look for AND surrounded by one or more parenthesis\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n if (q2 === \"AND\" && (q1 === \")\" || q3 === \"(\" )) {\n combined = true\n\n // Get left hand and right hand side expressions\n let lhsQ\n let lhsT\n let rhsQ\n let rhsT\n lhsQ = query.slice(0, i+1)\n lhsT = types.slice(0, i+1)\n if (q1 !== \")\") {\n lhsQ.unshift(\"(\")\n lhsT.unshift(\"P\")\n lhsQ.push(\")\")\n lhsT.push(\"P\")\n }\n rhsQ = query.slice(i+2, query.length)\n rhsT = types.slice(i+2, query.length)\n if (q3 !== \"(\") {\n rhsQ.unshift(\"(\")\n rhsT.unshift(\"P\")\n rhsQ.push(\")\")\n rhsT.push(\"P\")\n }\n\n // Split the rhs into parts separated by OR\n const rhsParts = []\n let prevPos = 1\n for (let j=1; j < rhsQ.length - 1; ++j) {\n const sq1 = rhsQ[j];\n if (sq1 === \"OR\") {\n rhsParts.push([rhsQ.slice(prevPos, j), rhsT.slice(prevPos, j)])\n prevPos = j+1\n } else if (j === rhsQ.length - 2) {\n rhsParts.push([rhsQ.slice(prevPos, j+1), rhsT.slice(prevPos, j+1)])\n }\n }\n\n // Combine lhs with the different parts of rhs\n let comboQ = []\n let comboT = []\n for (let j=0; j < rhsParts.length; ++j) {\n let icomboQ = []\n let icomboT = []\n icomboQ = icomboQ.concat(rhsParts[j][0])\n icomboT = icomboT.concat(rhsParts[j][1])\n icomboQ.push(\"AND\")\n icomboT.push(\"S\")\n icomboQ = icomboQ.concat(lhsQ)\n icomboT = icomboT.concat(lhsT)\n if (j === 0) {\n if (rhsParts.length > 1) {\n comboQ.push(\"(\")\n comboT.push(\"P\")\n }\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n if (rhsParts.length > 1) {\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n } else {\n comboQ.push(\"OR\")\n comboT.push(\"S\")\n comboQ.push(\"(\")\n comboT.push(\"P\")\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n }\n\n console.log(newQuery.join(\" \"));\n newQuery = comboQ\n newTypes = comboT\n break\n }\n }\n return [newQuery, newTypes, combined]\n }\n \n // Merge\n function merge(query, types) {\n\n // Remove unnecessary parenthesis\n [query, types] = removeParenthesis(query, types);\n\n // Combined all elements connected with AND\n [query, types] = combineANDIn(query, types);\n\n // Combine parenthesis statements\n let combined\n [query, types, combined] = combineANDOut(query, types);\n\n // If a combination was done, run recursively until no combination is\n // done\n if (combined) {\n [query, types] = merge(query, types)\n } else {\n [query, types] = removeParenthesis(query, types);\n [query, types] = combineANDIn(query, types);\n }\n return [query, types]\n }\n [query, types] = merge(query, types);\n console.log(query.join(\" \"));\n }\n\n\n getOptimadeQuery(allowOtherElements){\n\n this.reduceQuery(this.searchQuery, this.queryTypes)\n\n console.log('getOptimadeQuery this.searchQuery', this.searchQuery)\n\n if (this.searchQuery.length === 0 || \n !isQueryWellFormed(this.searchQuery)) return '';\n\n else if (this.searchQuery.length === 1 && this.queryTypes[0] === 'MN'){ // material case\n return `material_name=\"${this.searchQuery[0]}\"`;\n\n }else{ // elements and formulas\n let searchQueryMod = [];\n let parOptimadeQueries = [];\n let openIndex = -1;\n let parCounter = 0;\n this.searchQuery.forEach( (item, i) => {\n\n if ( this.searchQuery[i] === '(' ){\n openIndex = i;\n }\n else if ( this.searchQuery[i] === ')' ){\n searchQueryMod.push('('+parCounter);\n const parSearchQuery = this.searchQuery.slice(openIndex+1, i);\n //console.log('parSearchQuery: ',i, parSearchQuery, getOptimadeFromSearchQuery(allowOtherElements, parSearchQuery));\n parOptimadeQueries.push(getOptimadeFromSearchQuery(allowOtherElements, parSearchQuery))\n openIndex = -1;\n parCounter++;\n\n }else if (openIndex < 0) searchQueryMod.push(item);\n });\n // console.log('FFFFFFFFF searchQueryMod: ', searchQueryMod, parOptimadeQueries, getOptimadeFromSearchQuery(allowOtherElements, searchQueryMod, parOptimadeQueries));\n\n\n //********* Not valid expressions\n\n // return getOptimadeFromSearchQuery(allowOtherElements, this.searchQuery);\n return getOptimadeFromSearchQuery(allowOtherElements, searchQueryMod, parOptimadeQueries);\n } \n\n\n function getOptimadeFromSearchQuery(allowOtherElements, searchQuery, optimadeSubqueries){\n\n let optimadeQuery = '';\n\n // Get the subqueries: parts of the query separated by ORs\n const subqueries = [];\n let currentSubquery = [];\n subqueries.push(currentSubquery);\n\n searchQuery.forEach( (item, i) => {\n\n if (searchQuery[i] === 'OR'){\n currentSubquery = []; // new subquery\n subqueries.push(currentSubquery);\n }else\n currentSubquery.push(item);\n\n });\n console.log(\"getOptimadeQuery subqueries: \", subqueries);\n\n // Subqueries are either elements or formulas separated by ANDs\n subqueries.forEach( (subquery, i) => {\n\n if (i > 0) optimadeQuery += ' OR '\n\n if (allowOtherElements){ // Inclusive search\n \n subquery.forEach( (item, i) => { // For every item in the subquery\n if (item.charAt(0) === '('){\n // console.log('OPPPPP', optimadeSubqueries[+item.charAt(1)])\n optimadeQuery += `(${optimadeSubqueries[+item.charAt(1)]})`;\n\n }else if ( item === 'AND' || item === 'NOT'){\n optimadeQuery += ' '+item+' ';\n\n }else if ( isElement(item) ){\n optimadeQuery += `elements HAS \"${item}\"`\n \n }else{ // Formula\n let formula = new Formula(item);\n optimadeQuery += formula.getOptimadeSubquery(allowOtherElements);// optimadeQuery += ` formula=\"${this.searchQuery[i]}\"`\n }\n });\n\n }else{ // Exclusive search. For now only one formula or elements subqueries supported. Operator NOT doesn't make sense here\n\n const items = [];\n const notItems = [];\n let notItem = false;\n let areElements = false;\n subquery.forEach( (item) => {\n if (isElement(item)) areElements = true;\n if (item === 'NOT'){\n notItem = true;\n }else if (item !== 'AND'){ // item = element or formula\n if (notItem){\n notItem = false;\n if (areElements) notItems.push(`\"${item}\"`);\n else // formula\n notItems.push(new Formula(item).getFragments());\n\n }else{\n if (areElements) items.push(`\"${item}\"`);\n else // formula\n items.push(new Formula(item).getFragments());\n }\n \n } \n }); \n // console.log('Exclusive search: ', items)\n optimadeQuery += (areElements ? 'elements' : 'formula') + ' HAS ONLY '+ items.join(', ');\n\n }\n\n }); \n return optimadeQuery;\n }\n\n\n function isElement(item){\n return util.ELEMENTS.includes(item)\n }\n\n/* NOt used anymore??\n function getOptimadeElementExclusiveANDSubquery(subquery){ \n const elements = []\n subquery.forEach( (item) => {\n if (isElement(item)) elements.push(`\"${item}\"`);\n }); \n return 'elements HAS ONLY '+elements.join(', ');\n }\n \n\n function getOptimadeFormulaExclusiveANDSubquery(subquery){ \n const fragments = [];\n subquery.forEach( (item) => {\n if (!isElement(item) && item !== 'AND' && item !== 'NOT'){// Is formula\n fragments.push(new Formula(item).getFragments());\n }\n }); \n return 'formula HAS ONLY '+fragments.join(', ');\n }\n */\n\n function isQueryWellFormed(searchQuery){\n const openingParIndex = searchQuery.indexOf('(')\n const closingParIndex = searchQuery.indexOf(')')\n return searchQuery[searchQuery.length-1] !== 'NOT' && \n ((openingParIndex < 0) || // there are no paratheses or\n // they are in right position\n (openingParIndex >= 0 && openingParIndex < closingParIndex+1)) \n }\n\n }\n\n\n setBoolOperator(operator){\n this.currentOperator = operator;\n }\n\n\n updateSearchQuery(){\n let html= '';\n for (let i = 0; i < this.searchQuery.length; i++) {\n let type = this.queryTypes[i];\n\n if (type === 'S' || type === 'P')\n html+= `<span class=\"search-query-symbol\" > ${this.searchQuery[i]} </span>`;\n else\n html+= getTagHtml(this.searchQuery[i], ( type === 'F' ? true : false));\n }\n console.log('this.updateSearchQuery: ', this.searchQuery ,this.queryTypes);\n this.searchQueryBox.innerHTML = html;\n if (this.searchQueryChangeListener) this.searchQueryChangeListener();\n }\n\n\n addItemInSearchQuery(item, type){\n this.searchQuery.push(item);\n this.queryTypes.push(type);\n }\n\n\n addTag(tag, type){\n // If the it's an element and is already in the query it's not inserted\n if (type === 'E' && this.searchQuery.indexOf(tag) >= 0) return;\n\n if ( this.searchQuery.length > 0\n && this.searchQuery[this.searchQuery.length-1] !== '('\n && this.searchQuery[this.searchQuery.length-1] !== 'NOT' )\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery(tag, type);\n this.updateSearchQuery();\n }\n\n\n addElements(elementArray){\n let index = elementArray.length;\n while (index--) {\n this.addTag(elementArray[index], 'E');\n }\n return true;\n }\n\n\n addParentheses(isOpen){\n\n if ( this.searchQuery.length > 0 && isOpen)\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery( (isOpen ? '(' : ')'), 'P');\n this.updateSearchQuery();\n }\n\n\n addNOT(){\n\n if ( this.searchQuery.length > 0)\n this.addItemInSearchQuery(this.currentOperator, 'S');\n this.addItemInSearchQuery( 'NOT', 'S');\n this.updateSearchQuery();\n }\n\n\n removeElementORFormulaInSearchQuery(item){\n //console.log(\" removeElementORFormulaInSearchQuery item: \",item, this.searchQuery.indexOf(item));\n \n // NOT being used let isMaterialName = (this.queryTypes[0] === 'MN');\n \n // spot the item and remove the item and the bool operator(s) related\n let itemIndex = this.searchQuery.indexOf(item);\n if (itemIndex >= 0){\n let i, elementsToRemove;\n /*\n if (this.queryTypes[itemIndex+1] === 'S'){ // bool operator on the right\n console.log('itemIndex', itemIndex, this.searchQuery[itemIndex+1])\n if (this.searchQuery[itemIndex-1] === 'NOT'){\n i = itemIndex-1; elementsToRemove = 3;\n }else{ i = itemIndex; elementsToRemove = 2; }\n\n }else*/\n if (this.queryTypes[itemIndex-1] === 'S'){ // bool operator on the left\n // console.log('itemIndex', itemIndex, this.searchQuery[itemIndex-1])\n if (this.searchQuery[itemIndex-1] === 'NOT'){\n i = itemIndex-2; elementsToRemove = 3;\n }else{ i = itemIndex-1; elementsToRemove = 2; }\n \n }else{ // case: (item)\n i = itemIndex; elementsToRemove = 1;\n }\n \n removeItemsFromSearchQuery(this, i, elementsToRemove); \n }\n\n // Remove the symbols before the first element/formula\n if (this.queryTypes[0] === 'S') { // AND , OR\n let elementsToRemove = 1;\n if (this.queryTypes[1] === 'S') elementsToRemove = 2; // NOT\n removeItemsFromSearchQuery(this, 0, elementsToRemove);\n }\n\n // Travese the array removing the unnecessary parethesis (only tested for one level nested)\n if ( this.searchQuery.indexOf('(') >= 0){ // Recursion\n\n for (let i = 0; i < this.searchQuery.length; i++) { // dangerous: modifing a array being traversed\n if ( this.searchQuery[i] === '(' ){\n if ( this.searchQuery[i+1] === ')'){ // '()' case\n removeItemsFromSearchQuery(this, i, 2);// this.searchQuery.splice(i, 2); this.queryTypes.splice(i, 2);\n }else if (this.searchQuery[i+2] === ')'){ // '(item)' case\n this.searchQuery.splice(i, 3, this.searchQuery[i+1]);\n this.queryTypes.splice(i, 3, this.queryTypes[i+1]);\n }\n }\n }\n }\n\n this.updateSearchQuery();\n\n if (util.ELEMENTS.indexOf(item) >= 0){ // It's an element (being removed)\n this.removeElementListener(item);\n }\n\n if (this.queryTypes.length === 0){ // The search query gets blank\n this.cleanSearchQueryListener();\n }\n\n return true;\n\n\n function removeItemsFromSearchQuery(self, start, delCount){\n self.searchQuery.splice(start, delCount);\n self.queryTypes.splice(start, delCount);\n }\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}\n\n\nclass Formula{\n\n constructor(formula) {\n\n this.formula = formula;\n this.formulaMap = this._parseFormula(formula);\n console.log('this.formulaMap: ', this.formulaMap);\n\n }\n\n\n _parseFormula(formula){\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 return map;\n }\n\n getOptimadeSubquery(allowOtherElements) {\n const fragments = []\n this.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n return 'formula HAS ' + (allowOtherElements ? 'ALL ' : 'ONLY ') + fragments.join(', ')\n }\n\n getFragments() {\n const fragments = []\n this.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment);\n })\n return fragments;\n }\n\n\n/*\n getReducedFormula(getTokens = true){\n let counter = 0;\n while ( !checkIfReduced(this.formulaMap) ){ // console.log('Reducing', this.formulaMap);\n let div = 1;\n if (isDivisibleBy(this.formulaMap, 2)) div = 2;\n else if (isDivisibleBy(this.formulaMap, 3)) div = 3;\n else if (isDivisibleBy(this.formulaMap, 5)) div = 5;\n else if (isDivisibleBy(this.formulaMap, 7)) div = 7;\n else if (isDivisibleBy(this.formulaMap, 11)) div = 11;\n\n this.formulaMap.forEach( (value, key) => {\n this.formulaMap.set(key, (value/div));\n });\n //console.log('Reducing DIV', this.formulaMap);\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 this.formulaMap.forEach( (value, key) => tokens.push(key+value) );\n }else{\n let sortedElements = this._sortElements( Array.from( this.formulaMap.keys() ) );\n sortedElements.forEach( element => {\n canonicalFormula += element+this.formulaMap.get(element);\n //canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));\n });\n }\n\n console.log('_reduceFormula RETURN: ', this.formulaMap, tokens, canonicalFormula);\n return (getTokens ? tokens : canonicalFormula);\n }\n*/\n\n}\n\n// EXPORTS\nmodule.exports = SearchBox;\n\n\n//# sourceURL=webpack:///./src/search-mod/SearchBox.view.js?");
/***/ })
......
......@@ -81,9 +81,237 @@ class SearchBox{
}
/**
* Given the final query, this function uses boolean algebra to reduce it
* into a flattened list of sets combined with AND or OR.
*/
reduceQuery(query, types) {
// For combining elements connected by AND into a list
function combineANDIn(query, types) {
const newQuery = []
const newTypes = []
for (let i=0; i < query.length;) {
const q1 = query[i];
const t1 = types[i];
const q2 = query[i+1];
const t2 = types[i+1];
const q3 = query[i+2];
const t3 = types[i+2];
if ((t1 === "E" || t1 === "EL" ) && (t3 === "E" || t3 === "EL" ) && q2 === "AND") {
let active = true
let elements = [q1, q3]
i = i + 3
while(active) {
const q4 = query[i];
const t4 = types[i];
const q5 = query[i+1];
const t5 = types[i+1];
if (q4 === "AND" && (t5 === "E" || t5 === "EL" )) {
elements.push(q5)
i += 2
} else {
active = false
newQuery.push(elements.join(", "))
newTypes.push("EL")
}
}
} else {
newQuery.push(q1)
newTypes.push(t1)
i += 1
}
}
return [newQuery, newTypes]
}
// For removing unnecessary quotes
function removeParenthesis(query, types) {
//console.log(query)
//console.log(types)
let removed = []
for (let i=0; i < query.length; ++i) {
const q1 = query[i];
const t1 = types[i];
if (q1 === "(") {
//console.log("Start found!")
let index = 0;
for (let j=i+1; j < query.length; ++j) {
const q2 = query[j];
const t2 = types[j];
if (q2 === "(") {
index += 1;
}
if (q2 === ")") {
//console.log("End found!")
if (index != 0) {
index -= 1;
} else {
const contents = query.slice(i+1, j)
//console.log("PARENTHESIS VALUES")
//console.log(contents)
//console.log(i+1, j)
const tp = types.slice(i+1, j)
//console.log("PARENTHESIS TYPES")
//console.log(tp)
let noop = true
for (let t of tp) {
if (t === "S") {
noop = false
break
}
}
if (noop) {
removed.push(j)
removed.push(i)
}
break
}
}
}
}
}
// Remove found parentheses
removed = removed.sort((a, b) => b - a);
//console.log(query.join(" "));
//console.log(removed);
for (let j=0; j < removed.length; ++j) {
query.splice(removed[j], 1);
types.splice(removed[j], 1);
}
//console.log(query.join(" "));
return [query, types]
}
// For combining elements connected by AND into a list
function combineANDOut(query, types) {
let newQuery = query
let newTypes = types
let combined = false
for (let i=0; i < query.length; ++i) {
// Look for AND surrounded by one or more parenthesis
const q1 = query[i];
const t1 = types[i];
const q2 = query[i+1];
const t2 = types[i+1];
const q3 = query[i+2];
const t3 = types[i+2];
if (q2 === "AND" && (q1 === ")" || q3 === "(" )) {
combined = true
// Get left hand and right hand side expressions
let lhsQ
let lhsT
let rhsQ
let rhsT
lhsQ = query.slice(0, i+1)
lhsT = types.slice(0, i+1)
if (q1 !== ")") {
lhsQ.unshift("(")
lhsT.unshift("P")
lhsQ.push(")")
lhsT.push("P")
}
rhsQ = query.slice(i+2, query.length)
rhsT = types.slice(i+2, query.length)
if (q3 !== "(") {
rhsQ.unshift("(")
rhsT.unshift("P")
rhsQ.push(")")
rhsT.push("P")
}
// Split the rhs into parts separated by OR
const rhsParts = []
let prevPos = 1
for (let j=1; j < rhsQ.length - 1; ++j) {
const sq1 = rhsQ[j];
if (sq1 === "OR") {
rhsParts.push([rhsQ.slice(prevPos, j), rhsT.slice(prevPos, j)])
prevPos = j+1
} else if (j === rhsQ.length - 2) {
rhsParts.push([rhsQ.slice(prevPos, j+1), rhsT.slice(prevPos, j+1)])
}
}
// Combine lhs with the different parts of rhs
let comboQ = []
let comboT = []
for (let j=0; j < rhsParts.length; ++j) {
let icomboQ = []
let icomboT = []
icomboQ = icomboQ.concat(rhsParts[j][0])
icomboT = icomboT.concat(rhsParts[j][1])
icomboQ.push("AND")
icomboT.push("S")
icomboQ = icomboQ.concat(lhsQ)
icomboT = icomboT.concat(lhsT)
if (j === 0) {
if (rhsParts.length > 1) {
comboQ.push("(")
comboT.push("P")
}
comboQ = comboQ.concat(icomboQ)
comboT = comboT.concat(icomboT)
if (rhsParts.length > 1) {
comboQ.push(")")
comboT.push("P")
}
} else {
comboQ.push("OR")
comboT.push("S")
comboQ.push("(")
comboT.push("P")
comboQ = comboQ.concat(icomboQ)
comboT = comboT.concat(icomboT)
comboQ.push(")")
comboT.push("P")
}
}
newQuery = comboQ
newTypes = comboT
break
}
}
return [newQuery, newTypes, combined]
}
// Merge
function merge(query, types) {
// Remove unnecessary parenthesis
[query, types] = removeParenthesis(query, types);
// Combined all elements connected with AND
[query, types] = combineANDIn(query, types);
// Combine parenthesis statements
let combined
[query, types, combined] = combineANDOut(query, types);
// If a combination was done, run recursively until no combination is
// done
if (combined) {
[query, types] = merge(query, types)
} else {
[query, types] = removeParenthesis(query, types);
[query, types] = combineANDIn(query, types);
}
return [query, types]
}
[query, types] = merge(query, types);
console.log(query.join(" "));
}
getOptimadeQuery(allowOtherElements){
this.reduceQuery(this.searchQuery, this.queryTypes)
console.log('getOptimadeQuery this.searchQuery', this.searchQuery)
if (this.searchQuery.length === 0 ||
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment