Commit 3aed4032 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Fixed optimade query for inclusive formula search.

parent b006a38b
Pipeline #92693 skipped with stage
...@@ -426,7 +426,7 @@ eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed un ...@@ -426,7 +426,7 @@ eval("\n/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed un
/***/ ((module, __unused_webpack_exports, __webpack_require__) => { /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict"; "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 console.log(\"INCLUSIVE FORMULA\")\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 console.log(\"EXCLUSIVE FORMULA\")\n console.log(subquery[0])\n let formula = new Formula(subquery[0]);\n console.log(formula.getOptimadeSubquery(allowOtherElements))\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 let fragments = []\n this.formulaMap.forEach( (number, element)=> {\n let fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n return 'formula HAS ' + (allowOtherElements ? 'ANY ' : 'ONLY ') + fragments.join(', ')\n\n //let first = true;\n //this.formulaMap.forEach( (number, element)=> {\n //console.log(element, number);\n\n //if (first) first = false;\n //else optSubquery += ' AND ';\n\n //let token = element + (number === 1 ? '' : +number);\n //optSubquery += `formula HAS \"${token}\"`;\n ////optSubquery += `formula CONTAINS \"${token}\"`;\n //});\n //return optSubquery;\n }\n\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 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?");
/***/ }) /***/ })
......
...@@ -420,7 +420,7 @@ class Formula{ ...@@ -420,7 +420,7 @@ class Formula{
const fragment = '"' + element + (number === 1 ? '' : +number) + '"'; const fragment = '"' + element + (number === 1 ? '' : +number) + '"';
fragments.push(fragment) fragments.push(fragment)
}) })
return 'formula HAS ' + (allowOtherElements ? 'ANY ' : 'ONLY ') + fragments.join(', ') return 'formula HAS ' + (allowOtherElements ? 'ALL ' : 'ONLY ') + fragments.join(', ')
} }
......
Supports Markdown
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