From d05c7394da810ffe415530ecdab688baa2105592 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com>
Date: Wed, 17 Jul 2024 17:38:49 +0200
Subject: [PATCH] STY: styling only

---
 src/core/js/ext_references.js | 980 +++++++++++++++++-----------------
 1 file changed, 498 insertions(+), 482 deletions(-)

diff --git a/src/core/js/ext_references.js b/src/core/js/ext_references.js
index e25de210..13210b60 100644
--- a/src/core/js/ext_references.js
+++ b/src/core/js/ext_references.js
@@ -22,7 +22,6 @@
  * ** end header
  */
 
-
 /*
  * Check if an element is out of the viewport
  *
@@ -33,8 +32,7 @@
  * @param {Node} elem - The element to check
  * @return {Object} A set of booleans for each side of the element.
  */
-var isOutOfViewport = function (elem) {
-
+var isOutOfViewport = function(elem) {
     // Get element's bounding
     var bounding = elem.getBoundingClientRect();
 
@@ -42,18 +40,15 @@ var isOutOfViewport = function (elem) {
     var out = {};
     out.top = bounding.top < 0;
     out.left = bounding.left < 0;
-    out.bottom = bounding.bottom > (window.innerHeight ||
-  document.documentElement.clientHeight);
+    out.bottom = bounding.bottom >
+        (window.innerHeight || document.documentElement.clientHeight);
     out.right = bounding.right >
-  (window.innerWidth || document.documentElement.clientWidth);
-    out.any =
-  out.top || out.left || out.bottom || out.right;
-    out.all = out.top &&
-  out.left && out.bottom && out.right;
+        (window.innerWidth || document.documentElement.clientWidth);
+    out.any = out.top || out.left || out.bottom || out.right;
+    out.all = out.top && out.left && out.bottom && out.right;
     return out;
 };
 
-
 /**
  * @module reference_list_summary
  * @version 0.1
@@ -63,117 +58,114 @@ var isOutOfViewport = function (elem) {
  *
  * @author Timm Fitschen
  */
-var reference_list_summary = new function () {
-
-    var logger = log.getLogger("reference_list_summary");
-
-    /** Return a condensed string representation of an array of integers.
-     *
-     * @example simplify_integer_numbers([1,2,3,5,8,9,10]) returns "1-3, 5,
-     * 8-10"
-     *
-     * @example simplify_integer_numbers([]) returns ""
-     *
-     * @example simplify_integer_numbers([1]) returns "1"
-     *
-     * @example simplify_integer_numbers([1,2]) returns "1, 2" because an array
-     * with two elements gets a special treatment.
-     *
-     * The array may be unsorted.  simplify_integer_numbers([1,2,3]) returns
-     * "1-3" simplify_integer_numbers([2,1,3]) returns "1-3" as well
-     *
-     * The array may contain duplicates.  simplify_integer_numbers([1,2,2,3])
-     * returns "1-3"
-     *
-     * @param {numbers} array - unsorted array of integers, possibly with
-     * duplicates.  @return {string} a condensed string representation of the
-     * array.
-     */
-    this.simplify_integer_numbers = function (array) {
-  logger.trace("enter simplify_integer_numbers", array);
-  var set = Array.from(new Set(array));
-
-  if (set.length === 0) {
-      return ""
-  } else if (set.length === 1) {
-      return `${set[0]}`;
-  }
-
-  // sort numerically
-  set.sort((a, b) => a - b);
-
-  if (set.length === 2) {
-      return `${set[0]}, ${set[1]}`;
-  }
-
-
-  var ret = `${set[0]}`;
-  var last = undefined;
-  // set[0];
-
-  // e.g. [1,2,3,4,5,8,9,10];
-  for (const next of set) {
-      // append '-' to summarize consecutive numbers
-      if (next - last === 1 && !ret.endsWith("-")) {
-    ret += "-";
-      }
-
-      if (next - last > 1) {
-
-    if (ret.endsWith("-")) {
-        // close previous interval and start new
-        ret += `${last}, ${next}`;
-    } else {
-        // no previous interval, start interval.
-        ret += `, ${next}`;
-    }
-      } else if (next === set[set.length - 1]) {
-    // finish interval if next is last item
-    ret += next;
-    break;
-      }
-
-
-      last = next;
-
-  }
-
-  // e.g. "1-5, 8-10"
-  return ret;
-    }
-
-    /**
-     * Generate a summary of all reference_infos by calling the callback
-     * function of the first reference_info in the array.
-     *
-     * The summary is appended to the summary_container if available and
-     * returned (for testing purposes).
-     *
-     * Returns undefined, if ref_infos is empty or does not come with a
-     * callback function.
-     *
-     * @param {reference_info[]} ref_infos - array of reference_infos.
-     * @param {HTMLElement} summary_container - the summary is appended to this
-     *     element.
-     * @return {HTMLElement|string} generated summary
-     */
-    this.generate = function (ref_infos, summary_container) {
-  logger.trace("enter generate", ref_infos);
-  if (ref_infos.length > 0 &&
-      typeof ref_infos[0].callback === "function") {
-      const summary =
-    ref_infos[0].callback(ref_infos);
-      if (summary && summary_container) {
-    $(summary_container).append(summary);
-      }
-      logger.trace("leave generate", summary);
-      return summary;
-  }
-  logger.trace("leave generate, return undefined");
-  return undefined;
-    }
-}
-
+var reference_list_summary =
+    new function() {
+        var logger = log.getLogger("reference_list_summary");
+
+        /**
+         * Return a condensed string representation of an array of integers.
+         *
+         * @example simplify_integer_numbers([1,2,3,5,8,9,10]) returns "1-3, 5,
+         * 8-10"
+         *
+         * @example simplify_integer_numbers([]) returns ""
+         *
+         * @example simplify_integer_numbers([1]) returns "1"
+         *
+         * @example simplify_integer_numbers([1,2]) returns "1, 2" because an
+         * array with two elements gets a special treatment.
+         *
+         * The array may be unsorted.  simplify_integer_numbers([1,2,3]) returns
+         * "1-3" simplify_integer_numbers([2,1,3]) returns "1-3" as well
+         *
+         * The array may contain duplicates.  simplify_integer_numbers([1,2,2,3])
+         * returns "1-3"
+         *
+         * @param {numbers} array - unsorted array of integers, possibly with
+         * duplicates.  @return {string} a condensed string representation of the
+         * array.
+         */
+        this.simplify_integer_numbers =
+            function(array) {
+                logger.trace("enter simplify_integer_numbers", array);
+                var set = Array.from(new Set(array));
+
+                if (set.length === 0) {
+                    return "";
+                } else if (set.length === 1) {
+                    return `${set[0]}`;
+                }
+
+                // sort numerically
+                set.sort((a, b) => a - b);
+
+                if (set.length === 2) {
+                    return `${set[0]}, ${set[1]}`;
+                }
+
+                var ret = `${set[0]}`;
+                var last = undefined;
+                // set[0];
+
+                // e.g. [1,2,3,4,5,8,9,10];
+                for (const next of set) {
+                    // append '-' to summarize consecutive numbers
+                    if (next - last === 1 && !ret.endsWith("-")) {
+                        ret += "-";
+                    }
+
+                    if (next - last > 1) {
+
+                        if (ret.endsWith("-")) {
+                            // close previous interval and start new
+                            ret += `${last}, ${next}`;
+                        } else {
+                            // no previous interval, start interval.
+                            ret += `, ${next}`;
+                        }
+                    } else if (next === set[set.length - 1]) {
+                        // finish interval if next is last item
+                        ret += next;
+                        break;
+                    }
+
+                    last = next;
+                }
+
+                // e.g. "1-5, 8-10"
+                return ret;
+            };
+
+        /**
+         * Generate a summary of all reference_infos by calling the callback
+         * function of the first reference_info in the array.
+         *
+         * The summary is appended to the summary_container if available and
+         * returned (for testing purposes).
+         *
+         * Returns undefined, if ref_infos is empty or does not come with a
+         * callback function.
+         *
+         * @param {reference_info[]} ref_infos - array of reference_infos.
+         * @param {HTMLElement} summary_container - the summary is appended to
+         *     this element.
+         * @return {HTMLElement|string} generated summary
+         */
+        this.generate = function(ref_infos, summary_container) {
+            logger.trace("enter generate", ref_infos);
+            if (ref_infos.length > 0 &&
+                typeof ref_infos[0].callback === "function") {
+                const summary = ref_infos[0].callback(ref_infos);
+                if (summary && summary_container) {
+                    $(summary_container).append(summary);
+                }
+                logger.trace("leave generate", summary);
+                return summary;
+            }
+            logger.trace("leave generate, return undefined");
+            return undefined;
+        };
+    };
 
 /**
  * @module resolve_references
@@ -184,366 +176,390 @@ var reference_list_summary = new function () {
  * @author Timm Fitschen
  * @author Alexander Schlemmer
  */
-var resolve_references = new function () {
-
-    var logger = log.getLogger("resolve_references");
-
-    var _scroll_timeout = undefined;
-
-    // bind global function to local context for unit testing
-    this.retrieve = retrieve;
-    this.getParents = getParents;
-
-    /**
-     * This event is dispatched on the summary container after the summary has
-     * been generated and appended to the container.
-     */
-    this.summary_ready_event = new Event("resolve_references.summary.ready");
-
-    /**
-     * Scroll listener calls {@link resolve_references.update_visible_references} 500 milliseconds after the
-     * last scroll event.
-     */
-    var scroll_listener = () => {
-  if (_scroll_timeout) {
-      clearTimeout(_scroll_timeout);
-  }
-  _scroll_timeout = setTimeout(function () {
-      resolve_references.update_visible_references();
-  }, 500);
+var resolve_references =
+    new function() {
+        var logger = log.getLogger("resolve_references");
+
+        var _scroll_timeout = undefined;
+
+        // bind global function to local context for unit testing
+        this.retrieve = retrieve;
+        this.getParents = getParents;
+
+        /**
+         * This event is dispatched on the summary container after the summary has
+         * been generated and appended to the container.
+         */
+        this.summary_ready_event = new Event("resolve_references.summary.ready");
+
+        /**
+         * Scroll listener calls {@link
+         * resolve_references.update_visible_references} 500 milliseconds after
+         * the last scroll event.
+         */
+        var scroll_listener = () => {
+            if (_scroll_timeout) {
+                clearTimeout(_scroll_timeout);
+            }
+            _scroll_timeout = setTimeout(
+                function() {
+                    resolve_references.update_visible_references();
+                },
+                500);
+        };
+
+        /**
+         * Initilize the scroll listener which triggers the resolution of the
+         * entity ids and trigger it for the first time in order to resolve all
+         * visible references.
+         */
+        this.init =
+            function() {
+                if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
+                    scroll_listener();
+
+                    // mainly for vertical scrolling
+                    $(window).scroll(scroll_listener);
+
+                    // for horizontal scrolling.
+                    $(".caosdb-value-list").scroll(scroll_listener);
+                }
+            };
+
+        /**
+         * Check if an element is inside of the viewport on the vertical axis.
+         *
+         * Returns true if any of the element's bounding box's top or bottom
+         * edges are in the viewport.
+         *
+         * @param {HTMLElement} elem - the element to check @return {boolean}
+         *
+         */
+        this.is_in_viewport_vertically =
+            function(elem) {
+                var out = isOutOfViewport(elem);
+                return !(out.top || out.bottom);
+            };
+
+        /**
+         * Check if an element is inside of the viewport on the
+         * horizontal axis.
+         *
+         * This criterion is very much different from the vertical
+         * correspondent: It looks for the parent which contains the list
+         * scroll bar for LinkAhead values. Then it is checked whether the
+         * element's bounding box is visible inside the scroll box.
+         *
+         * @param {HTMLElement} elem - the element to
+         *     check @return {boolean}
+         *
+         */
+        this.is_in_viewport_horizontally =
+            function(elem) {
+                var scrollbox = elem.parentElement.parentElement;
+                // Check this condition only if the grand parent is a list and return
+                // true otherwise.
+                if (scrollbox.classList.contains("caosdb-value-list") == true) {
+                    var boundel = elem.getBoundingClientRect();
+                    var boundscroll = scrollbox.getBoundingClientRect();
+                    var leftcrit = boundel.right > boundscroll.left;
+                    var rightcrit = boundel.left < boundscroll.right;
+                    return leftcrit && rightcrit;
+                } else {
+                    return true;
+                }
+            };
+
+        /**
+         * Return true iff the entity has at least one direct parent
+         * named `par`.
+         *
+         * @param {HTMLElement} entity - entity in HTML
+         *     representation.  @param
+         * {string} par - parent name.  @return {boolean}
+         */
+        this.is_child =
+            function(entity, par) {
+                var pars = resolve_references.getParents(entity);
+                for (const thispar of pars) {
+                    if (thispar.name === par) {
+                        return true;
+                    }
+                }
+                return false;
+            };
+
+        /**
+         * @typedef {reference_info}
+         * @property {string} text
+         * @property {function} callback - a callback function
+         * with one parameter
+         *     (`data`) which generates a summary from the array
+         * of data objects which are passed to this function.
+         * @property {object} data - an object with properties
+         * which can be understood and used by the `callback`
+         * function.
+         */
+
+        /**
+         * Return a reference_info for an entity.
+         *
+         * You may add your own custom resolver by specifying a JS
+         * module via the `BUILD_EXT_REFERENCES_CUSTOM_RESOLVER`
+         * build variable. The custom resolver has to be a JS
+         * module (typically located at
+         * linkahead-webui/src/ext/js), the name of which is given
+         * as the value of `BUILD_EXT_REFERENCES_CUSTOM_RESOLVER`.
+         * It has to provide a `resolve` function that takes the
+         * entity id to be resolved as a string and returns a
+         * `reference_info` object with the resolved custom
+         * reference as a `text` property.
+         *
+         * See
+         * linkahead-webui/src/core/js/reference_resolver/caosdb_default_person.js
+         * for an example.
+         *
+         * TODO refactor to be configurable.
+         * @async @param {string} id - the id of the entity which
+         * is to be resolved.  @return {reference_info}
+         */
+        this.resolve_reference =
+            async function(id) {
+                const custom_reference_resolver =
+                    window["${BUILD_EXT_REFERENCES_CUSTOM_RESOLVER}"];
+                if (custom_reference_resolver &&
+                    typeof custom_reference_resolver.resolve === "function") {
+                    // try custom_reference_resolver and fall-back to standard
+                    // implementation
+                    var ret = await custom_reference_resolver.resolve(id);
+                    if (ret) {
+                        return ret;
+                    }
+                }
+
+
+
+                const entity = (await resolve_references.retrieve(id))[0];
+
+                // TODO handle multiple parents
+                const par = resolve_references.getParents(entity)[0] || {};
+                const name = getEntityName(entity);
+
+                var ret = {
+                    "text": id
+                };
+                if (par.name === "TestReferenced" &&
+                    typeof resolve_references.test_resolver === "function") {
+                    // this is a test case, initialized by the test suite.
+                    ret = resolve_references.test_resolver(entity);
+                } else if (typeof name !== "undefined" && name.length > 0) {
+                    ret["text"] = name;
+                } else if (getEntityHeadingAttribute(entity, "path") !== undefined ||
+                    par.name == "Image") {
+                    // show file name
+                    var pths = getEntityHeadingAttribute(entity, "path").split("/");
+                    ret["text"] = pths[pths.length - 1];
+                }
+
+                return ret;
+            };
+
+        this._target_class =
+            "caosdb-resolve-reference-target";
+
+        /**
+         * Add a target span where the resolved reference information can be
+         * shown.
+         *
+         * If the element has a target yet, the existing one is returned.
+         *
+         * @param {HTMLElement} element - where to append the target.
+         * @return {HTMLElement} the new/existing target element.
+         */
+        this.add_target =
+            function(element) {
+                if (element.getElementsByClassName(this._target_class).length > 0) {
+                    return element.getElementsByClassName(this._target_class);
+                } else {
+                    return $(`<span class="${this._target_class}"/>`)
+                        .appendTo(element)[0];
+                }
+            };
+
+        /**
+         * Retrieve meaningful information for a single
+         * caosdb-f-reference-value element.
+         *
+         * @param {HTMLElement} rs - resolvable reference link
+         * @return {reference_info} the resolved reference information
+         */
+        this.update_single_resolvable_reference =
+            async function(rs) {
+                $(rs).find(".caosdb-id-button").hide();
+                const target = resolve_references.add_target(rs);
+                const id = getEntityID(rs);
+                target.textContent = id;
+                const resolved_entity_info =
+                    (await resolve_references.resolve_reference(id));
+                target.textContent = resolved_entity_info.text;
+                return resolved_entity_info;
+            };
+
+        /**
+         * Add a summary field to the the list_values element.
+         *
+         * A summary field is a DIV element with class
+         * `caosdb-resolve-reference-summary`. The summary field is used
+         * to display a condensed string representation of the referenced
+         * entities in the list-property.
+         *
+         * @param {HTMLElement} list_values - where to add the summary
+         *     field.
+         * @return {HTMLElement} a summary field.
+         */
+        this.add_summary_field =
+            function(list_values) {
+                const summary =
+                    $(`<div class="${resolve_references._summary_class}"/>`);
+                $(list_values).prepend(summary);
+                return summary[0];
+            };
+
+        this._summary_class = "caosdb-resolve-reference-summary";
+
+        /**
+         * All references which have not yet been resolved are contained in an
+         * HTML Element with this css class.
+         */
+        this._unresolved_class_name = "caosdb-resolvable-reference";
+
+        this.get_resolvable_properties =
+            function(container) {
+                const _unresolved_class_name = this._unresolved_class_name;
+                return $(container)
+                    .find(".caosdb-f-property-value")
+                    .has(`.${_unresolved_class_name}`)
+                    .toArray();
+            };
+
+        /**
+         * This function updates all references in the body which are inside
+         * of the current viewport.
+         *
+         * If the optional container parameter is given, only elements inside
+         * the container are being processed.
+         *
+         * @param {HTMLElement} container
+         */
+        this.update_visible_references = async function(container) {
+            const property_values = resolve_references.get_resolvable_properties(
+                container || document.body);
+
+            const _unresolved_class_name =
+                resolve_references._unresolved_class_name;
+
+            // Loop over all property values in the container element. Note: each
+            // property_value can be a single reference or a list of references.
+            for (const property_value of property_values) {
+                var lists = findElementByConditions(
+                    property_value, x => x.classList.contains("caosdb-value-list"),
+                    x => x.classList.contains("caosdb-preview-container"));
+                lists = $(lists).has(`.${_unresolved_class_name}`);
+
+                if (lists.length > 0) {
+                    logger.debug("processing list of references", lists);
+
+                    for (var i = 0; i < lists.length; i++) {
+                        const list = lists[i];
+                        if (resolve_references.is_in_viewport_vertically(list)) {
+                            const rs = $(list)
+                                .find(`.${_unresolved_class_name}`)
+                                .toggleClass(_unresolved_class_name, false);
+
+                            // First resolve only one reference. If the `ref_info`
+                            // indicates that a summary is to be generated from the
+                            // list of references, retrieve all other other
+                            // references. Otherwise retrieve only those which are
+                            // visible in the viewport horizontally and trigger the
+                            // retrieval of the others when they are scrolled into
+                            // the view port.
+                            const first_ref_info =
+                                await resolve_references.update_single_resolvable_reference(
+                                    rs[0]);
+
+                            first_ref_info["index"] = 0;
+
+                            if (typeof first_ref_info.callback === "function") {
+                                // there is a callback function, hence we need to
+                                // generate a summary.
+                                logger.debug("loading all references for summary", rs);
+                                const summary_field =
+                                    resolve_references.add_summary_field(property_value);
+
+                                // collect ref infos for the summary
+                                const ref_infos = [first_ref_info];
+                                for (var j = 1; j < rs.length; j++) {
+                                    const ref_info =
+                                        resolve_references.update_single_resolvable_reference(
+                                            rs[j]);
+                                    ref_info["index"] = j;
+                                    ref_infos.push(ref_info);
+                                }
+
+                                // wait for resolution of references,
+                                // then generate the summary,
+                                // dispatch event when ready.
+                                Promise.all(ref_infos)
+                                    .then(_ref_infos => {
+                                        reference_list_summary.generate(_ref_infos,
+                                            summary_field);
+                                    })
+                                    .then(() => {
+                                        summary_field.dispatchEvent(
+                                            resolve_references.summary_ready_event);
+                                    })
+                                    .catch((err) => {
+                                        logger.error(err);
+                                    });
+
+                            } else {
+                                // no summary to be generated
+
+                                logger.debug("lazy loading references", rs);
+                                for (var j = 1; j < rs.length; j++) {
+                                    // mark others to be loaded later and only if
+                                    // visible
+                                    $(rs[j]).toggleClass(_unresolved_class_name, true);
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Load all remaining references. These are single reference values
+                // and those references from lists which are left for lazy loading.
+                const rs = findElementByConditions(
+                    property_value,
+                    x => x.classList.contains(`${_unresolved_class_name}`),
+                    x => x.classList.contains("caosdb-preview-container"));
+                for (var i = 0; i < rs.length; i++) {
+                    if (resolve_references.is_in_viewport_vertically(rs[i]) &&
+                        resolve_references.is_in_viewport_horizontally(rs[i])) {
+                        logger.debug("processing single references", rs);
+                        $(rs[i]).toggleClass(_unresolved_class_name, false);
+
+                        // discard return value as it is not needed for any summary
+                        // generation as above.
+                        resolve_references.update_single_resolvable_reference(rs[i]);
+                    }
+                }
+            }
+        };
     };
 
-
-    /**
-     * Initilize the scroll listener which triggers the resolution of the
-     * entity ids and trigger it for the first time in order to resolve all
-     * visible references.
-     */
-    this.init = function () {
-  if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
-      scroll_listener();
-
-      // mainly for vertical scrolling
-      $(window).scroll(scroll_listener);
-
-      // for horizontal scrolling.
-      $(".caosdb-value-list").scroll(scroll_listener);
-  }
-    }
-
-    /**
-     * Check if an element is inside of the viewport on the vertical axis.
-     *
-     * Returns true if any of the element's bounding box's top or bottom edges are
-     * in the viewport.
-     *
-     * @param {HTMLElement} elem - the element to check @return {boolean}
-     *
-     */
-    this.is_in_viewport_vertically = function (elem) {
-  var out =
-      isOutOfViewport(elem);
-  return !(out.top || out.bottom);
-    }
-
-    /** Check if an element is inside of the viewport on the horizontal axis.
-     *
-     * This criterion is very much different from the vertical correspondent: It
-     * looks for the parent which contains the list scroll bar for LinkAhead values.
-     * Then it is checked whether the element's bounding box is visible inside the
-     * scroll box.
-     *
-     * @param {HTMLElement} elem - the element to check @return {boolean}
-     *
-     */
-    this.is_in_viewport_horizontally = function (elem) {
-  var scrollbox = elem.parentElement.parentElement;
-  // Check this condition only if the grand parent is a list and return true
-  // otherwise.
-  if (scrollbox.classList.contains("caosdb-value-list") ==
-      true) {
-      var boundel = elem.getBoundingClientRect();
-      var boundscroll = scrollbox.getBoundingClientRect();
-      var leftcrit = boundel.right > boundscroll.left;
-      var rightcrit = boundel.left < boundscroll.right;
-      return leftcrit && rightcrit;
-  } else {
-      return true;
-  }
-    }
-
-
-    /**
-     * Return true iff the entity has at least one direct parent named `par`.
-     *
-     * @param {HTMLElement} entity - entity in HTML representation.  @param
-     * {string} par - parent name.  @return {boolean}
-     */
-    this.is_child = function (entity, par) {
-  var pars = resolve_references.getParents(entity);
-  for (const thispar of pars) {
-      if (thispar.name === par) {
-    return true;
-      }
-  }
-  return false;
-    }
-
-    /**
-     * @typedef {reference_info}
-     * @property {string} text
-     * @property {function} callback - a callback function with one parameter
-     *     (`data`) which generates a summary from the array of data objects
-     *     which are passed to this function.
-     * @property {object} data - an object with properties which can be
-     *     understood and used by the `callback` function.
-     */
-
-    /**
-     * Return a reference_info for an entity.
-     *
-     * You may add your own custom resolver by specifying a JS module
-     * via the `BUILD_EXT_REFERENCES_CUSTOM_RESOLVER` build
-     * variable. The custom resolver has to be a JS module (typically
-     * located at linkahead-webui/src/ext/js), the name of which is given
-     * as the value of `BUILD_EXT_REFERENCES_CUSTOM_RESOLVER`. It has
-     * to provide a `resolve` function that takes the entity id to be
-     * resolved as a string and returns a `reference_info` object with
-     * the resolved custom reference as a `text` property.
-     *
-     * See linkahead-webui/src/core/js/reference_resolver/caosdb_default_person.js
-     * for an example.
-     *
-     * TODO refactor to be configurable.  @async @param {string} id - the id of
-     * the entity which is to be resolved.  @return {reference_info}
-     */
-    this.resolve_reference = async function (id) {
-  const custom_reference_resolver = window["${BUILD_EXT_REFERENCES_CUSTOM_RESOLVER}"];
-  if (custom_reference_resolver && typeof custom_reference_resolver.resolve === "function") {
-      // try custom_reference_resolver and fall-back to standard implementation
-      var ret = await custom_reference_resolver.resolve(id);
-      if (ret) {
-        return ret;
-      }
-  }
-
-  const entity = (await resolve_references.retrieve(id))[0];
-
-  // TODO handle multiple parents
-  const par = resolve_references.getParents(entity)[0] || {};
-  const name = getEntityName(entity);
-
-  var ret = {
-      "text": id
-  };
-  if (par.name === "TestReferenced" && typeof resolve_references.test_resolver === "function") {
-      // this is a test case, initialized by the test suite.
-      ret = resolve_references.test_resolver(entity);
-  } else if (typeof name !== "undefined" && name.length > 0){
-    ret["text"] = name;
-  } else if (getEntityHeadingAttribute(entity, "path") !==
-      undefined || par.name == "Image") {
-      // show file name
-      var pths = getEntityHeadingAttribute(entity, "path")
-    .split("/");
-      ret["text"] = pths[pths.length - 1];
-  }
-
-
-  return ret;
-    }
-
-
-    this._target_class = "caosdb-resolve-reference-target";
-
-    /**
-     * Add a target span where the resolved reference information can be shown.
-     *
-     * If the element has a target yet, the existing one is returned.
-     *
-     * @param {HTMLElement} element - where to append the target.
-     * @return {HTMLElement} the new/existing target element.
-     */
-    this.add_target = function (element) {
-  if(element.getElementsByClassName(this._target_class).length > 0){
-      return element.getElementsByClassName(this._target_class);
-  } else {
-      return $(`<span class="${this._target_class}"/>`)
-    .appendTo(element)[0];
-  }
-    }
-
-    /**
-     * Retrieve meaningful information for a single caosdb-f-reference-value
-     * element.
-     *
-     * @param {HTMLElement} rs - resolvable reference link
-     * @return {reference_info} the resolved reference information
-     */
-    this.update_single_resolvable_reference = async function (rs) {
-  $(rs).find(".caosdb-id-button").hide();
-  const target = resolve_references.add_target(rs);
-  const id = getEntityID(rs);
-  target.textContent = id;
-  const resolved_entity_info = (
-      await resolve_references.resolve_reference(id));
-  target.textContent = resolved_entity_info.text;
-  return resolved_entity_info;
-    }
-
-
-    /**
-     * Add a summary field to the the list_values element.
-     *
-     * A summary field is a DIV element with class
-     * `caosdb-resolve-reference-summary`. The summary field is used to display
-     * a condensed string representation of the referenced entities in the
-     * list-property.
-     *
-     * @param {HTMLElement} list_values - where to add the summary field.
-     * @return {HTMLElement} a summary field.
-     */
-    this.add_summary_field = function (list_values) {
-  const summary = $(
-      `<div class="${resolve_references._summary_class}"/>`);
-  $(list_values).prepend(summary);
-  return summary[0];
-    }
-
-    this._summary_class = "caosdb-resolve-reference-summary";
-
-    /**
-     * All references which have not yet been resolved are contained in an HTML
-     * Element with this css class.
-     */
-    this._unresolved_class_name = "caosdb-resolvable-reference";
-
-    this.get_resolvable_properties = function (container) {
-  const _unresolved_class_name = this._unresolved_class_name;
-  return $(container).find(".caosdb-f-property-value").has(
-      `.${_unresolved_class_name}`).toArray();
-    }
-
-
-    /**
-     * This function updates all references in the body which are inside of the
-     * current viewport.
-     *
-     * If the optional container parameter is given, only elements inside the
-     * container are being processed.
-     *
-     * @param {HTMLElement} container
-     */
-    this.update_visible_references = async function (container) {
-  const property_values = resolve_references
-      .get_resolvable_properties(container || document.body);
-
-  const _unresolved_class_name = resolve_references
-      ._unresolved_class_name;
-
-  // Loop over all property values in the container element. Note: each property_value can be a single reference or a list of references.
-  for (const property_value of property_values) {
-      var lists = findElementByConditions(
-    property_value,
-    x => x.classList.contains("caosdb-value-list"),
-    x => x.classList.contains("caosdb-preview-container"))
-      lists = $(lists).has(`.${_unresolved_class_name}`);
-
-      if (lists.length > 0) {
-    logger.debug("processing list of references", lists);
-
-    for (var i = 0; i < lists.length; i++) {
-        const list = lists[i];
-        if (resolve_references
-          .is_in_viewport_vertically(list)) {
-      const rs = $(list).find(
-        `.${_unresolved_class_name}`)
-          .toggleClass(_unresolved_class_name, false);
-
-      // First resolve only one reference. If the `ref_info`
-      // indicates that a summary is to be generated from the
-      // list of references, retrieve all other other
-      // references. Otherwise retrieve only those which are
-      // visible in the viewport horizontally and trigger the
-      // retrieval of the others when they are scrolled into
-      // the view port.
-      const first_ref_info = await resolve_references
-          .update_single_resolvable_reference(rs[0]);
-
-      first_ref_info["index"] = 0;
-
-      if (typeof first_ref_info.callback === "function") {
-          // there is a callback function, hence we need to
-          // generate a summary.
-          logger.debug("loading all references for summary",
-        rs);
-          const summary_field = resolve_references
-        .add_summary_field(property_value);
-
-          // collect ref infos for the summary
-          const ref_infos = [first_ref_info];
-          for (var j = 1; j < rs.length; j++) {
-        const ref_info = resolve_references
-            .update_single_resolvable_reference(rs[j]);
-        ref_info["index"] = j;
-        ref_infos.push(ref_info);
-          }
-
-          // wait for resolution of references,
-          // then generate the summary,
-          // dispatch event when ready.
-          Promise.all(ref_infos)
-        .then(_ref_infos => {reference_list_summary
-            .generate(_ref_infos, summary_field);})
-        .then(() => {
-            summary_field.dispatchEvent(
-          resolve_references
-              .summary_ready_event
-            );})
-        .catch((err) => {
-            logger.error(err);
-        })
-
-      } else {
-          // no summary to be generated
-
-          logger.debug("lazy loading references", rs);
-          for (var j = 1; j < rs.length; j++) {
-        // mark others to be loaded later and only if
-        // visible
-        $(rs[j]).toggleClass(_unresolved_class_name, true);
-          }
-      }
+$(document)
+    .ready(function() {
+        if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
+            caosdb_modules.register(resolve_references);
         }
-    }
-      }
-
-      // Load all remaining references. These are single reference values
-      // and those references from lists which are left for lazy loading.
-      const rs = findElementByConditions(
-    property_value,
-    x => x.classList.contains(`${_unresolved_class_name}`),
-    x => x.classList.contains("caosdb-preview-container"));
-      for (var i = 0; i < rs.length; i++) {
-    if (resolve_references.is_in_viewport_vertically(
-      rs[i]) &&
-        resolve_references.is_in_viewport_horizontally(
-      rs[i])) {
-        logger.debug("processing single references", rs);
-        $(rs[i]).toggleClass(_unresolved_class_name, false);
-
-        // discard return value as it is not needed for any summary
-        // generation as above.
-        resolve_references.update_single_resolvable_reference(rs[i]);
-    }
-      }
-  }
-    }
-}
-
-
-$(document).ready(function () {
-    if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
-  caosdb_modules.register(resolve_references);
-    }
-});
+    });
-- 
GitLab