diff --git a/src/core/js/query_form.js b/src/core/js/query_form.js index 89056c13dd9f82f8fa9781d5a1676e9616b7714c..5b3ae1cd78761d9794de4da36fec5a7e58c1ad46 100644 --- a/src/core/js/query_form.js +++ b/src/core/js/query_form.js @@ -1,245 +1,270 @@ /** * Extend the functionality of the pure html query panel. * + * Deprecated. This is to be replaced by the query form of caosdb-webui-core-components. + * + * @deprecated * @module queryForm * @global */ -var queryForm = function () { - - const init = function (form) { - queryForm.restoreLastQuery(form, () => window.sessionStorage.lastQuery); - queryForm.bindOnClick(form, (set) => { - window.sessionStorage.lastQuery = set; - }); - const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = ""; - queryForm.initFreeSearch(form, `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}`); - }; +var queryForm = (function () { + const init = function (form) { + queryForm.restoreLastQuery(form, () => window.sessionStorage.lastQuery); + queryForm.bindOnClick(form, (set) => { + window.sessionStorage.lastQuery = set; + }); + const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = ""; + queryForm.initFreeSearch( + form, + `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}` + ); + }; - const logger = log.getLogger("queryForm"); - var role_name_facet_select = undefined; + const logger = log.getLogger("queryForm"); + var role_name_facet_select = undefined; - const _isCql = function (query) { - query = query.toUpperCase().trim(); - return (query.startsWith("FIND") || query.startsWith("COUNT") || query.startsWith("SELECT")); - } + const _isCql = function (query) { + query = query.toUpperCase().trim(); + return ( + query.startsWith("FIND") || + query.startsWith("COUNT") || + query.startsWith("SELECT") + ); + }; - /** - * Initialize the free search to generate search queries which search only - * within one of several options of roles or entity names. - * - * E.g. when `options` is "Person, Experiment, Sample" the user can select - * one of these options before submitting the query. When the user types in - * something that doesn't looks like a CQL-query, a query is generated - * instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE - * "*something*". - * - * Note: This feature is disabled by default. Enable it by specifying the - * build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a - * comma-separated list of **properly quoted** expressions, e.g. - * "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'". - * Otherwise, the server will throw a syntax error. - * - * @function initFreeSearch - * @param {HTMLElement} form - the form which will be initialized for the - * free search. - * @param {string} options - comma-separated list of options. - * @return {boolean} - true if the initialization was successful, false - * otherwise. - */ - const initFreeSearch = function (form, options) { - const textArea = $(form).find(".caosdb-f-query-textarea"); - logger.trace("initFreeSearch", form, textArea, options); - if (textArea.length > 0 && options && options != "") { - const lastQuery = window.localStorage["freeTextQuery:" + textArea[0].value]; - if (lastQuery) { - textArea[0].value = lastQuery; - } - - const selected = window.localStorage["role_name_facet_option"]; - const select = $(`<select class="btn btn-secondary"/>`); - for (let option of options.split(",")) { - select.append(`<option ${selected === option.trim() ? "selected" : ""}>${option}</option>`); - } - $(form).find(".input-group").prepend(select); - role_name_facet_select = select[0]; - - const switchFreeSearch = (text_area) => { - if(_isCql(text_area.value)) { - select.hide(); - $(text_area).css({"border-top-left-radius": "0.375rem", "border-bottom-left-radius": "0.375rem"}); - } else { - select.show(); - $(text_area).css({}); - } - } - - switchFreeSearch(textArea[0]); - - textArea.on("keydown", (e) => { - switchFreeSearch(e.target); - }); - return true; - } - role_name_facet_select = undefined; - return false; - } + /** + * Initialize the free search to generate search queries which search only + * within one of several options of roles or entity names. + * + * E.g. when `options` is "Person, Experiment, Sample" the user can select + * one of these options before submitting the query. When the user types in + * something that doesn't looks like a CQL-query, a query is generated + * instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE + * "*something*". + * + * Note: This feature is disabled by default. Enable it by specifying the + * build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a + * comma-separated list of **properly quoted** expressions, e.g. + * "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'". + * Otherwise, the server will throw a syntax error. + * + * @function initFreeSearch + * @param {HTMLElement} form - the form which will be initialized for the + * free search. + * @param {string} options - comma-separated list of options. + * @return {boolean} - true if the initialization was successful, false + * otherwise. + */ + const initFreeSearch = function (form, options) { + const textArea = $(form).find(".caosdb-f-query-textarea"); + logger.trace("initFreeSearch", form, textArea, options); + if (textArea.length > 0 && options && options != "") { + const lastQuery = + window.localStorage["freeTextQuery:" + textArea[0].value]; + if (lastQuery) { + textArea[0].value = lastQuery; + } - /** - * @function restoreLastQuery - */ - const restoreLastQuery = function (form, getter) { - if (form == null) { - throw new Error("form was null"); - } - if (getter()) { - form.query.value = getter(); - } - }; + const selected = window.localStorage["role_name_facet_option"]; + const select = $(`<select class="btn btn-secondary"/>`); + for (let option of options.split(",")) { + select.append( + `<option ${ + selected === option.trim() ? "selected" : "" + }>${option}</option>` + ); + } + $(form).find(".input-group").prepend(select); + role_name_facet_select = select[0]; - /** - * @function redirect - * @param {string} query - the query string. - * @param {string} paging - the paging string, e.g. 0L10. - */ - const redirect = function (query, paging) { - var pagingparam = "" - if (paging && paging.length > 0) { - pagingparam = "P=" + paging + "&"; + const switchFreeSearch = (text_area) => { + if (_isCql(text_area.value)) { + select.hide(); + $(text_area).css({ + "border-top-left-radius": "0.375rem", + "border-bottom-left-radius": "0.375rem", + }); + } else { + select.show(); + $(text_area).css({}); } - location.href = connection.getBasePath() + "Entity/?" + pagingparam + "query=" + query; + }; + + switchFreeSearch(textArea[0]); + + textArea.on("keydown", (e) => { + switchFreeSearch(e.target); + }); + return true; } + role_name_facet_select = undefined; + return false; + }; - /** - * Read out the selector for the role/name facet of the query. Return - * "RECORD" if the selector is disabled. - * @function getRoleNameFacet - */ - const getRoleNameFacet = function () { - if (role_name_facet_select) { - const result = role_name_facet_select.value; - window.localStorage["role_name_facet_option"] = result; - return result; - } - return "RECORD"; + /** + * @function restoreLastQuery + */ + const restoreLastQuery = function (form, getter) { + if (form == null) { + throw new Error("form was null"); + } + if (getter()) { + form.query.value = getter(); } + }; - const _splitSearchTermsPattern = /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g; - - /** - * Split a query string into single terms. - * - * Terms are separated by white spaces. Terms which contain white spaces - * which are to be preserved must be enclosed in " or ' quotes. The - * enclosing quotation marks are being stripped. Currently no support for - * escape sequences for quotation marks. - * - * @function splitSearchTerms - * @param {string} query - complete query string. - * @return {string[]} array of the search terms. - */ - const splitSearchTerms = function (query) { - // add empty space at the end, so every matching group ends with it -> easier regex. Also, undefined is filtered out - return Array.from((query + " ").matchAll(_splitSearchTermsPattern), (m) => m[1] || m[2] || m[3]).filter((word) => word); + /** + * @function redirect + * @param {string} query - the query string. + * @param {string} paging - the paging string, e.g. 0L10. + */ + const redirect = function (query, paging) { + var pagingparam = ""; + if (paging && paging.length > 0) { + pagingparam = "P=" + paging + "&"; } + location.href = + connection.getBasePath() + "Entity/?" + pagingparam + "query=" + query; + }; - /** - * Is the query a SELECT field,... FROM entity query? - * - * @function isSelectQuery - * @param {HTMLElement} query, the query to be tested. - * @return {Boolean} - */ - const isSelectQuery = function (query) { - return query.toUpperCase().startsWith("SELECT"); + /** + * Read out the selector for the role/name facet of the query. Return + * "RECORD" if the selector is disabled. + * @function getRoleNameFacet + */ + const getRoleNameFacet = function () { + if (role_name_facet_select) { + const result = role_name_facet_select.value; + window.localStorage["role_name_facet_option"] = result; + return result; } + return "RECORD"; + }; - /** - * @function bindOnClick - */ - const bindOnClick = function (form, setter) { - if (setter == null || typeof (setter) !== 'function' || setter.length !== 1) { - throw new Error("setter must be a function with one param"); - } + const _splitSearchTermsPattern = + /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g; + + /** + * Split a query string into single terms. + * + * Terms are separated by white spaces. Terms which contain white spaces + * which are to be preserved must be enclosed in " or ' quotes. The + * enclosing quotation marks are being stripped. Currently no support for + * escape sequences for quotation marks. + * + * @function splitSearchTerms + * @param {string} query - complete query string. + * @return {string[]} array of the search terms. + */ + const splitSearchTerms = function (query) { + // add empty space at the end, so every matching group ends with it -> easier regex. Also, undefined is filtered out + return Array.from( + (query + " ").matchAll(_splitSearchTermsPattern), + (m) => m[1] || m[2] || m[3] + ).filter((word) => word); + }; + + /** + * Is the query a SELECT field,... FROM entity query? + * + * @function isSelectQuery + * @param {HTMLElement} query, the query to be tested. + * @return {Boolean} + */ + const isSelectQuery = function (query) { + return query.toUpperCase().startsWith("SELECT"); + }; - /* + /** + * @function bindOnClick + */ + const bindOnClick = function (form, setter) { + if (setter == null || typeof setter !== "function" || setter.length !== 1) { + throw new Error("setter must be a function with one param"); + } + + /* Here a submit handler is created that is attached to both the form submit handler and the click handler of the button. See https://developer.mozilla.org/en-US/docs/Web/Events/submit why this is necessary. */ - var submithandler = function () { - // store current query - var queryField = form.query; - var value = queryField.value; - if (typeof value == "undefined" || value.length == 0) { - return; - } - if (!_isCql(value)) { - // split words in query field at space and create query fragments - var words = splitSearchTerms(queryField.value).map(word => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'`); - if (!words.length) { - return false; - } - const e = getRoleNameFacet(); - const query_string = `FIND ${e} WHICH HAS ` + words.join(" AND "); - queryField.value = query_string; - - // store original value of the text field - window.localStorage["freeTextQuery:" + query_string] = value; - } - setter(queryField.value); - - var paging = ""; - if (form.P && !isSelectQuery(queryField.value)) { - paging = form.P.value - } - - queryForm.redirect(queryField.value.trim(), paging); - - var btn = $(form).find(".caosdb-search-btn"); - btn.html(`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`); - btn.prop("disabled", true); - - var textField = $(form).find(".caosdb-f-query-textarea"); - textField.blur(); - textField.prop("disabled", true); - }; - - // handler for the form - form.onsubmit = function (e) { - e.preventDefault(); - submithandler(); - return false; - }; - - // same handler for the button - form.getElementsByClassName("caosdb-search-btn")[0].onclick = function () { - submithandler(); - }; + var submithandler = function () { + // store current query + var queryField = form.query; + var value = queryField.value; + if (typeof value == "undefined" || value.length == 0) { + return; + } + if (!_isCql(value)) { + // split words in query field at space and create query fragments + var words = splitSearchTerms(queryField.value).map( + (word) => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'` + ); + if (!words.length) { + return false; + } + const e = getRoleNameFacet(); + const query_string = `FIND ${e} WHICH HAS ` + words.join(" AND "); + queryField.value = query_string; + + // store original value of the text field + window.localStorage["freeTextQuery:" + query_string] = value; + } + setter(queryField.value); + + var paging = ""; + if (form.P && !isSelectQuery(queryField.value)) { + paging = form.P.value; + } + + queryForm.redirect(queryField.value.trim(), paging); + + var btn = $(form).find(".caosdb-search-btn"); + btn.html( + `<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>` + ); + btn.prop("disabled", true); + + var textField = $(form).find(".caosdb-f-query-textarea"); + textField.blur(); + textField.prop("disabled", true); }; - /** - * @function getRoleNameFacetSelect - */ - - return { - init: init, - initFreeSearch: initFreeSearch, - isSelectQuery: isSelectQuery, - restoreLastQuery: restoreLastQuery, - redirect: redirect, - bindOnClick: bindOnClick, - splitSearchTerms: splitSearchTerms, - getRoleNameFacet: getRoleNameFacet, - getRoleNameFacetSelect: () => role_name_facet_select, - } -}(); + // handler for the form + form.onsubmit = function (e) { + e.preventDefault(); + submithandler(); + return false; + }; + // same handler for the button + form.getElementsByClassName("caosdb-search-btn")[0].onclick = function () { + submithandler(); + }; + }; + + /** + * @function getRoleNameFacetSelect + */ + + return { + init: init, + initFreeSearch: initFreeSearch, + isSelectQuery: isSelectQuery, + restoreLastQuery: restoreLastQuery, + redirect: redirect, + bindOnClick: bindOnClick, + splitSearchTerms: splitSearchTerms, + getRoleNameFacet: getRoleNameFacet, + getRoleNameFacetSelect: () => role_name_facet_select, + }; +})(); $(document).ready(function () { - if (`${BUILD_MODULE_LEGACY_QUERY_FORM}` == "ENABLED") { - var form = document.getElementById("caosdb-query-form"); - if (form != null) { - queryForm.init(form); - } - } + if (`${BUILD_MODULE_LEGACY_QUERY_FORM}` == "ENABLED") { + var form = document.getElementById("caosdb-query-form"); + if (form != null) { + queryForm.init(form); + } + } });