Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
encyclopedia-gui
Commits
bc872242
Commit
bc872242
authored
Apr 01, 2021
by
Lauri Himanen
Browse files
Fixed issue with query syntax popup not showing and material name query not working.
parent
2e801e69
Pipeline
#97478
skipped with stage
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
client/bundle.js
View file @
bc872242
...
...
@@ -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\n
let util = __webpack_require__(/*! ../common/util.js */
\"
./src/common/util.js
\"
);
\n
let InfoSys = __webpack_require__(/*! ../common/InfoSys.js */
\"
./src/common/InfoSys.js
\"
);
\n
let LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */
\"
./src/common/LoadingPopup.js
\"
);
\n\n
//const RESULTS_PER_PAGE = 20;
\n\n\n
class MaterialList {
\n\n
constructor(){
\n
this.element = document.createElement('div');
\n
this.element.className = 'MaterialList';
\n\n
// state
\n
this.visible = false;
\n
this.noResults = true;
\n
this.matMap = new Map();
\n
//this.currentSystemType = 'bulk';
\n
this.optimadeQuery = null;
\n
this.newestQuery = null;
\n
\n
this.noResultsBox = document.createElement('div');
\n
this.noResultsBox.style = 'text-align: center; font-weight: bold'
\n
this.noResultsBox.innerHTML = 'NO RESULTS FOUND';
\n
this.element.append(this.noResultsBox);
\n\n
this.matListWrapper = document.createElement('div');
\n
this.element.append(this.matListWrapper);
\n
this.pagControl = new PaginationControl();
\n
this.matListWrapper.append(this.pagControl.element);
\n
this.pagControl.setPrevPageListener( page => {
\n
this._search(page);
\n
});
\n
this.pagControl.setNextPageListener( page => {
\n
this._search(page);
\n
})
\n\n
this.matListContainer = new MatListContainer();
\n
this.matListWrapper.append(this.matListContainer.element);
\n
}
\n\n\n
attachAndSetEvents(element){
\n
element.appendChild(this.element);
\n
this._render();
\n
}
\n\n\n
invalidateSearch(){
\n
this.visible = false;
\n
this._render();
\n
}
\n\n\n
initSearch(optimadeQuery){
\n
this.optimadeQuery = optimadeQuery;
\n
this._search();
\n
}
\n\n
_search(page){
\n
//this.resultsContainer.style.visibility = 'hidden';
\n
this.matMap.clear();
\n\n
LoadingPopup.show();
\n\n
let reqJson = {
\n
query: this.optimadeQuery,
\n
search_by: {}
\n
};
\n
if (page) reqJson.search_by = { page: page}
\n\n
// Add the restricted option from the checkbox
\n
//
let restrictedEl = document.getElementById('restricted-search');
\n
//
reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');
\n
reqJson.search_by.restricted = '0';
\n
console.log('SEARCHING: ', reqJson );
\n
const timestamp = Date.now();
\n
this.newestQuery = timestamp;
\n\n
document.querySelector('#syntax-error').style.visibility = 'hidden';
\n
fetch(util.getSearchURL(), {
\n
method: 'POST',
\n
headers: {'Content-Type': 'application/json;charset=utf-8'},
\n
body: JSON.stringify(reqJson)
\n
})
\n
.then( resp => resp.json() )
\n
.then( result => {
\n
console.log('GETTING: ', result);
\n\n
// If a newer query has been sent, ignore the results of an old query.
\n
if (this.newestQuery === timestamp) {
\n
// Update state
\n
this.noResults = (result.results.length === 0);
\n
this._setMatList(result.results);
\n
this.pagControl.set(result.pages);
\n
\n
this.visible = true;
\n
this._render();
\n
}
\n
})
\n
.catch(error => {
\n
document.querySelector('#syntax-error').style.visibility = 'visible';
\n
})
\n
.finally(() => {
\n
LoadingPopup.hide();
\n
});
\n
\n
\n
/*
\n
oReq.addEventListener(
\"
error
\"
, e => { // Not valid query
\n
console.log('Search ERROR - Not valid query ');
\n
this.total_results= 0;
\n
this.setData([]);
\n
this._updateUI();
\n
this.resultsContainer.style.visibility = 'visible';
\n
LoadingPopup.hide();
\n
});
\n
*/
\n
}
\n\n\n
_render(){
\n
this.element.style.display = this.visible ? '' : 'none';
\n
if (this.visible) {
\n
this.noResultsBox.style.display = this.noResults ? '' : 'none';
\n
this.matListWrapper.style.display = this.noResults ? 'none' : '';
\n
this.matListContainer.updateList(this.matMap);
\n
//document.querySelector('.user-msg-box').innerHTML = this.noResults ? 'No search results' : 'See result list below';
\n
} else {
\n
//document.querySelector('.user-msg-box').innerHTML = '';
\n
}
\n
}
\n\n\n
_setMatList(matList){
\n\n
if (matList.length > 0){
\n
matList.forEach( mat => {
\n\n
if (this.matMap.has(mat.formula_reduced)){
\n
let matArray = this.matMap.get(mat.formula_reduced);
\n
matArray.push(mat);
\n
}else
\n
this.matMap.set(mat.formula_reduced, [mat]);
\n
});
\n\n
}else this.matMap.clear(); // Right query - results not found
\n
}
\n\n
}
\n\n\n\n
class 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
\"
/> prev
\n
</span>
\n
<span class=
\"
page
\"
> X </span>
\n
<span class=
\"
nextButton
\"
> next
\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\n
class 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\n
module.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\n
let util = __webpack_require__(/*! ../common/util.js */
\"
./src/common/util.js
\"
);
\n
let InfoSys = __webpack_require__(/*! ../common/InfoSys.js */
\"
./src/common/InfoSys.js
\"
);
\n
let LoadingPopup = __webpack_require__(/*! ../common/LoadingPopup.js */
\"
./src/common/LoadingPopup.js
\"
);
\n\n
//const RESULTS_PER_PAGE = 20;
\n\n\n
class MaterialList {
\n\n
constructor(){
\n
this.element = document.createElement('div');
\n
this.element.className = 'MaterialList';
\n\n
// state
\n
this.visible = false;
\n
this.noResults = true;
\n
this.matMap = new Map();
\n
//this.currentSystemType = 'bulk';
\n
this.optimadeQuery = null;
\n
this.newestQuery = null;
\n
\n
this.noResultsBox = document.createElement('div');
\n
this.noResultsBox.style = 'text-align: center; font-weight: bold'
\n
this.noResultsBox.innerHTML = 'NO RESULTS FOUND';
\n
this.element.append(this.noResultsBox);
\n\n
this.matListWrapper = document.createElement('div');
\n
this.element.append(this.matListWrapper);
\n
this.pagControl = new PaginationControl();
\n
this.matListWrapper.append(this.pagControl.element);
\n
this.pagControl.setPrevPageListener( page => {
\n
this._search(page);
\n
});
\n
this.pagControl.setNextPageListener( page => {
\n
this._search(page);
\n
})
\n\n
this.matListContainer = new MatListContainer();
\n
this.matListWrapper.append(this.matListContainer.element);
\n
}
\n\n\n
attachAndSetEvents(element){
\n
element.appendChild(this.element);
\n
this._render();
\n
}
\n\n\n
invalidateSearch(){
\n
this.visible = false;
\n
this._render();
\n
}
\n\n\n
initSearch(optimadeQuery){
\n
this.optimadeQuery = optimadeQuery;
\n
this._search();
\n
}
\n\n
_search(page){
\n
//this.resultsContainer.style.visibility = 'hidden';
\n
this.matMap.clear();
\n\n
LoadingPopup.show();
\n\n
let reqJson = {
\n
query: this.optimadeQuery,
\n
search_by: {}
\n
};
\n
if (page) reqJson.search_by = { page: page}
\n\n
// Add the restricted option from the checkbox
\n
let restrictedEl = document.getElementById('restricted-search');
\n
reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');
\n
console.log('SEARCHING: ', reqJson );
\n
const timestamp = Date.now();
\n
this.newestQuery = timestamp;
\n\n
document.querySelector('#syntax-error').style.visibility = 'hidden';
\n
fetch(util.getSearchURL(), {
\n
method: 'POST',
\n
headers: {'Content-Type': 'application/json;charset=utf-8'},
\n
body: JSON.stringify(reqJson)
\n
})
\n
.then( resp => resp.json() )
\n
.then( result => {
\n
console.log('GETTING: ', result);
\n\n
// If a newer query has been sent, ignore the results of an old query.
\n
if (this.newestQuery === timestamp) {
\n
// Update state
\n
this.noResults = (result.results.length === 0);
\n
this._setMatList(result.results);
\n
this.pagControl.set(result.pages);
\n
\n
this.visible = true;
\n
this._render();
\n
}
\n
})
\n
.catch(error => {
\n
console.log(
\"
Error
\"
)
\n
document.querySelector('#syntax-error').style.visibility = 'visible';
\n
})
\n
.finally(() => {
\n
LoadingPopup.hide();
\n
});
\n
\n
\n
/*
\n
oReq.addEventListener(
\"
error
\"
, e => { // Not valid query
\n
console.log('Search ERROR - Not valid query ');
\n
this.total_results= 0;
\n
this.setData([]);
\n
this._updateUI();
\n
this.resultsContainer.style.visibility = 'visible';
\n
LoadingPopup.hide();
\n
});
\n
*/
\n
}
\n\n\n
_render(){
\n
this.element.style.display = this.visible ? '' : 'none';
\n
if (this.visible) {
\n
this.noResultsBox.style.display = this.noResults ? '' : 'none';
\n
this.matListWrapper.style.display = this.noResults ? 'none' : '';
\n
this.matListContainer.updateList(this.matMap);
\n
//document.querySelector('.user-msg-box').innerHTML = this.noResults ? 'No search results' : 'See result list below';
\n
} else {
\n
//document.querySelector('.user-msg-box').innerHTML = '';
\n
}
\n
}
\n\n\n
_setMatList(matList){
\n\n
if (matList.length > 0){
\n
matList.forEach( mat => {
\n\n
if (this.matMap.has(mat.formula_reduced)){
\n
let matArray = this.matMap.get(mat.formula_reduced);
\n
matArray.push(mat);
\n
}else
\n
this.matMap.set(mat.formula_reduced, [mat]);
\n
});
\n\n
}else this.matMap.clear(); // Right query - results not found
\n
}
\n\n
}
\n\n\n\n
class 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
\"
/> prev
\n
</span>
\n
<span class=
\"
page
\"
> X </span>
\n
<span class=
\"
nextButton
\"
> next
\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\n
class 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\n
module.exports = MaterialList;
\n\n\n
//# sourceURL=webpack:///./src/search-mod/MaterialList.view.js?
"
);
/***/
}),
...
...
@@ -447,7 +447,7 @@ eval("/**\n * Copyright 2016-2019 Iker Hurtado, Georg Huhs\n *\n * Licensed unde
/***/
((
module
,
__unused_webpack_exports
,
__webpack_require__
)
=>
{
"
use strict
"
;
eval
(
"
\n
/**
\n
* Copyright 2016-2019 Iker Hurtado, Georg Huhs
\n
*
\n
* Licensed under the Apache License, Version 2.0 (the
\"
License
\"
);
\n
* you may not use this file except in compliance with the License.
\n
* You may obtain a copy of the License at
\n
*
\n
* http://www.apache.org/licenses/LICENSE-2.0
\n
*
\n
* Unless required by applicable law or agreed to in writing, software
\n
* distributed under the License is distributed on an
\"
AS IS
\"
BASIS,
\n
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\n
* See the License for the specific language governing permissions and
\n
* limitations under the License.
\n
*
\n
*/
\n\n\n
/*
\n
This file implements the Search Module of the application.
\n
It's a container UI component that shows the seach part of the application:
\n
front search interface and search results page.
\n
*/
\n\n\n\n
let util = __webpack_require__(/*! ../common/util.js */
\"
./src/common/util.js
\"
);
\n
let UserGuidance = __webpack_require__(/*! ../common/UserGuidance.js */
\"
./src/common/UserGuidance.js
\"
);
\n
let SearchBox = __webpack_require__(/*! ./SearchBox.view.js */
\"
./src/search-mod/SearchBox.view.js
\"
);
\n
let ElementTable = __webpack_require__(/*! ./ElemenTable.view.js */
\"
./src/search-mod/ElemenTable.view.js
\"
);
\n
let MaterialList = __webpack_require__(/*! ./MaterialList.view.js */
\"
./src/search-mod/MaterialList.view.js
\"
);
\n
let MaterialNameBox = __webpack_require__(/*! ./MaterialName.view.js */
\"
./src/search-mod/MaterialName.view.js
\"
);
\n
let FilterPanel = __webpack_require__(/*! ./FilterPanel.view.js */
\"
./src/search-mod/FilterPanel.view.js
\"
);
\n
let SwitchComponent = __webpack_require__(/*! ../common/SwitchComponent.js */
\"
./src/common/SwitchComponent.js
\"
);
\n\n\n
const REACTIVE_SEARCH_RIGHT = false; // Whether the right panel uses reactive search
\n
const REACTIVE_SEARCH_LEFT = true; // Whether the left panel uses reactive search
\n
const INVALIDATE_RESULTS = false; // Whether the search results are invalidated upon changing search criteria
\n\n
function replaceDashes(s){
\n
return s.split('-').join('_');
\n
}
\n\n
class NewSearchMod {
\n\n
constructor() {
\n\n
this.userGuidance = true; // can enabled/disabled
\n
this.searchFilters = [];
\n
this.isVisible = true;
\n
this.element = document.createElement('div');
\n
this.element.setAttribute(
\"
id
\"
,'search-module');
\n
this.element.innerHTML=
\n
`
\n
<div class=
\"
search-filter-side
\"
>
\n
</div>
\n
<div class=
\"
search-main-side
\"
>
\n
<div class=
\"
composition
\"
>
\n
<div class=
\"
search-box-placeholder
\"
> </div>
\n
<div class=
\"
add-buttons
\"
>
\n
<div class=
\"
tab-buttons
\"
style=
\"
width: 70%; display: inline-block
\"
>
\n
<button class=
\"
element-add-btn
\"
id=
\"
add-tab-selected
\"
>Element<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button><button class=
\"
formula-add-btn
\"
>Formula<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button><button class=
\"
name-add-btn
\"
>Name<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button>
\n
</div>
\n
<div class=
\"
bool-buttons
\"
style=
\"
width: 28%; display: inline-block
\"
>
\n
OR <span id=
\"
and-or-switch
\"
></span> AND
\n
<button class=
\"
not-button
\"
>NOT</button>
\n
<button class=
\"
open-parentheses
\"
>(</button>
\n
<button class=
\"
close-parentheses
\"
>)</button>
\n
</div>
\n
</div>
\n
</div>
\n
<div class=
\"
add-box
\"
>
\n
<div class=
\"
add-panel
\"
>
\n
</div>
\n
</div>
\n
<div class=
\"
results-panel
\"
>
\n
</div>
\n
</div>
\n
`;
\n\n
this.filterSidePanel = this.element.querySelector('.search-filter-side');
\n
this.addBox = this.element.querySelector('.add-box');
\n\n
this.searchBox = new SearchBox();
\n
this.searchBox.setBoolOperator('AND');
\n
this.element.querySelector('.search-box-placeholder').append(this.searchBox.element);
\n
this.searchBox.setRemoveElementListener( (element) => {
\n
this.elementTable.deselectElement(element);
\n
});
\n
// When the clear all button is clicked on and
\n
// when the search query gets blank by removing query items
\n
this.searchBox.setCleanSearchQueryListener( () => {
\n
this.addFormulaButton.disabled = false;
\n
this.addMatNameButton.disabled = false;
\n
this.addElementButton.disabled = false;
\n
this.formulaBox.enableInput();
\n
this.materialNameBox.enableInput();
\n
this.elementTable.deselectAllElements();
\n
});
\n\n
\n
this.searchBox.setSearchQueryChangeListener( () => {
\n
REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
});
\n\n
/* This button could be out of the search box because its functionality
\n
is not part of it. It can also be removed eventually, if considered not necessary */
\n
this.searchButton = this.searchBox.getSearchButtonElement();
\n
this.searchButton.addEventListener( 'click', e => {
\n
this.sendQuery();
\n
this.addBox.style.display = 'none';
\n
this.isVisible = false;
\n
const icon = this.currentTabElement.querySelector('.search-fold-icon')
\n
icon.src =
\"
img/folded.png
\"
;
\n
});
\n\n
this.addButtonsBox= this.element.querySelector('.add-buttons');
\n
this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn');
\n
this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn');
\n
this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn');
\n
this.currentTabElement = this.addElementButton;
\n
this.currentTab = 'element';
\n\n
this.addPanel= this.element.querySelector('.add-panel');
\n\n
let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch_new');
\n
this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element);
\n
andOrSwitch.setListener( e => {
\n
this.searchBox.setBoolOperator( e ? 'AND' : 'OR');
\n
});
\n\n
this.notButton = this.element.querySelector('.not-button');
\n
this.notButton.addEventListener( 'click', e => {
\n
this.searchBox.addNOT();
\n
});
\n\n
this.openParenthButton = this.element.querySelector('.open-parentheses');
\n
this.closeParenthButton = this.element.querySelector('.close-parentheses');
\n
this.openParenthButton.addEventListener( 'click', e => {
\n
this.searchBox.addParentheses(true);
\n
});
\n
this.closeParenthButton.addEventListener( 'click', e => {
\n
this.searchBox.addParentheses(false);
\n
});
\n\n
this.elementTable = new ElementTable();
\n
this.elementTable.setClickListener(elementArray => {
\n
this.searchBox.addElements(elementArray);
\n
this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code
\n
this.addFormulaButton.disabled = true;
\n
});
\n
this.elementTable.setDeselectListener(e => {
\n
this.searchBox.removeElementORFormulaInSearchQuery(e)
\n
});
\n\n
this.formulaBox = new FormulaBox();
\n
this.formulaBox.setAddFormulaListener(formula => {
\n
if (formula.trim() !== ''){
\n
this.searchBox.addTag(formula, 'formula');
\n
this.addElementButton.disabled = true;
\n
this.addMatNameButton.disabled = true;
\n
}
\n
});
\n\n
this.materialNameBox = new MaterialNameBox();
\n
this.materialNameBox.setAddMaterialNameListener( name => {
\n
if (name.trim() !== ''){
\n
this.searchBox.addTag(name, 'material');
\n
this.addElementButton.disabled = true;
\n
this.addFormulaButton.disabled = true;
\n
}
\n
});
\n\n
this.filterPanel = new FilterPanel();
\n
this.filterSidePanel.appendChild(this.filterPanel.element);
\n\n
this.filterPanel.setPropsChangeListener( propsMap => {
\n
REACTIVE_SEARCH_LEFT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
})
\n\n
this.materialList= new MaterialList();
\n
this.resultsPage = this.element.querySelector('.results-panel');
\n
this.materialList.attachAndSetEvents(this.resultsPage);
\n\n
this.addPanel.appendChild(this.elementTable.element);
\n\n
this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');
\n
this.allowOtherElementsCheckbox.addEventListener( 'change', e => {
\n
REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
});
\n
this._events();
\n
}
\n\n\n
_events() {
\n
this.addButtonsBox.addEventListener(
\"
click
\"
, (e) => {
\n
if (e.target !== e.currentTarget) { // When the event source is a child
\n
let className = e.target.className;
\n
let index = className.indexOf('add-btn');
\n\n
if (index > 0){
\n
let selectingElement;
\n
let selectingTab = className.substring(0, index-1);
\n
if (selectingTab === 'element') {
\n
selectingElement = this.elementTable.element;
\n
} else if (selectingTab === 'name') {
\n
selectingElement = this.materialNameBox.element;
\n
// add autocomplete functionality,
\n
// but load data not before the name tab is activated
\n
this.materialNameBox.setAutocomplete();
\n
} else if (selectingTab === 'formula') {
\n
selectingElement = this.formulaBox.element;
\n
}
\n\n
this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);
\n\n
// Hide or show the current tab
\n
const icon = e.target.querySelector('.search-fold-icon')
\n
if (this.currentTab == selectingTab) {
\n
if (this.isVisible) {
\n
this.addBox.style.display = 'none';
\n
icon.src =
\"
img/folded.png
\"
;
\n
} else {
\n
this.addBox.style.display = 'block';
\n
icon.src =
\"
img/unfolded.png
\"
;
\n
}
\n
this.isVisible = !this.isVisible;
\n
} else {
\n
this.addBox.style.display = 'block';
\n
icon.src =
\"
img/unfolded.png
\"
;
\n
this.isVisible = true;
\n
}
\n\n
// Change the styles of the buttons
\n
let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn');
\n
this._setTabSelectedStyles(selEl, false);
\n
this._setTabSelectedStyles(e.target, true);
\n\n
this.currentTab = selectingTab;
\n
this.currentTabElement = selEl;
\n\n
// Disable/enable bool operation buttons on the material name tab
\n
//this.element.querySelectorAll('.bool-buttons button').forEach( button => {
\n
//button.disabled = (selectingTab === 'name');
\n
//});
\n
/*
\n
if (this.userGuidance){
\n
if (selectingTab === 'element'){
\n
UserGuidance.showIndependentTip(7, false);
\n
UserGuidance.showIndependentTip(3, true);
\n
}
\n
else if (selectingTab === 'props'){
\n
UserGuidance.showIndependentTip(3, false);
\n
UserGuidance.showIndependentTip(7, true);
\n
}else if (selectingTab === 'formula'){
\n
UserGuidance.showIndependentTip(3, false);
\n
UserGuidance.showIndependentTip(7, false);
\n
}
\n
}
\n
*/
\n\n
}
\n
}
\n
});
\n
}
\n\n\n
sendQuery() {
\n
\n
//**** The optimade query must be formed from the search box and the properties selected
\n
const
allowOtherElements
= document.getElementById('allow-other-elements').checked
;
\n
const searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(allowOtherElements);
\n
const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());
\n
const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');
\n\n
// If one of them is empty, it and sep variable are ''
\n
this.materialList.initSearch(searchBoxOptimadeQuery+sep+propsOptimadeQuery);
\n
// this.materialList.invalidateSearch();
\n
\n
function getOptimadeQueryFromProps(propsValuesMap){
\n
let query = '';
\n
propsValuesMap.forEach( (values, prop) => {
\n
let subquery = '';
\n
values.forEach( v => { // values should be and array, sometimes with just one value
\n
let val = ( v === true ? 'TRUE' : '
\"
'+v+'
\"
')
\n
subquery += (subquery === '' ? '' : ' OR ')+prop+'='+val;
\n
})
\n
query += (query === '' ? '' : ' AND ')+`(${subquery})`
\n
})
\n
return query
\n
}
\n
}
\n\n
_addFiltersInSearchQuery(filterMap, searchExpressionQuery){
\n
let rootQueryObj = { 'bool' : {} };
\n
rootQueryObj.bool.must = [];
\n
rootQueryObj.bool.must.push( searchExpressionQuery );
\n\n\n
filterMap.forEach((values/*Array*/, filterName) => {
\n\n
let filterNameDef = replaceDashes(filterName);
\n\n
if (filterName === 'mass-density' || filterName === 'band-gap'){
\n
//***** util.eV2J() apply?
\n
rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) );
\n\n
}else if (filterName === 'band-gap-type'){ // special case
\n
if ( values !== 'both')
\n
rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct',
\n
( values === 'direct' ? true : false ) ) );
\n\n
}else if (filterName.startsWith('has')){ // has- filters
\n
rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) );
\n\n
}else{ // normal case
\n
//rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) );
\n
rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) );
\n\n
//console.log(this._getESOperatorMatch(filterNameDef, values, false) );
\n
}
\n\n
});
\n\n
return rootQueryObj;
\n
}
\n\n
_setTabSelectedStyles(element, value){
\n
/*
\n
element.style.fontWeight = (value ? 'bold' : 'normal');
\n
element.style.color = (value ? '#E56400' : '#777');
\n
element.style.borderColor = (value ? '#E56400' : '#777');
\n
*/
\n
element.id = (value ? 'add-tab-selected' : '');
\n
}
\n\n
_sortElements(elements){
\n
let numbers = [];
\n
let sortedElements = [];
\n
elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) );
\n
numbers.sort( (a, b) => a - b ); // atomic number-1
\n
numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) );
\n
//console.log('_sortElements ',numbers, elString);
\n
return sortedElements;
\n
}
\n\n
_reduceFormula(formula, getTokens = true){
\n
let index = 0;
\n
let map = new Map();
\n
let key;
\n
while ( index < formula.length ){
\n
let el2 = formula.substring(index, index+2);
\n
let el1 = formula.substring(index, index+1);
\n\n
if (util.ELEMENTS.indexOf(el2) >= 0){
\n
map.set(el2, 1); // 1 default value
\n
index += 2;
\n
key = el2;
\n
//console.log('eleemnt 2chars', key);
\n
}else if (util.ELEMENTS.indexOf(el1) >= 0){
\n
map.set(el1, 1); // 1 default value
\n
index++;
\n
key = el1;
\n
//console.log('eleemnt 1chars', key);
\n
}else{ // It's a number
\n
let num = parseInt(el2);
\n
if (num >= 10) index += 2; // 2 figures number
\n
else index++;// 1 figure number
\n
//console.log('number ', num, key);
\n
map.set(key, num);
\n
}
\n
// console.log('FINAL LOOP', map, index);
\n
}
\n\n
let counter = 0;
\n
while ( !checkIfReduced(map) ){ // console.log('Reducing', map);
\n
let div = 1;
\n
if (isDivisibleBy(map, 2)) div = 2;
\n
else if (isDivisibleBy(map, 3)) div = 3;
\n
else if (isDivisibleBy(map, 5)) div = 5;
\n
else if (isDivisibleBy(map, 7)) div = 7;
\n
else if (isDivisibleBy(map, 11)) div = 11;
\n\n
map.forEach( (value, key) => {
\n
map.set(key, (value/div));
\n
});
\n
//console.log('Reducing DIV', map);
\n
counter++;
\n
if (counter > 5) break;
\n
}
\n\n
function checkIfReduced(formulaMap){
\n
let min = 100;
\n
formulaMap.forEach( (value, key) => {
\n
if (value < min) min = value;
\n
});
\n
return min === 1;
\n
}
\n\n
function isDivisibleBy(formulaMap, n){
\n
let div = true;
\n
formulaMap.forEach( (value, key) => {
\n
if (value % n !== 0) div = false;
\n
});
\n
return div;
\n
}
\n\n
let tokens = [];
\n
let canonicalFormula = '';
\n
if (getTokens){
\n
map.forEach( (value, key) => tokens.push(key+value) );
\n
}else{
\n
let sortedElements = this._sortElements( Array.from( map.keys() ) );
\n
sortedElements.forEach( element => {
\n
canonicalFormula += element+map.get(element);
\n
//canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));
\n
});
\n
}
\n\n\n
console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula);
\n
return (getTokens ? tokens : canonicalFormula);
\n
}
\n\n\n
_processFormula(formula, type){
\n
let index = 0;
\n
let map = new Map();
\n
let key;
\n
while ( index < formula.length ){
\n
let el2 = formula.substring(index, index+2);
\n
let el1 = formula.substring(index, index+1);
\n\n
if (util.ELEMENTS.indexOf(el2) >= 0){
\n
map.set(el2, 1); // 1 default value
\n
index += 2;
\n
key = el2;
\n
//console.log('eleemnt 2chars', key);
\n
}else if (util.ELEMENTS.indexOf(el1) >= 0){
\n
map.set(el1, 1); // 1 default value
\n
index++;
\n
key = el1;
\n
//console.log('eleemnt 1chars', key);
\n
}else{ // It's a number
\n
let num = parseInt(el2);
\n
if (num >= 10) index += 2; // 2 figures number
\n
else index++;// 1 figure number
\n
//console.log('number ', num, key);
\n
map.set(key, num);
\n
}
\n
// console.log('FINAL LOOP', map, index);
\n
}
\n\n
if (type === 'tokens'){
\n
let tokens = [];
\n
map.forEach( (value, key) => tokens.push(key+value) );
\n
console.log('_processFormula RETURN: ', map, tokens);
\n
return tokens;
\n
}else{
\n
let sortedElements = this._sortElements( Array.from( map.keys() ) );
\n
if (type === 'canonical-formula'){
\n
let formulaString = '';
\n
sortedElements.forEach( element => {
\n
formulaString += element+map.get(element);
\n
});
\n
console.log('_processFormula RETURN: ', map, formulaString);
\n
return formulaString;
\n
}else{ // elements-string
\n
let elementsString = '';
\n
let elementsInFormulas = [];
\n
sortedElements.forEach( element => {
\n
elementsString += element;
\n
let val = map.get(element);
\n
if (val !== 0) elementsInFormulas.push(element+val);
\n
});
\n
console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]);
\n
return [elementsInFormulas ,elementsString];
\n
}
\n
}
\n\n
}
\n\n
}
\n\n\n
class FormulaBox {
\n\n
constructor() {
\n
this.element = document.createElement('div');
\n
this.element.className = 'FormulaBox';// this.element.setAttribute(
\"
id
\"
,'formula-box');
\n
this.element.innerHTML=
\n
`
\n
<div style=
\"
display: flex; flex-direction: column
\"
>
\n
<div style=
\"
display: flex; flex-direction: row
\"
>
\n
<input type=
\"
text
\"
placeholder=
\"
Add formula to the search query above
\"
>
\n
<button class=
\"
adding-formula-btn
\"
disabled> Add to query </button>
\n
</div>
\n
<div class=
\"
perm-tooltip search-option
\"
style=
\"
margin-right: 10px
\"
>
\n
<input id=
\"
allow-other-elements
\"
name=
\"
allow-other-elements
\"
type=
\"
checkbox
\"
checked>
\n
<label for=
\"
allow-other-elements
\"
class=
\"
perm-tooltip
\"
>Allow other elements</label>
\n
<span class=
\"
tooltiptext
\"
>If selected, the returned materials may also contain other elements.</span>
\n
</div>
\n
</div>
\n
`;
\n
this.formulaTextField = this.element.querySelector('input');
\n
this.formulaButton = this.element.querySelector('.adding-formula-btn');
\n\n
this.formulaButton.addEventListener(
\"
click
\"
, (e) => {
\n
this.addFormulaListener(this.formulaTextField.value);
\n
this.formulaTextField.value = '';
\n
});
\n\n
this.formulaTextField.addEventListener( 'input', e => {
\n
//console.log('formulaTextField input: ',this.formulaTextField.value);
\n
this.formulaButton.disabled = (this.formulaTextField.value === '');
\n
});
\n\n
}
\n\n\n
setAddFormulaListener(listener) {
\n
this.addFormulaListener= listener;
\n
}
\n\n\n
disableInput() {
\n
this.formulaTextField.disabled = true;
\n
this.formulaButton.disabled = true;
\n
}
\n\n
enableInput() {
\n
this.formulaTextField.disabled = false;
\n
this.formulaButton.disabled = false;
\n
}
\n\n
/*
\n
getAllowOtherElements(){
\n
return this.element.querySelector('.allow-other-elements').checked;
\n
}
\n\n\n
getMultiplesOfFormula(){
\n
return this.element.querySelector('.multiples-of-formula').checked;
\n
}*/
\n\n
}
\n\n
// EXPORTS
\n
module.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\n
let util = __webpack_require__(/*! ../common/util.js */
\"
./src/common/util.js
\"
);
\n
let UserGuidance = __webpack_require__(/*! ../common/UserGuidance.js */
\"
./src/common/UserGuidance.js
\"
);
\n
let SearchBox = __webpack_require__(/*! ./SearchBox.view.js */
\"
./src/search-mod/SearchBox.view.js
\"
);
\n
let ElementTable = __webpack_require__(/*! ./ElemenTable.view.js */
\"
./src/search-mod/ElemenTable.view.js
\"
);
\n
let MaterialList = __webpack_require__(/*! ./MaterialList.view.js */
\"
./src/search-mod/MaterialList.view.js
\"
);
\n
let MaterialNameBox = __webpack_require__(/*! ./MaterialName.view.js */
\"
./src/search-mod/MaterialName.view.js
\"
);
\n
let FilterPanel = __webpack_require__(/*! ./FilterPanel.view.js */
\"
./src/search-mod/FilterPanel.view.js
\"
);
\n
let SwitchComponent = __webpack_require__(/*! ../common/SwitchComponent.js */
\"
./src/common/SwitchComponent.js
\"
);
\n\n\n
const REACTIVE_SEARCH_RIGHT = false; // Whether the right panel uses reactive search
\n
const REACTIVE_SEARCH_LEFT = true; // Whether the left panel uses reactive search
\n
const INVALIDATE_RESULTS = false; // Whether the search results are invalidated upon changing search criteria
\n\n
function replaceDashes(s){
\n
return s.split('-').join('_');
\n
}
\n\n
class NewSearchMod {
\n\n
constructor() {
\n\n
this.userGuidance = true; // can enabled/disabled
\n
this.searchFilters = [];
\n
this.isVisible = true;
\n
this.element = document.createElement('div');
\n
this.element.setAttribute(
\"
id
\"
,'search-module');
\n
this.element.innerHTML=
\n
`
\n
<div class=
\"
search-filter-side
\"
>
\n
</div>
\n
<div class=
\"
search-main-side
\"
>
\n
<div class=
\"
composition
\"
>
\n
<div class=
\"
search-box-placeholder
\"
> </div>
\n
<div class=
\"
add-buttons
\"
>
\n
<div class=
\"
tab-buttons
\"
style=
\"
width: 70%; display: inline-block
\"
>
\n
<button class=
\"
element-add-btn
\"
id=
\"
add-tab-selected
\"
>Element<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button><button class=
\"
formula-add-btn
\"
>Formula<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button><button class=
\"
name-add-btn
\"
>Name<img class=
\"
search-fold-icon
\"
src=
\"
img/unfolded.png
\"
></button>
\n
</div>
\n
<div class=
\"
bool-buttons
\"
style=
\"
width: 28%; display: inline-block
\"
>
\n
OR <span id=
\"
and-or-switch
\"
></span> AND
\n
<button class=
\"
not-button
\"
>NOT</button>
\n
<button class=
\"
open-parentheses
\"
>(</button>
\n
<button class=
\"
close-parentheses
\"
>)</button>
\n
</div>
\n
</div>
\n
</div>
\n
<div class=
\"
add-box
\"
>
\n
<div class=
\"
add-panel
\"
>
\n
</div>
\n
</div>
\n
<div class=
\"
results-panel
\"
>
\n
</div>
\n
</div>
\n
`;
\n\n
this.filterSidePanel = this.element.querySelector('.search-filter-side');
\n
this.addBox = this.element.querySelector('.add-box');
\n\n
this.searchBox = new SearchBox();
\n
this.searchBox.setBoolOperator('AND');
\n
this.element.querySelector('.search-box-placeholder').append(this.searchBox.element);
\n
this.searchBox.setRemoveElementListener( (element) => {
\n
this.elementTable.deselectElement(element);
\n
});
\n
// When the clear all button is clicked on and
\n
// when the search query gets blank by removing query items
\n
this.searchBox.setCleanSearchQueryListener( () => {
\n
this.addFormulaButton.disabled = false;
\n
this.addMatNameButton.disabled = false;
\n
this.addElementButton.disabled = false;
\n
this.formulaBox.enableInput();
\n
this.materialNameBox.enableInput();
\n
this.elementTable.deselectAllElements();
\n
});
\n\n
\n
this.searchBox.setSearchQueryChangeListener( () => {
\n
REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
});
\n\n
/* This button could be out of the search box because its functionality
\n
is not part of it. It can also be removed eventually, if considered not necessary */
\n
this.searchButton = this.searchBox.getSearchButtonElement();
\n
this.searchButton.addEventListener( 'click', e => {
\n
this.sendQuery();
\n
this.addBox.style.display = 'none';
\n
this.isVisible = false;
\n
const icon = this.currentTabElement.querySelector('.search-fold-icon')
\n
icon.src =
\"
img/folded.png
\"
;
\n
});
\n\n
this.addButtonsBox= this.element.querySelector('.add-buttons');
\n
this.addElementButton = this.addButtonsBox.querySelector('.element-add-btn');
\n
this.addFormulaButton = this.addButtonsBox.querySelector('.formula-add-btn');
\n
this.addMatNameButton = this.addButtonsBox.querySelector('.name-add-btn');
\n
this.currentTabElement = this.addElementButton;
\n
this.currentTab = 'element';
\n\n
this.addPanel= this.element.querySelector('.add-panel');
\n\n
let andOrSwitch = new SwitchComponent(util.IMAGE_DIR+'switch_new');
\n
this.element.querySelector('#and-or-switch').appendChild(andOrSwitch.element);
\n
andOrSwitch.setListener( e => {
\n
this.searchBox.setBoolOperator( e ? 'AND' : 'OR');
\n
});
\n\n
this.notButton = this.element.querySelector('.not-button');
\n
this.notButton.addEventListener( 'click', e => {
\n
this.searchBox.addNOT();
\n
});
\n\n
this.openParenthButton = this.element.querySelector('.open-parentheses');
\n
this.closeParenthButton = this.element.querySelector('.close-parentheses');
\n
this.openParenthButton.addEventListener( 'click', e => {
\n
this.searchBox.addParentheses(true);
\n
});
\n
this.closeParenthButton.addEventListener( 'click', e => {
\n
this.searchBox.addParentheses(false);
\n
});
\n\n
this.elementTable = new ElementTable();
\n
this.elementTable.setClickListener(elementArray => {
\n
this.searchBox.addElements(elementArray);
\n
this.addMatNameButton.disabled = true; // Not always necessary but it simplifies the code
\n
this.addFormulaButton.disabled = true;
\n
});
\n
this.elementTable.setDeselectListener(e => {
\n
this.searchBox.removeElementORFormulaInSearchQuery(e)
\n
});
\n\n
this.formulaBox = new FormulaBox();
\n
this.formulaBox.setAddFormulaListener(formula => {
\n
if (formula.trim() !== ''){
\n
this.searchBox.addTag(formula, 'formula');
\n
this.addElementButton.disabled = true;
\n
this.addMatNameButton.disabled = true;
\n
}
\n
});
\n\n
this.materialNameBox = new MaterialNameBox();
\n
this.materialNameBox.setAddMaterialNameListener( name => {
\n
if (name.trim() !== ''){
\n
this.searchBox.addTag(name, 'material');
\n
this.addElementButton.disabled = true;
\n
this.addFormulaButton.disabled = true;
\n
}
\n
});
\n\n
this.filterPanel = new FilterPanel();
\n
this.filterSidePanel.appendChild(this.filterPanel.element);
\n\n
this.filterPanel.setPropsChangeListener( propsMap => {
\n
REACTIVE_SEARCH_LEFT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
})
\n\n
this.materialList= new MaterialList();
\n
this.resultsPage = this.element.querySelector('.results-panel');
\n
this.materialList.attachAndSetEvents(this.resultsPage);
\n\n
this.addPanel.appendChild(this.elementTable.element);
\n\n
this.allowOtherElementsCheckbox = this.element.querySelector('#allow-other-elements');
\n
this.allowOtherElementsCheckbox.addEventListener( 'change', e => {
\n
REACTIVE_SEARCH_RIGHT ? this.sendQuery() : INVALIDATE_RESULTS && this.materialList.invalidateSearch();
\n
});
\n
this._events();
\n
}
\n\n\n
_events() {
\n
this.addButtonsBox.addEventListener(
\"
click
\"
, (e) => {
\n
if (e.target !== e.currentTarget) { // When the event source is a child
\n
let className = e.target.className;
\n
let index = className.indexOf('add-btn');
\n\n
if (index > 0){
\n
let selectingElement;
\n
let selectingTab = className.substring(0, index-1);
\n
if (selectingTab === 'element') {
\n
selectingElement = this.elementTable.element;
\n
} else if (selectingTab === 'name') {
\n
selectingElement = this.materialNameBox.element;
\n
// add autocomplete functionality,
\n
// but load data not before the name tab is activated
\n
this.materialNameBox.setAutocomplete();
\n
} else if (selectingTab === 'formula') {
\n
selectingElement = this.formulaBox.element;
\n
}
\n\n
this.addPanel.replaceChild(selectingElement, this.addPanel.lastChild);
\n\n
// Hide or show the current tab
\n
const icon = e.target.querySelector('.search-fold-icon')
\n
if (this.currentTab == selectingTab) {
\n
if (this.isVisible) {
\n
this.addBox.style.display = 'none';
\n
icon.src =
\"
img/folded.png
\"
;
\n
} else {
\n
this.addBox.style.display = 'block';
\n
icon.src =
\"
img/unfolded.png
\"
;
\n
}
\n
this.isVisible = !this.isVisible;
\n
} else {
\n
this.addBox.style.display = 'block';
\n
icon.src =
\"
img/unfolded.png
\"
;
\n
this.isVisible = true;
\n
}
\n\n
// Change the styles of the buttons
\n
let selEl = this.element.querySelector('.'+this.currentTab+'-add-btn');
\n
this._setTabSelectedStyles(selEl, false);
\n
this._setTabSelectedStyles(e.target, true);
\n\n
this.currentTab = selectingTab;
\n
this.currentTabElement = selEl;
\n\n
// Disable/enable bool operation buttons on the material name tab
\n
//this.element.querySelectorAll('.bool-buttons button').forEach( button => {
\n
//button.disabled = (selectingTab === 'name');
\n
//});
\n
/*
\n
if (this.userGuidance){
\n
if (selectingTab === 'element'){
\n
UserGuidance.showIndependentTip(7, false);
\n
UserGuidance.showIndependentTip(3, true);
\n
}
\n
else if (selectingTab === 'props'){
\n
UserGuidance.showIndependentTip(3, false);
\n
UserGuidance.showIndependentTip(7, true);
\n
}else if (selectingTab === 'formula'){
\n
UserGuidance.showIndependentTip(3, false);
\n
UserGuidance.showIndependentTip(7, false);
\n
}
\n
}
\n
*/
\n\n
}
\n
}
\n
});
\n
}
\n\n\n
sendQuery() {
\n
\n
//**** The optimade query must be formed from the search box and the properties selected
\n
const
elementCheckbox
= document.getElementById('allow-other-elements')
;
\n
let allowOtherElements = true
\n
if (elementCheckbox) {
\n
allowOtherElements = elementCheckbox
.checked
\n
}
\n
const searchBoxOptimadeQuery = this.searchBox.getOptimadeQuery(allowOtherElements);
\n
const propsOptimadeQuery = getOptimadeQueryFromProps(this.filterPanel.getValues());
\n
const sep = (searchBoxOptimadeQuery !== '' && propsOptimadeQuery !== '' ? ' AND ' : '');
\n\n
// If one of them is empty, it and sep variable are ''
\n
this.materialList.initSearch(searchBoxOptimadeQuery+sep+propsOptimadeQuery);
\n
// this.materialList.invalidateSearch();
\n
\n
function getOptimadeQueryFromProps(propsValuesMap){
\n
let query = '';
\n
propsValuesMap.forEach( (values, prop) => {
\n
let subquery = '';
\n
values.forEach( v => { // values should be and array, sometimes with just one value
\n
let val = ( v === true ? 'TRUE' : '
\"
'+v+'
\"
')
\n
subquery += (subquery === '' ? '' : ' OR ')+prop+'='+val;
\n
})
\n
query += (query === '' ? '' : ' AND ')+`(${subquery})`
\n
})
\n
return query
\n
}
\n
}
\n\n
_addFiltersInSearchQuery(filterMap, searchExpressionQuery){
\n
let rootQueryObj = { 'bool' : {} };
\n
rootQueryObj.bool.must = [];
\n
rootQueryObj.bool.must.push( searchExpressionQuery );
\n\n\n
filterMap.forEach((values/*Array*/, filterName) => {
\n\n
let filterNameDef = replaceDashes(filterName);
\n\n
if (filterName === 'mass-density' || filterName === 'band-gap'){
\n
//***** util.eV2J() apply?
\n
rootQueryObj.bool.must.push( this._getFieldESRange(filterNameDef, values) );
\n\n
}else if (filterName === 'band-gap-type'){ // special case
\n
if ( values !== 'both')
\n
rootQueryObj.bool.must.push( this._getESSimpleMatch('band_gap_direct',
\n
( values === 'direct' ? true : false ) ) );
\n\n
}else if (filterName.startsWith('has')){ // has- filters
\n
rootQueryObj.bool.must.push( this._getESSimpleMatch(filterNameDef, values, false) );
\n\n
}else{ // normal case
\n
//rootQueryObj.bool.must.push( this._getESOperatorMatch(filterNameDef, values, false) );
\n
rootQueryObj.bool.must.push( this._getESTermsArray(filterNameDef, values) );
\n\n
//console.log(this._getESOperatorMatch(filterNameDef, values, false) );
\n
}
\n\n
});
\n\n
return rootQueryObj;
\n
}
\n\n
_setTabSelectedStyles(element, value){
\n
/*
\n
element.style.fontWeight = (value ? 'bold' : 'normal');
\n
element.style.color = (value ? '#E56400' : '#777');
\n
element.style.borderColor = (value ? '#E56400' : '#777');
\n
*/
\n
element.id = (value ? 'add-tab-selected' : '');
\n
}
\n\n
_sortElements(elements){
\n
let numbers = [];
\n
let sortedElements = [];
\n
elements.forEach( e => numbers.push(util.ELEMENTS.indexOf(e)) );
\n
numbers.sort( (a, b) => a - b ); // atomic number-1
\n
numbers.forEach( n => sortedElements.push(util.ELEMENTS[n]) );
\n
//console.log('_sortElements ',numbers, elString);
\n
return sortedElements;
\n
}
\n\n
_reduceFormula(formula, getTokens = true){
\n
let index = 0;
\n
let map = new Map();
\n
let key;
\n
while ( index < formula.length ){
\n
let el2 = formula.substring(index, index+2);
\n
let el1 = formula.substring(index, index+1);
\n\n
if (util.ELEMENTS.indexOf(el2) >= 0){
\n
map.set(el2, 1); // 1 default value
\n
index += 2;
\n
key = el2;
\n
//console.log('eleemnt 2chars', key);
\n
}else if (util.ELEMENTS.indexOf(el1) >= 0){
\n
map.set(el1, 1); // 1 default value
\n
index++;
\n
key = el1;
\n
//console.log('eleemnt 1chars', key);
\n
}else{ // It's a number
\n
let num = parseInt(el2);
\n
if (num >= 10) index += 2; // 2 figures number
\n
else index++;// 1 figure number
\n
//console.log('number ', num, key);
\n
map.set(key, num);
\n
}
\n
// console.log('FINAL LOOP', map, index);
\n
}
\n\n
let counter = 0;
\n
while ( !checkIfReduced(map) ){ // console.log('Reducing', map);
\n
let div = 1;
\n
if (isDivisibleBy(map, 2)) div = 2;
\n
else if (isDivisibleBy(map, 3)) div = 3;
\n
else if (isDivisibleBy(map, 5)) div = 5;
\n
else if (isDivisibleBy(map, 7)) div = 7;
\n
else if (isDivisibleBy(map, 11)) div = 11;
\n\n
map.forEach( (value, key) => {
\n
map.set(key, (value/div));
\n
});
\n
//console.log('Reducing DIV', map);
\n
counter++;
\n
if (counter > 5) break;
\n
}
\n\n
function checkIfReduced(formulaMap){
\n
let min = 100;
\n
formulaMap.forEach( (value, key) => {
\n
if (value < min) min = value;
\n
});
\n
return min === 1;
\n
}
\n\n
function isDivisibleBy(formulaMap, n){
\n
let div = true;
\n
formulaMap.forEach( (value, key) => {
\n
if (value % n !== 0) div = false;
\n
});
\n
return div;
\n
}
\n\n
let tokens = [];
\n
let canonicalFormula = '';
\n
if (getTokens){
\n
map.forEach( (value, key) => tokens.push(key+value) );
\n
}else{
\n
let sortedElements = this._sortElements( Array.from( map.keys() ) );
\n
sortedElements.forEach( element => {
\n
canonicalFormula += element+map.get(element);
\n
//canonicalFormula += element+(map.get(element) === 1 ? '' : map.get(element));
\n
});
\n
}
\n\n\n
console.log('_reduceFormula RETURN: ', map, tokens, canonicalFormula);
\n
return (getTokens ? tokens : canonicalFormula);
\n
}
\n\n\n
_processFormula(formula, type){
\n
let index = 0;
\n
let map = new Map();
\n
let key;
\n
while ( index < formula.length ){
\n
let el2 = formula.substring(index, index+2);
\n
let el1 = formula.substring(index, index+1);
\n\n
if (util.ELEMENTS.indexOf(el2) >= 0){
\n
map.set(el2, 1); // 1 default value
\n
index += 2;
\n
key = el2;
\n
//console.log('eleemnt 2chars', key);
\n
}else if (util.ELEMENTS.indexOf(el1) >= 0){
\n
map.set(el1, 1); // 1 default value
\n
index++;
\n
key = el1;
\n
//console.log('eleemnt 1chars', key);
\n
}else{ // It's a number
\n
let num = parseInt(el2);
\n
if (num >= 10) index += 2; // 2 figures number
\n
else index++;// 1 figure number
\n
//console.log('number ', num, key);
\n
map.set(key, num);
\n
}
\n
// console.log('FINAL LOOP', map, index);
\n
}
\n\n
if (type === 'tokens'){
\n
let tokens = [];
\n
map.forEach( (value, key) => tokens.push(key+value) );
\n
console.log('_processFormula RETURN: ', map, tokens);
\n
return tokens;
\n
}else{
\n
let sortedElements = this._sortElements( Array.from( map.keys() ) );
\n
if (type === 'canonical-formula'){
\n
let formulaString = '';
\n
sortedElements.forEach( element => {
\n
formulaString += element+map.get(element);
\n
});
\n
console.log('_processFormula RETURN: ', map, formulaString);
\n
return formulaString;
\n
}else{ // elements-string
\n
let elementsString = '';
\n
let elementsInFormulas = [];
\n
sortedElements.forEach( element => {
\n
elementsString += element;
\n
let val = map.get(element);
\n
if (val !== 0) elementsInFormulas.push(element+val);
\n
});
\n
console.log('_processFormula RETURN: ', map, [elementsInFormulas ,elementsString]);
\n
return [elementsInFormulas ,elementsString];
\n
}
\n
}
\n\n
}
\n\n
}
\n\n\n
class FormulaBox {
\n\n
constructor() {
\n
this.element = document.createElement('div');
\n
this.element.className = 'FormulaBox';// this.element.setAttribute(
\"
id
\"
,'formula-box');
\n
this.element.innerHTML=
\n
`
\n
<div style=
\"
display: flex; flex-direction: column
\"
>
\n
<div style=
\"
display: flex; flex-direction: row
\"
>
\n
<input type=
\"
text
\"
placeholder=
\"
Add formula to the search query above
\"
>
\n
<button class=
\"
adding-formula-btn
\"
disabled> Add to query </button>
\n
</div>
\n
<div class=
\"
perm-tooltip search-option
\"
style=
\"
margin-right: 10px
\"
>
\n
<input id=
\"
allow-other-elements
\"
name=
\"
allow-other-elements
\"
type=
\"
checkbox
\"
checked>
\n
<label for=
\"
allow-other-elements
\"
class=
\"
perm-tooltip
\"
>Allow other elements</label>
\n
<span class=
\"
tooltiptext
\"
>If selected, the returned materials may also contain other elements.</span>
\n
</div>
\n
</div>
\n
`;
\n
this.formulaTextField = this.element.querySelector('input');
\n
this.formulaButton = this.element.querySelector('.adding-formula-btn');
\n\n
this.formulaButton.addEventListener(
\"
click
\"
, (e) => {
\n
this.addFormulaListener(this.formulaTextField.value);
\n
this.formulaTextField.value = '';
\n
});
\n\n
this.formulaTextField.addEventListener( 'input', e => {
\n
//console.log('formulaTextField input: ',this.formulaTextField.value);
\n
this.formulaButton.disabled = (this.formulaTextField.value === '');
\n
});
\n\n
}
\n\n\n
setAddFormulaListener(listener) {
\n
this.addFormulaListener= listener;
\n
}
\n\n\n
disableInput() {
\n
this.formulaTextField.disabled = true;
\n
this.formulaButton.disabled = true;
\n
}
\n\n
enableInput() {
\n
this.formulaTextField.disabled = false;
\n
this.formulaButton.disabled = false;
\n
}
\n\n
/*
\n
getAllowOtherElements(){
\n
return this.element.querySelector('.allow-other-elements').checked;
\n
}
\n\n\n
getMultiplesOfFormula(){
\n
return this.element.querySelector('.multiples-of-formula').checked;
\n
}*/
\n\n
}
\n\n
// EXPORTS
\n
module.exports = NewSearchMod;
\n\n\n
//# sourceURL=webpack:///./src/search-mod/NewSearchMod.js?
"
);
/***/
}),
...
...
client/conf.js
View file @
bc872242
window
.
nomadEnv
=
{
//apiRoot: "https://nomad-lab.eu/dev/nomad/enc-search/api/encyclopedia/",
apiRoot
:
"
https://nomad-lab.eu/dev/rae/enc-search/api/encyclopedia/
"
,
//apiRoot: "https://nomad-lab.eu/prod/rae/beta/api/encyclopedia/",
//apiRoot: "https://nomad-lab.eu/prod/rae/api/encyclopedia/",
...
...
client/css/styles.css
View file @
bc872242
...
...
@@ -399,8 +399,6 @@ div.title span.unfolded::before{
background-color
:
white
;
border
:
2px
solid
#E56400
;
padding
:
8px
6px
;
overflow-y
:
hidden
;
overflow-x
:
hidden
;
box-sizing
:
border-box
;
min-height
:
50px
;
}
...
...
client/src/search-mod/MaterialList.view.js
View file @
bc872242
...
...
@@ -95,9 +95,8 @@ class MaterialList {
if
(
page
)
reqJson
.
search_by
=
{
page
:
page
}
// Add the restricted option from the checkbox
//let restrictedEl = document.getElementById('restricted-search');
//reqJson.search_by.restricted = (restrictedEl.checked ? '1' : '0');
reqJson
.
search_by
.
restricted
=
'
0
'
;
let
restrictedEl
=
document
.
getElementById
(
'
restricted-search
'
);
reqJson
.
search_by
.
restricted
=
(
restrictedEl
.
checked
?
'
1
'
:
'
0
'
);
console
.
log
(
'
SEARCHING:
'
,
reqJson
);
const
timestamp
=
Date
.
now
();
this
.
newestQuery
=
timestamp
;
...
...
@@ -124,6 +123,7 @@ class MaterialList {
}
})
.
catch
(
error
=>
{
console
.
log
(
"
Error
"
)
document
.
querySelector
(
'
#syntax-error
'
).
style
.
visibility
=
'
visible
'
;
})
.
finally
(()
=>
{
...
...
client/src/search-mod/NewSearchMod.js
View file @
bc872242
...
...
@@ -270,7 +270,11 @@ class NewSearchMod {
sendQuery
()
{
//**** The optimade query must be formed from the search box and the properties selected
const
allowOtherElements
=
document
.
getElementById
(
'
allow-other-elements
'
).
checked
;
const
elementCheckbox
=
document
.
getElementById
(
'
allow-other-elements
'
);
let
allowOtherElements
=
true
if
(
elementCheckbox
)
{
allowOtherElements
=
elementCheckbox
.
checked
}
const
searchBoxOptimadeQuery
=
this
.
searchBox
.
getOptimadeQuery
(
allowOtherElements
);
const
propsOptimadeQuery
=
getOptimadeQueryFromProps
(
this
.
filterPanel
.
getValues
());
const
sep
=
(
searchBoxOptimadeQuery
!==
''
&&
propsOptimadeQuery
!==
''
?
'
AND
'
:
''
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment