Commit 5fe5eec5 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Added missing parenthesis to OptimadeTranslator, fixed margin issue with MaterialList.

parent c3408954
Pipeline #93911 skipped with stage
......@@ -425,7 +425,7 @@ eval("let util = __webpack_require__(/*! ../common/util.js */ \"./src/common/uti
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component implements the list of materials found in the search\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\n//const RESULTS_PER_PAGE = 20;\n\n\nclass MaterialList {\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'MaterialList';\n\n // state \n this.visible = false;\n this.noResults = true;\n this.matMap = new Map();\n //this.currentSystemType = 'bulk';\n this.optimadeQuery = null;\n \n this.noResultsBox = document.createElement('div');\n this.noResultsBox.innerHTML = 'NO RESULTS FOUND';\n this.element.append(this.noResultsBox);\n\n this.matListWrapper = document.createElement('div');\n this.element.append(this.matListWrapper);\n this.pagControl = new PaginationControl();\n this.matListWrapper.append(this.pagControl.element);\n this.pagControl.setPrevPageListener( page => {\n this._search(page);\n });\n this.pagControl.setNextPageListener( page => {\n this._search(page);\n })\n\n this.matListContainer = new MatListContainer();\n this.matListWrapper.append(this.matListContainer.element);\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._render();\n }\n\n\n invalidateSearch(){\n this.visible = false;\n this._render();\n }\n\n\n initSearch(optimadeQuery){\n\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show();\n\n let reqJson = {\n query: this.optimadeQuery,\n search_by: {}\n };\n if (page) reqJson.search_by = { page: page}\n\n // Add the restricted option from the checkbox\n let restrictedEl = document.getElementById('restricted-search');\n reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');\n console.log('SEARCHING: ', reqJson );\n\n 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?");
eval("\n/**\n * Copyright 2016-2018 Iker Hurtado\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n\n /*\n This component implements the list of materials found in the search\n */\n\n\n\nlet util = __webpack_require__(/*! ../common/util.js */ \"./src/common/util.js\");\nlet InfoSys = __webpack_require__(/*! ../common/InfoSys.js */ \"./src/common/InfoSys.js\");\nlet LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */ \"./src/common/LoadingPopup.js\");\n\n//const RESULTS_PER_PAGE = 20;\n\n\nclass MaterialList {\n\n constructor(){\n this.element = document.createElement('div');\n this.element.className = 'MaterialList';\n\n // state \n this.visible = false;\n this.noResults = true;\n this.matMap = new Map();\n //this.currentSystemType = 'bulk';\n this.optimadeQuery = null;\n \n this.noResultsBox = document.createElement('div');\n this.noResultsBox.style = 'text-align: center; font-weight: bold'\n this.noResultsBox.innerHTML = 'NO RESULTS FOUND';\n this.element.append(this.noResultsBox);\n\n this.matListWrapper = document.createElement('div');\n this.element.append(this.matListWrapper);\n this.pagControl = new PaginationControl();\n this.matListWrapper.append(this.pagControl.element);\n this.pagControl.setPrevPageListener( page => {\n this._search(page);\n });\n this.pagControl.setNextPageListener( page => {\n this._search(page);\n })\n\n this.matListContainer = new MatListContainer();\n this.matListWrapper.append(this.matListContainer.element);\n }\n\n\n attachAndSetEvents(element){\n element.appendChild(this.element);\n this._render();\n }\n\n\n invalidateSearch(){\n this.visible = false;\n this._render();\n }\n\n\n initSearch(optimadeQuery){\n\n this.optimadeQuery = optimadeQuery;\n this._search();\n }\n\n _search(page){\n //this.resultsContainer.style.visibility = 'hidden';\n this.matMap.clear();\n\n LoadingPopup.show();\n\n let reqJson = {\n query: this.optimadeQuery,\n search_by: {}\n };\n if (page) reqJson.search_by = { page: page}\n\n // Add the restricted option from the checkbox\n let restrictedEl = document.getElementById('restricted-search');\n reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');\n console.log('SEARCHING: ', reqJson );\n\n 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 .catch(error => {\n document.querySelector('.user-msg-box').innerHTML = 'Query expression NOT valid';\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 document.querySelector('.user-msg-box').innerHTML = this.noResults ? 'No search results' : 'See result list below';\n }else\n document.querySelector('.user-msg-box').innerHTML = '';\n\n \n }\n\n\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?");
/***/ }),
......@@ -457,7 +457,7 @@ eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed un
\**********************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
eval("let Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n\n/**\n * This class is used to translate the GUI query language into a corresponding\n * Optimade query.\n */\nclass OptimadeTranslator {\n \n constructor() {\n }\n\n /**\n * Translates the given GUI query into the corresponding optimade query\n * string.\n */\n translate(queryIn, typesIn, allowOtherElements) {\n // Make a copy so that this function becomes pure.\n let query = [...queryIn];\n let types = [...typesIn];\n\n // Add escaped quotes to all elements\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n query[i] = `\\\"${q}\\\"`\n }\n }\n\n // Add escaped quotes to formulas\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"F\") {\n const formula = new Formula(q)\n const fragments = []\n formula.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n query[i] = fragments.join(', ')\n }\n }\n\n // Construct the reduced expression\n [query, types] = this.simplify(query, types);\n\n // Convert into optimade syntax\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n if (allowOtherElements) {\n q = \"elements HAS ALL \" + q\n } else {\n q = \"elements HAS ONLY \" + q\n }\n } else if (t === \"F\") {\n if (allowOtherElements) {\n q = \"formula HAS ALL \" + q\n } else {\n q = \"formula HAS ONLY \" + q\n }\n }\n query[i] = q;\n }\n\n let res = query.join(\" \")\n console.log(\"FINAL OPTIMADE QUERY:\")\n console.log(res)\n return res\n }\n\n /**\n * For combining elements/formulas connected by AND into a single list.\n */\n combineANDIn(query, types) {\n const newQuery = []\n const newTypes = []\n for (let i=0; i < query.length;) {\n const q0 = query[i-1];\n const t0 = types[i-1];\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\" && t3 === \"E\") || (t1 === \"F\" && t3 === \"F\")) && q2 === \"AND\" && q0 !== \"NOT\") {\n let active = true\n let type = t3\n let joined = [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 === type) {\n joined.push(q5)\n i += 2\n } else {\n active = false\n newQuery.push(joined.join(\", \"))\n newTypes.push(type)\n }\n }\n } else {\n newQuery.push(q1)\n newTypes.push(t1)\n i += 1\n }\n }\n return [newQuery, newTypes]\n }\n\n /**\n * For finding parenthesis pairs\n */\n findPairs(query, types) {\n const pairs = new Map();\n\n // Find list of indices where parenthesis should be removed\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 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 if (index != 0) {\n index -= 1;\n } else {\n pairs.set(i, j)\n pairs.set(j, i)\n break\n }\n }\n }\n }\n }\n return pairs;\n }\n\n /**\n * For removing unnecessary parenthesis\n */\n removeParenthesis(query, types) {\n // Find list of indices where parenthesis should be removed\n const pairs = this.findPairs(query, 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 let p = pairs.get(i)\n\n // Remove outer parentheses\n if (i === 0 && p == query.length-1) {\n removed.push(p)\n removed.push(i)\n // Remove parentheses that do not contain multiple operations\n } else {\n const contents = query.slice(i+1, p)\n const tp = types.slice(i+1, p)\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(p)\n removed.push(i)\n }\n }\n }\n }\n\n // Find repeated quotes and reduce them to single one\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n if (q1 === \"(\" && q1 === \"(\") {\n let p1 = pairs.get(i)\n let p2 = pairs.get(i+1)\n if (p2 == p1 - 1) {\n removed.push(i)\n removed.push(p1)\n }\n }\n }\n\n // Remove found parentheses\n removed = removed.sort((a, b) => b - a);\n for (let j=0; j < removed.length; ++j) {\n query.splice(removed[j], 1);\n types.splice(removed[j], 1);\n }\n return [query, types]\n }\n\n /**\n * Combines AND statements where either one or both of the statements has a\n * parenthesis. These statements are refactored using distributive laws of\n * set theory:\n * A AND (B OR C) == (A AND B) OR (A AND C)\n * in order to see if there are lists of elements that should be joined.\n */\n combineANDOut(query, types) {\n let newQuery = query\n let newTypes = types\n const pairs = this.findPairs(query, 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 let rhsEnd\n let lhsStart\n if (q1 === \")\") {\n lhsStart = pairs.get(i)\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n } else {\n if (query[i-1] === \"NOT\") {\n lhsStart = i-1\n } else {\n lhsStart = i\n }\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n lhsQ.unshift(\"(\")\n lhsT.unshift(\"P\")\n lhsQ.push(\")\")\n lhsT.push(\"P\")\n }\n if (q3 === \"(\") {\n rhsEnd = pairs.get(i+2)+1\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n } else {\n if (query[i+2] === \"NOT\") {\n rhsEnd = i+4\n } else {\n rhsEnd = i+3\n }\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\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 comboQ.unshift(\"(\")\n comboT.unshift(\"P\")\n comboQ.push(\")\")\n comboT.push(\"P\")\n newQuery = query.slice(0, lhsStart).concat(comboQ).concat(query.slice(rhsEnd, query.length))\n newTypes = types.slice(0, lhsStart).concat(comboT).concat(types.slice(rhsEnd, query.length))\n break\n }\n }\n return [newQuery, newTypes, combined]\n }\n \n /**\n * Recursive function for simplifying the query into a form that can be\n * correctly interpreted by Optimade.\n */\n simplify(query, types) {\n\n // Remove unnecessary parenthesis, combine all elements connected with\n // AND, and remove unnecessary parenthesis again.\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n\n // Combine parenthesis statements\n let combined\n [query, types, combined] = this.combineANDOut(query, types);\n\n // If a combination was done, run recursively until no combination is\n // done\n if (combined) {\n [query, types] = this.simplify(query, types)\n // After the last step perform on final cleanup\n } else {\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n }\n return [query, types]\n }\n}\n\n// EXPORTS\nmodule.exports = OptimadeTranslator;\n\n\n//# sourceURL=webpack:///./src/search-mod/OptimadeTranslator.js?");
eval("let Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n\n/**\n * This class is used to translate the GUI query language into a corresponding\n * Optimade query.\n */\nclass OptimadeTranslator {\n \n constructor() {\n }\n\n /**\n * Translates the given GUI query into the corresponding optimade query\n * string.\n */\n translate(queryIn, typesIn, allowOtherElements) {\n // Make a copy so that this function becomes pure.\n let query = [...queryIn];\n let types = [...typesIn];\n\n // Add escaped quotes to all elements\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n query[i] = `\\\"${q}\\\"`\n }\n }\n\n // Add escaped quotes to formulas\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"F\") {\n const formula = new Formula(q)\n const fragments = []\n formula.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n query[i] = fragments.join(', ')\n }\n }\n\n // Construct the reduced expression\n [query, types] = this.simplify(query, types);\n\n // Convert into optimade syntax\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n if (allowOtherElements) {\n q = \"elements HAS ALL \" + q\n } else {\n q = \"elements HAS ONLY \" + q\n }\n } else if (t === \"F\") {\n if (allowOtherElements) {\n q = \"formula HAS ALL \" + q\n } else {\n q = \"formula HAS ONLY \" + q\n }\n }\n query[i] = q;\n }\n\n // Surround with parenthesis so that this part will not get incorrectly\n // mixed with the filters due to operator precedence \n let res = \"(\" + query.join(\" \") + \")\"\n console.log(\"FINAL OPTIMADE QUERY:\")\n console.log(res)\n return res\n }\n\n /**\n * For combining elements/formulas connected by AND into a single list.\n */\n combineANDIn(query, types) {\n const newQuery = []\n const newTypes = []\n for (let i=0; i < query.length;) {\n const q0 = query[i-1];\n const t0 = types[i-1];\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\" && t3 === \"E\") || (t1 === \"F\" && t3 === \"F\")) && q2 === \"AND\" && q0 !== \"NOT\") {\n let active = true\n let type = t3\n let joined = [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 === type) {\n joined.push(q5)\n i += 2\n } else {\n active = false\n newQuery.push(joined.join(\", \"))\n newTypes.push(type)\n }\n }\n } else {\n newQuery.push(q1)\n newTypes.push(t1)\n i += 1\n }\n }\n return [newQuery, newTypes]\n }\n\n /**\n * For finding parenthesis pairs\n */\n findPairs(query, types) {\n const pairs = new Map();\n\n // Find list of indices where parenthesis should be removed\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 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 if (index != 0) {\n index -= 1;\n } else {\n pairs.set(i, j)\n pairs.set(j, i)\n break\n }\n }\n }\n }\n }\n return pairs;\n }\n\n /**\n * For removing unnecessary parenthesis\n */\n removeParenthesis(query, types) {\n // Find list of indices where parenthesis should be removed\n const pairs = this.findPairs(query, 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 let p = pairs.get(i)\n\n // Remove outer parentheses\n if (i === 0 && p == query.length-1) {\n removed.push(p)\n removed.push(i)\n // Remove parentheses that do not contain multiple operations\n } else {\n const contents = query.slice(i+1, p)\n const tp = types.slice(i+1, p)\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(p)\n removed.push(i)\n }\n }\n }\n }\n\n // Find repeated quotes and reduce them to single one\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n if (q1 === \"(\" && q1 === \"(\") {\n let p1 = pairs.get(i)\n let p2 = pairs.get(i+1)\n if (p2 == p1 - 1) {\n removed.push(i)\n removed.push(p1)\n }\n }\n }\n\n // Remove found parentheses\n removed = removed.sort((a, b) => b - a);\n for (let j=0; j < removed.length; ++j) {\n query.splice(removed[j], 1);\n types.splice(removed[j], 1);\n }\n return [query, types]\n }\n\n /**\n * Combines AND statements where either one or both of the statements has a\n * parenthesis. These statements are refactored using distributive laws of\n * set theory:\n * A AND (B OR C) == (A AND B) OR (A AND C)\n * in order to see if there are lists of elements that should be joined.\n */\n combineANDOut(query, types) {\n let newQuery = query\n let newTypes = types\n const pairs = this.findPairs(query, 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 let rhsEnd\n let lhsStart\n if (q1 === \")\") {\n lhsStart = pairs.get(i)\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n } else {\n if (query[i-1] === \"NOT\") {\n lhsStart = i-1\n } else {\n lhsStart = i\n }\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n lhsQ.unshift(\"(\")\n lhsT.unshift(\"P\")\n lhsQ.push(\")\")\n lhsT.push(\"P\")\n }\n if (q3 === \"(\") {\n rhsEnd = pairs.get(i+2)+1\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n } else {\n if (query[i+2] === \"NOT\") {\n rhsEnd = i+4\n } else {\n rhsEnd = i+3\n }\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\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 comboQ.unshift(\"(\")\n comboT.unshift(\"P\")\n comboQ.push(\")\")\n comboT.push(\"P\")\n newQuery = query.slice(0, lhsStart).concat(comboQ).concat(query.slice(rhsEnd, query.length))\n newTypes = types.slice(0, lhsStart).concat(comboT).concat(types.slice(rhsEnd, query.length))\n break\n }\n }\n return [newQuery, newTypes, combined]\n }\n \n /**\n * Recursive function for simplifying the query into a form that can be\n * correctly interpreted by Optimade.\n */\n simplify(query, types) {\n\n // Remove unnecessary parenthesis, combine all elements connected with\n // AND, and remove unnecessary parenthesis again.\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n\n // Combine parenthesis statements\n let combined\n [query, types, combined] = this.combineANDOut(query, types);\n\n // If a combination was done, run recursively until no combination is\n // done\n if (combined) {\n [query, types] = this.simplify(query, types)\n // After the last step perform on final cleanup\n } else {\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n }\n return [query, types]\n }\n}\n\n// EXPORTS\nmodule.exports = OptimadeTranslator;\n\n\n//# sourceURL=webpack:///./src/search-mod/OptimadeTranslator.js?");
/***/ }),
......
......@@ -751,8 +751,9 @@ img.remove-label{ cursor: pointer; }
#elementable{width:690px; position: relative;
padding: 0 26px;
margin: 0 auto 60px;
background-color: #DDD;}
margin: 0 auto 0;
background-color: #DDD;
}
.element-info{position: absolute;
display: none;
......@@ -852,9 +853,9 @@ div#specialRows{margin: 20px 40px;}
padding: 2px 12px; margin: 4px 0;
}
.MaterialList{ /*width: 690px; margin: 0 auto 80px;*/}
.MaterialList{ /*width: 690px; margin: 0 auto 80px;*/
margin-top: 30px;
}
/* #paginationWg erased*/
.MaterialList .formula {
......
......@@ -65,7 +65,9 @@ class OptimadeTranslator {
query[i] = q;
}
let res = query.join(" ")
// Surround with parenthesis so that this part will not get incorrectly
// mixed with the filters due to operator precedence
let res = "(" + query.join(" ") + ")"
console.log("FINAL OPTIMADE QUERY:")
console.log(res)
return res
......
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