diff --git a/src/core/js/ext_bookmarks.js b/src/core/js/ext_bookmarks.js index c261b91ae6dbd4667b4a4d94659b02d47b625522..12f3d995e5d4e55436adff496e13918ccaeedd60 100644 --- a/src/core/js/ext_bookmarks.js +++ b/src/core/js/ext_bookmarks.js @@ -23,8 +23,6 @@ */ 'use strict'; - - /** * Keep track of bookmarked entities and provide functions for export, viewing * all entities and resetting the bookmarks. @@ -61,570 +59,593 @@ */ var ext_bookmarks = function ($, logger, config) { - config = config || {}; - - /** - * This counter is used as a cache for the number of current bookmarks. It - * is used to quickly change the counter in the bookmarks menu and lazily - * updated. - */ - var counter = 0; - - /** - * Currently this is mainly usefull for testing, but in the future it might - * be desirable to have multiple bookmark collection at the same time. It - * would be easy to extent this class for this because the collection_id is - * used in the generated links and as part of the storage key in the - * bookmark_storage. - */ - var collection_id = config["collection_id"] || 0; - - const data_getters = config["data_getters"] || { - "URI": (id) => get_context_root() + id - }; - const data_no_cache = config["data_no_cache"] || ["URI"]; - - const data_attribute = config["data_attribute"] || "data-bmval"; - - /** - * Return all bookmark buttons on this page or which are children of scope. - * - * @param {HTMLElement|string} [scope='body'] - * @return {HTMLElement[]} array of bookmark buttons. - */ - const get_bookmark_buttons = function (scope) { - return $(scope || "body").find(`[${data_attribute}]`).toArray(); - } - - /** - * Sets the click event handler of the clear button. - * - * @param {function} cb - event handler. - */ - const set_clear_button_click = config["set_clear_button_click"] || function (cb) { - $("#caosdb-f-bookmarks-clear") - .toggleClass("disabled", !cb) - .on("click", cb); - } + config = config || {}; + + /** + * This counter is used as a cache for the number of current bookmarks. It + * is used to quickly change the counter in the bookmarks menu and lazily + * updated. + */ + var counter = 0; + + /** + * Currently this is mainly usefull for testing, but in the future it might + * be desirable to have multiple bookmark collection at the same time. It + * would be easy to extent this class for this because the collection_id is + * used in the generated links and as part of the storage key in the + * bookmark_storage. + */ + var collection_id = config["collection_id"] || 0; + + const data_getters = config["data_getters"] || { + "URI": (id) => get_context_root() + id + }; + const data_no_cache = config["data_no_cache"] || ["URI"]; + + const data_attribute = config["data_attribute"] || "data-bmval"; + + /** + * Return all bookmark buttons on this page or which are children of scope. + * + * @param {HTMLElement|string} [scope='body'] + * @return {HTMLElement[]} array of bookmark buttons. + */ + const get_bookmark_buttons = function (scope) { + return $(scope || "body").find(`[${data_attribute}]`).toArray(); + } - /** - * Sets the click event handler of the export button. - * - * @param {function} cb - event handler. - */ - const set_export_button_click = config["set_export_button_click"] || function (cb) { - $("#caosdb-f-bookmarks-export-link") - .toggleClass("disabled", !cb) - .on("click", cb); - } + /** + * Sets the click event handler of the clear button. + * + * @param {function} cb - event handler. + */ + const set_clear_button_click = config["set_clear_button_click"] || function (cb) { + $("#caosdb-f-bookmarks-clear") + .toggleClass("disabled", !cb) + .on("click", cb); + } - const getPaging = config["getPaging"] || (() => "?P=0L10"); - - /** - * The storage backend for the bookmarks. - */ - const bookmark_storage = config["bookmark_storage"] || window.localStorage; - - /** - * Set the href attribute of the bookmark collection link. - * - * @param {string} uri - */ - const set_collection_link = config["set_collection_link"] || function (uri) { - const link = $("#caosdb-f-bookmarks-collection-link") - .toggleClass("disabled", !uri) - .find("a"); - if (uri) { - link.attr("href", uri); - } else { - link.removeAttr("href"); + /** + * Sets the click event handler of the export button. + * + * @param {function} cb - event handler. + */ + const set_export_button_click = config["set_export_button_click"] || function (cb) { + $("#caosdb-f-bookmarks-export-link") + .toggleClass("disabled", !cb) + .on("click", cb); } - } - /** - * Set the counter badge in the bookmark menu. - */ - const update_counter = config["set_counter"] || function (counter) { - $("#caosdb-f-bookmarks-collection-counter").text(counter); - } + const getPaging = config["getPaging"] || (() => "?P=0L10"); + + /** + * The storage backend for the bookmarks. + */ + const bookmark_storage = config["bookmark_storage"] || window.localStorage; + + /** + * Set the href attribute of the bookmark collection link. + * + * @param {string} uri + */ + const set_collection_link = config["set_collection_link"] || function (uri) { + const link = $("#caosdb-f-bookmarks-collection-link") + .toggleClass("disabled", !uri) + .find("a"); + if (uri) { + link.attr("href", uri); + } else { + link.removeAttr("href"); + } + } - const get_context_root = config["get_context_root"] || (() => ""); - - /** - * This is used as a prefix of the key in the bookmark_storage. - */ - const key_prefix = "_bm_"; - - /** - * This marker is used to identify uris which specify a bookmark collection - * which should be reloaded. - */ - const uri_marker = "_bm_"; - - /** - * Extract the bookmark id from the bookmark button. - * - * @param {HTMLElement} button - the bookmark button. - * @return {string} the bookmark id. - */ - const get_value = function (button) { - const result = $(button).attr(data_attribute); - return result; - } + /** + * Set the counter badge in the bookmark menu. + */ + const update_counter = config["set_counter"] || function (counter) { + $("#caosdb-f-bookmarks-collection-counter").text(counter); + } - /** - * Construct the prefix of the key for the bookmark_storage. - * - * This can be used to construct the item key and the data key - * to delete all storage keys which belong to the current bookmark - * collection. - * - * @param {string} - */ - const get_collection_prefix = function () { - return key_prefix + collection_id; - } + const get_context_root = config["get_context_root"] || (() => ""); + + /** + * This is used as a prefix of the key in the bookmark_storage. + */ + const key_prefix = "_bm_"; + + /** + * This marker is used to identify uris which specify a bookmark collection + * which should be reloaded. + */ + const uri_marker = "_bm_"; + + /** + * Extract the bookmark id from the bookmark button. + * + * @param {HTMLElement} button - the bookmark button. + * @return {string} the bookmark id. + */ + const get_value = function (button) { + const result = $(button).attr(data_attribute); + return result; + } - /** - * Generate the key for the bookmark_storage. - * - * @param {string} val - the value which is used to generate the key. - */ - const get_key = function (val) { - return get_collection_prefix() + '_it_' + val; - } + /** + * Construct the prefix of the key for the bookmark_storage. + * + * This can be used to construct the item key and the data key + * to delete all storage keys which belong to the current bookmark + * collection. + * + * @param {string} + */ + const get_collection_prefix = function () { + return key_prefix + collection_id; + } + /** + * Generate the key for the bookmark_storage. + * + * @param {string} val - the value which is used to generate the key. + */ + const get_key = function (val) { + return get_collection_prefix() + '_it_' + val; + } - /** - * These will be the columns in the TSV file. For each column there should - * exist a data_getter. - */ - const tsv_columns = config["tsv_columns"] || ["URI"]; - /** - * Generate a single TSV row - * - * @return {string[]} array of row columns - */ - const get_export_table_row = async function (id) { - const row = []; - for (var col of tsv_columns) { - row.push(await get_bookmark_data(id, col)); + /** + * These will be the columns in the TSV file. For each column there should + * exist a data_getter. + */ + const tsv_columns = config["tsv_columns"] || ["URI"]; + + /** + * Generate a single TSV row + * + * @return {string[]} array of row columns + */ + const get_export_table_row = async function (id) { + const row = []; + for (var col of tsv_columns) { + row.push(await get_bookmark_data(id, col)); + } + return row; } - return row; - } - /** - * Generate the TSV data for the export callback with all current - * bookmarks. - * - * TODO merge with caosdb_utils.create_tsv_table. - * - * @param {string[]} bookmarks - array of ids. - * @param {string} [preamble="data:text/csv;charset=utf-8,"] - the preamble - * which is used for generating tables which can be downloaded by - * browsers. - * @param {string} [tab="%09"] - the tab string. - * @param {string} [newline="%0A"] - the newline string. - * @param {string[]} [leading_comments] - comment lines which are to be put - * even before the header line. They should be appropriately escaped (e.g. - * with "%23"). - */ - const get_export_table = async function (bookmarks, preamble, tab, newline, leading_comments) { - // TODO merge with related code in the module "caosdb_table_export". - preamble = ((typeof preamble == 'undefined') ? "data:text/csv;charset=utf-8,": preamble); - tab = tab || "%09"; - newline = newline || "%0A"; - leading_comments = (leading_comments ? leading_comments.join(newline) + newline : ""); - const header = leading_comments + tsv_columns.join(tab) + newline; - const rows = []; - for (let i = 0; i < bookmarks.length; i++) { - rows.push((await get_export_table_row(bookmarks[i])).join(tab)); - } - return `${preamble}${header}${rows.join(newline)}`; - } + /** + * Generate the TSV data for the export callback with all current + * bookmarks. + * + * TODO merge with caosdb_utils.create_tsv_table. + * + * @param {string[]} bookmarks - array of ids. + * @param {string} [preamble="data:text/csv;charset=utf-8,"] - the preamble + * which is used for generating tables which can be downloaded by + * browsers. + * @param {string} [tab="%09"] - the tab string. + * @param {string} [newline="%0A"] - the newline string. + * @param {string[]} [leading_comments] - comment lines which are to be put + * even before the header line. They should be appropriately escaped (e.g. + * with "%23"). + */ + const get_export_table = async function (bookmarks, preamble, tab, newline, leading_comments) { + // TODO merge with related code in the module "caosdb_table_export". + preamble = ((typeof preamble == 'undefined') ? "data:text/csv;charset=utf-8," : preamble); + tab = tab || "%09"; + newline = newline || "%0A"; + leading_comments = (leading_comments ? leading_comments.join(newline) + newline : ""); + const header = leading_comments + tsv_columns.join(tab) + newline; + const rows = []; + for (let i = 0; i < bookmarks.length; i++) { + rows.push((await get_export_table_row(bookmarks[i])).join(tab)); + } + return `${preamble}${header}${rows.join(newline)}`; + } - /** - * Download the table with a given filename. - * - * This method adds a temporay <A> element to the dom tree and triggers - * "click" because otherwise the filename cannot be set. - * - * See also: - * https://stackoverflow.com/questions/21177078/javascript-download-csv-as-file - */ - const download = function (table, filename) { - console.log("download"); - const link = $(`<a style="display: none" download="${filename}" href="${table}"/>`); - $("body").append(link); - link[0].click(); - link.remove(); - } + /** + * Download the table with a given filename. + * + * This method adds a temporay <A> element to the dom tree and triggers + * "click" because otherwise the filename cannot be set. + * + * See also: + * https://stackoverflow.com/questions/21177078/javascript-download-csv-as-file + */ + const download = function (table, filename) { + console.log("download"); + const link = $(`<a style="display: none" download="${filename}" href="${table}"/>`); + $("body").append(link); + link[0].click(); + link.remove(); + } - /** - * Trigger the download of the TSV table with all current bookmarks. - * - * This is the call-back for the export button. - */ - const export_bookmarks = async function () { - const ids = get_bookmarks(); - const versioned_bookmarks = [] - for (let id of ids) { - if (id.indexOf("@") > -1) { - versioned_bookmarks.push(id); - } else { - versioned_bookmarks.push(id + "@" + (await get_bookmark_data(id, "Version"))); + /** + * Trigger the download of the TSV table with all current bookmarks. + * + * This is the call-back for the export button. + */ + const export_bookmarks = async function () { + const ids = get_bookmarks(); + const versioned_bookmarks = [] + for (let id of ids) { + if (id.indexOf("@") > -1) { + versioned_bookmarks.push(id); + } else { + versioned_bookmarks.push(id + "@" + (await get_bookmark_data(id, "Version"))); + } } + const uri = get_collection_link(ids); + const leading_comments = [encodeURIComponent(`#Link to all entities: ${uri}`)]; + const export_table = await get_export_table(ids, undefined, undefined, undefined, leading_comments); + download(export_table, "bookmarked_entities.tsv"); } - const uri = get_collection_link(ids); - const leading_comments = [encodeURIComponent(`#Link to all entities: ${uri}`)]; - const export_table = await get_export_table(ids, undefined, undefined, undefined, leading_comments); - download(export_table, "bookmarked_entities.tsv"); - } - /** - * Return all current bookmarks. - * - * @return {string[]} array of bookmarked ids. - */ - const get_bookmarks = function () { - const result = []; - - const storage_key_prefix = get_key(""); - for (let i = 0; i < bookmark_storage.length; i++) { - const key = bookmark_storage.key(i); - if (key.indexOf(storage_key_prefix) > -1) { - result.push(bookmark_storage[key]); + /** + * Return all current bookmarks. + * + * @return {string[]} array of bookmarked ids. + */ + const get_bookmarks = function () { + const result = []; + + const storage_key_prefix = get_key(""); + for (let i = 0; i < bookmark_storage.length; i++) { + const key = bookmark_storage.key(i); + if (key.indexOf(storage_key_prefix) > -1) { + result.push(bookmark_storage[key]); + } } + return result; } - return result; - } - /** - * Update the clear button (i.e. add a click handler which resets the bookmarks.) - * - * @param {string[]} bookmarks - array of ids. - */ - const update_clear_button = function (bookmarks) { - if (bookmarks.length > 0) { - set_clear_button_click(clear_bookmark_storage); - } else { - set_clear_button_click(false); + /** + * Update the clear button (i.e. add a click handler which resets the bookmarks.) + * + * @param {string[]} bookmarks - array of ids. + */ + const update_clear_button = function (bookmarks) { + if (bookmarks.length > 0) { + set_clear_button_click(clear_bookmark_storage); + } else { + set_clear_button_click(false); + } } - } - /** - * Update the export button (i.e. add a click handler which generates the - * tsv file.) - * - * @param {string[]} bookmarks - array of ids. - */ - const update_export_link = function (bookmarks) { - if (bookmarks.length > 0) { - set_export_button_click(export_bookmarks); - } else { - set_export_button_click(false); + /** + * Update the export button (i.e. add a click handler which generates the + * tsv file.) + * + * @param {string[]} bookmarks - array of ids. + */ + const update_export_link = function (bookmarks) { + if (bookmarks.length > 0) { + set_export_button_click(export_bookmarks); + } else { + set_export_button_click(false); + } } - } - /** - * Generate the uri for the collection of all bookmarked entities. - * - * @param {string[]} bookmarks - array of ids. - * @return {string} uri - */ - const get_collection_link = function (bookmarks) { - const uri_segment = bookmarks.join("&"); - return get_context_root() + uri_segment + getPaging() + - "#" + uri_marker + collection_id; - } + /** + * Generate the uri for the collection of all bookmarked entities. + * + * @param {string[]} bookmarks - array of ids. + * @return {string} uri + */ + const get_collection_link = function (bookmarks) { + const uri_segment = bookmarks.join("&"); + return get_context_root() + uri_segment + getPaging() + + "#" + uri_marker + collection_id; + } - /** - * Update the link of the collection of bookmarks in the bookmark drop down - * menu. - * - * @param {string[]} bookmarks - array of ids. - */ - const update_collection_link = function (bookmarks) { - if (bookmarks.length > 0) { - const link = get_collection_link(bookmarks); - set_collection_link(link); - } else { - set_collection_link(false); + /** + * Update the link of the collection of bookmarks in the bookmark drop down + * menu. + * + * @param {string[]} bookmarks - array of ids. + */ + const update_collection_link = function (bookmarks) { + if (bookmarks.length > 0) { + const link = get_collection_link(bookmarks); + set_collection_link(link); + } else { + set_collection_link(false); + } } - } - /** - * Syncronize the bookmark_storage and currently visible bookmark button and - * update all buttons and other visible elements and the bookmarks drop - * down menu. - * - * @param {string[]} [bookmarks] - array of ids. If omitted, the - * get_bookmarks function is being called. - */ - const update_collection = function (bookmarks) { - bookmarks = bookmarks || get_bookmarks(); - update_counter(bookmarks.length); - update_collection_link(bookmarks); - update_export_link(bookmarks); - update_clear_button(bookmarks); - } + /** + * Syncronize the bookmark_storage and currently visible bookmark button and + * update all buttons and other visible elements and the bookmarks drop + * down menu. + * + * @param {string[]} [bookmarks] - array of ids. If omitted, the + * get_bookmarks function is being called. + */ + const update_collection = function (bookmarks) { + bookmarks = bookmarks || get_bookmarks(); + update_counter(bookmarks.length); + update_collection_link(bookmarks); + update_export_link(bookmarks); + update_clear_button(bookmarks); + } - /** - * Toggle the active class of the button and change the title of the button - * accordingly. - * - * @param {HTMLElement} button - bookmark button - * @param {boolean} is_active - whether the new state is active or not. - */ - const set_button_state = function (button, is_active) { - $(button).toggleClass("active", is_active); - if (is_active) { - $(button).attr("title", "Remove bookmark"); - } else { - $(button).attr("title", "Add bookmark"); + /** + * Toggle the active class of the button and change the title of the button + * accordingly. + * + * @param {HTMLElement} button - bookmark button + * @param {boolean} is_active - whether the new state is active or not. + */ + const set_button_state = function (button, is_active) { + $(button).toggleClass("active", is_active); + if (is_active) { + $(button).attr("title", "Remove bookmark"); + } else { + $(button).attr("title", "Add bookmark"); + } } - } - /** - * Event handler for the click event of the bookmark buttons. - * - * Toggles the buttons state and adds or removes the bookmark. - * - * @param {Event} e - the click event; - */ - const toggle_bookmark_active = function (e) { - const button = $(this); - - const new_is_active = !button.is(".active"); - set_button_state(button, new_is_active); - - const value = get_value(button[0]); - const key = get_key(value); - if (new_is_active) { - bookmark_storage.setItem(key, value); - update_counter(++counter); - - // fill the cache immediately. This is a good idea, because many - // data_getters can work on the DOM tree when the bookmark is being - // selected. Later, when the user has left the current page, the - // getters might need to request the database. We want to prevent - // that. - collect_bookmark_data(value); - } else { - bookmark_storage.removeItem(key); - update_counter(--counter); - remove_bookmark_data(value); - } - update_collection(); - } + /** + * Event handler for the click event of the bookmark buttons. + * + * Toggles the buttons state and adds or removes the bookmark. + * + * @param {Event} e - the click event; + */ + const toggle_bookmark_active = function (e) { + const button = $(this); + + const new_is_active = !button.is(".active"); + set_button_state(button, new_is_active); + + const value = get_value(button[0]); + const key = get_key(value); + if (new_is_active) { + bookmark_storage.setItem(key, value); + update_counter(++counter); + + // fill the cache immediately. This is a good idea, because many + // data_getters can work on the DOM tree when the bookmark is being + // selected. Later, when the user has left the current page, the + // getters might need to request the database. We want to prevent + // that. + collect_bookmark_data(value); + } else { + bookmark_storage.removeItem(key); + update_counter(--counter); + remove_bookmark_data(value); + } + update_collection(); + } - /** - * Fill the cache with data for the export for a bookmark. - * - * @param {string} id - bookmark id. - */ - const collect_bookmark_data = function (id) { - for (let data_key in data_getters) { - if (data_no_cache.indexOf(data_key) == -1) { - // do nothing, only trigger the fetching - get_bookmark_data(id, data_key) + /** + * Fill the cache with data for the export for a bookmark. + * + * @param {string} id - bookmark id. + */ + const collect_bookmark_data = function (id) { + for (let data_key in data_getters) { + if (data_no_cache.indexOf(data_key) == -1) { + // do nothing, only trigger the fetching + get_bookmark_data(id, data_key) + } } } - } - /** - * Remove all data item which belong to a bookmark. - * - * @param {string} id - bookmark id. - */ - const remove_bookmark_data = function (id) { - const data_key_prefix = get_data_key(id, ""); - remove_from_storage_by_prefix(data_key_prefix); - } + /** + * Remove all data item which belong to a bookmark. + * + * @param {string} id - bookmark id. + */ + const remove_bookmark_data = function (id) { + const data_key_prefix = get_data_key(id, ""); + remove_from_storage_by_prefix(data_key_prefix); + } - /** - * Initialize a single bookmark button. - * - * Fetch the state from the bookmark_storage and set the bookmark button to - * active or inactive. Also add the onclick handler which toggles the - * bookmark state. - * - * @param {HTMLElement} button - The bookmark button - */ - const init_button = function (button) { - // load state - const key = get_key(get_value(button)); - const is_bookmarked = !!key && !!bookmark_storage[key]; - set_button_state(button, is_bookmarked); - - // onlick handler - button.onclick = toggle_bookmark_active; - } + /** + * Initialize a single bookmark button. + * + * Fetch the state from the bookmark_storage and set the bookmark button to + * active or inactive. Also add the onclick handler which toggles the + * bookmark state. + * + * @param {HTMLElement} button - The bookmark button + */ + const init_button = function (button) { + // load state + const key = get_key(get_value(button)); + const is_bookmarked = !!key && !!bookmark_storage[key]; + set_button_state(button, is_bookmarked); + + // onlick handler + button.onclick = toggle_bookmark_active; + } - /** - * Remove all items in the bookmark_storage by a prefix. - * - * Useful for resetting the whole bookmark_storage or just deleting a - * single item along with its data items. - * - * @param {string} prefix - */ - const remove_from_storage_by_prefix = function (prefix) { - const keys = []; - for (let i = 0; i < bookmark_storage.length; i++) { - const key = bookmark_storage.key(i); - if (key.indexOf(prefix) > -1) { - keys.push(key); + /** + * Remove all items in the bookmark_storage by a prefix. + * + * Useful for resetting the whole bookmark_storage or just deleting a + * single item along with its data items. + * + * @param {string} prefix + */ + const remove_from_storage_by_prefix = function (prefix) { + const keys = []; + for (let i = 0; i < bookmark_storage.length; i++) { + const key = bookmark_storage.key(i); + if (key.indexOf(prefix) > -1) { + keys.push(key); + } + } + for (let i = 0; i < keys.length; i++) { + bookmark_storage.removeItem(keys[i]); } } - for (let i = 0; i < keys.length; i++) { - bookmark_storage.removeItem(keys[i]); - } - } - /** - * Remove all bookmarks, clear the counter and reset the buttons - */ - const clear_bookmark_storage = function () { - counter = 0; - update_collection([]); + /** + * Remove all bookmarks, clear the counter and reset the buttons + */ + const clear_bookmark_storage = function () { + counter = 0; + update_collection([]); - // re-init to reset all buttons - init(); + // re-init to reset all buttons + init(); - const storage_key_prefix = get_collection_prefix() - remove_from_storage_by_prefix(storage_key_prefix); - } + const storage_key_prefix = get_collection_prefix() + remove_from_storage_by_prefix(storage_key_prefix); + } - /** - * Add all bookmarks to storage. - * - * @param {string[]} ids - an array of ids. - */ - const add_all_bookmarks_to_storage = function (ids) { - counter = counter + ids.length; - for (let i = 0; i < ids.length; i++) { - const id = ids[i]; - if (id) { - bookmark_storage[get_key(id)] = id + /** + * Add all bookmarks to storage. + * + * @param {string[]} ids - an array of ids. + */ + const add_all_bookmarks_to_storage = function (ids) { + counter = counter + ids.length; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + if (id) { + bookmark_storage[get_key(id)] = id + } } + update_collection(); } - update_collection(); - } - /** - * Parse a list of bookmarked entities and the collection_id from and URI. - * - * @param {string} uri - * @return {object} dict with two keys: - * {string[]} bookmarks - * {string} _collection_id - */ - const parse_uri = function (uri) { - var cut_index = uri.indexOf("#" + uri_marker); - if (cut_index > -1) { - // get collection id - const _collection_id = uri - .substring(cut_index + uri_marker.length + 1) - // remove query - const query_marker = uri.indexOf("?"); - if (query_marker > -1) { - cut_index = query_marker; + /** + * Parse a list of bookmarked entities and the collection_id from and URI. + * + * @param {string} uri + * @return {object} dict with two keys: + * {string[]} bookmarks + * {string} _collection_id + */ + const parse_uri = function (uri) { + var cut_index = uri.indexOf("#" + uri_marker); + if (cut_index > -1) { + // get collection id + const _collection_id = uri + .substring(cut_index + uri_marker.length + 1) + // remove query + const query_marker = uri.indexOf("?"); + if (query_marker > -1) { + cut_index = query_marker; + } + const uri_segments = uri.substring(0, cut_index).split("/") + const ids = uri_segments[uri_segments.length - 1].split("&"); + logger.debug("found ids in uri", ids); + return { + bookmarks: ids, + collection_id: _collection_id + }; } - const uri_segments = uri.substring(0, cut_index).split("/") - const ids = uri_segments[uri_segments.length - 1].split("&"); - logger.debug("found ids in uri", ids); - return { - bookmarks: ids, - collection_id: _collection_id - }; - } - return undefined; - } + return undefined; + } - /** - * Initialize all bookmark buttons which are children of scope. - * - * @param {HTMLElement|string} [scope="body"] - element or jquery selector. - */ - const init_bookmark_buttons = function (scope) { - logger.trace("enter init_bookmark_buttons", scope); - $(get_bookmark_buttons(scope)).each((idx, button) => { - init_button(button); - }); - } + /** + * Initialize all bookmark buttons which are children of scope. + * + * @param {HTMLElement|string} [scope="body"] - element or jquery selector. + */ + const init_bookmark_buttons = function (scope) { + logger.trace("enter init_bookmark_buttons", scope); + $(get_bookmark_buttons(scope)).each((idx, button) => { + init_button(button); + }); + } - /** - * Setter for the collection_id. - * - * @param {string} id - */ - const set_collection_id = function (id) { - collection_id = id; - } + /** + * Setter for the collection_id. + * + * @param {string} id + */ + const set_collection_id = function (id) { + collection_id = id; + } - /** - * Initialize this module. - */ - const init = async function (scope) { - logger.info("init ext_bookmarks"); - //add_bookmark_buttons(); - counter = 0; - const parsed_uri = parse_uri(window.location.href); - if (typeof parsed_uri != "undefined") { - // this hack removes the "#_bm" marker from the uri without - // reloading the page. - window.location.href = "#"; - - clear_bookmark_storage(); - collection_id = parsed_uri["collection_id"]; - add_all_bookmarks_to_storage(parsed_uri["bookmarks"]); - } - - init_bookmark_buttons(scope); - update_collection(); - if (edit_mode) { - window.document.body.addEventListener(edit_mode.end_edit.type, (e) => { - init_bookmark_buttons(e.target); - }, true); + /** + * Add a button to add all query results to bookmarks. + */ + const add_add_query_results_button = function () { + const button_html = $(`<div class="row"><p class="text-end">Bookmark all query results</p></div>`)[0]; + + // Add to query results box + $(".caosdb-query-response-heading").append(button_html); } - } - /** - * Construct the key for data items in the bookmark_storage. - */ - const get_data_key = function (id, data_key) { - return get_collection_prefix() + '_da_' + id + "_" + data_key; - } + /** + * Transform a given query it to a "SELECT ID FROM ..." query. + * + * @param {string} query_string + */ + const get_select_id_query_string = function (query_string) { - /** - * Get a specific data item which belongs to a bookmark. - * - * This is currently prominently used by the tsv-export. - * - * @param {string} id - the bookmarked id - * @param {string} data_key - an identifier for the data item to be - * retrieved. - * @returns {string} the `data_key` of bookmark `id`. - */ - const get_bookmark_data = async function (id, data_key) { - // get full key (needed for the cache) - const full_data_key = get_data_key(id, data_key); + } - // retrieve from cache - const cached = bookmark_storage[full_data_key]; - if (typeof cached != "undefined") { - return cached; + /** + * Initialize this module. + */ + const init = async function (scope) { + logger.info("init ext_bookmarks"); + //add_bookmark_buttons(); + counter = 0; + const parsed_uri = parse_uri(window.location.href); + if (typeof parsed_uri != "undefined") { + // this hack removes the "#_bm" marker from the uri without + // reloading the page. + window.location.href = "#"; + + clear_bookmark_storage(); + collection_id = parsed_uri["collection_id"]; + add_all_bookmarks_to_storage(parsed_uri["bookmarks"]); + } + + init_bookmark_buttons(scope); + update_collection(); + if (edit_mode) { + window.document.body.addEventListener(edit_mode.end_edit.type, (e) => { + init_bookmark_buttons(e.target); + }, true); + } + + if (true) { + // if ("${BUILD_MODULE_EXT_ADD_QUERY_TO_BOOKMARKS}" == "ENABLED") { + add_add_query_results_button(); + } } - // not in cache, try the data_getters - var uncached = undefined - if (data_getters[data_key]) { - uncached = (await data_getters[data_key](id)) + /** + * Construct the key for data items in the bookmark_storage. + */ + const get_data_key = function (id, data_key) { + return get_collection_prefix() + '_da_' + id + "_" + data_key; } + /** + * Get a specific data item which belongs to a bookmark. + * + * This is currently prominently used by the tsv-export. + * + * @param {string} id - the bookmarked id + * @param {string} data_key - an identifier for the data item to be + * retrieved. + * @returns {string} the `data_key` of bookmark `id`. + */ + const get_bookmark_data = async function (id, data_key) { + // get full key (needed for the cache) + const full_data_key = get_data_key(id, data_key); + + // retrieve from cache + const cached = bookmark_storage[full_data_key]; + if (typeof cached != "undefined") { + return cached; + } + + // not in cache, try the data_getters + var uncached = undefined + if (data_getters[data_key]) { + uncached = (await data_getters[data_key](id)) + } // don't cache if getting the information is trivial or there are other // reasons why this is in the data_no_cache array. if (data_no_cache.indexOf(data_key) == -1) { diff --git a/test/core/js/modules/ext_bookmarks.js.js b/test/core/js/modules/ext_bookmarks.js.js index 831df74231e479d5b4550524f6bc0da617c98fb3..f92c51dfd6aee5b27767649cdbcd2c2335740d2c 100644 --- a/test/core/js/modules/ext_bookmarks.js.js +++ b/test/core/js/modules/ext_bookmarks.js.js @@ -179,3 +179,10 @@ QUnit.test("bookmark buttons", function (assert) { inside_buttons.remove(); outside_button.remove(); }); + +QUnit.test("select-query transformation", function (assert) { + assert.equal(ext_bookmarks.get_select_id_query_string("FIND analysis"), "SELECT ID FROM analysis"); + assert.equal(ext_bookmarks.get_select_id_query_string("FIND RECORD analysis WHICH HAS A date > 2012"), "SELECT ID FROM RECORD analysis WHICH HAS A date > 2012"); + assert.equal(ext_bookmarks.get_select_id_query_string("SELECT name, date FROM analysis"), "SELECT ID FROM analysis"); + assert.equal(ext_bookmarks.get_select_id_query_string("COUNT analysis"), "SELECT ID FROM analysis"); +});