From a3f95af2214ba8e4f519b1828f9eb446da3b5828 Mon Sep 17 00:00:00 2001
From: florian <f.spreckelsen@inidscale.com>
Date: Mon, 13 Sep 2021 12:36:11 +0200
Subject: [PATCH] ENH: Move person-reference resolver to separate module

---
 build.properties.d/00_default.properties |   4 +-
 src/core/js/ext_references.js            | 504 +++++++++++------------
 src/ext/js/person_reference_resover.js   |  24 +-
 3 files changed, 267 insertions(+), 265 deletions(-)

diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index 58d20c52..1c505bcc 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -42,7 +42,6 @@
 # Modules enabled/disabled by default
 ##############################################################################
 BUILD_MODULE_EXT_PREVIEW=ENABLED
-BUILD_MODULE_EXT_RESOLVE_REFERENCES=ENABLED
 BUILD_MODULE_EXT_SSS_MARKDOWN=DISABLED
 BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM=DISABLED
 BUILD_MODULE_EXT_AUTOCOMPLETE=ENABLED
@@ -55,6 +54,9 @@ BUILD_MODULE_EXT_ANNOTATION=ENABLED
 BUILD_MODULE_USER_MANAGEMENT=ENABLED
 BUILD_MODULE_USER_MANAGEMENT_CHANGE_OWN_PASSWORD_REALM=CaosDB
 
+BUILD_MODULE_EXT_RESOLVE_REFERENCES=ENABLED
+BUILD_EXT_REFERENCES_CUSTOM_RESOLVER=person_reference
+
 ##############################################################################
 # Navbar properties
 ##############################################################################
diff --git a/src/core/js/ext_references.js b/src/core/js/ext_references.js
index bb4f10ed..8a4db7ea 100644
--- a/src/core/js/ext_references.js
+++ b/src/core/js/ext_references.js
@@ -43,13 +43,13 @@ var isOutOfViewport = function (elem) {
     out.top = bounding.top < 0;
     out.left = bounding.left < 0;
     out.bottom = bounding.bottom > (window.innerHeight ||
-        document.documentElement.clientHeight);
+	document.documentElement.clientHeight);
     out.right = bounding.right >
-        (window.innerWidth || document.documentElement.clientWidth);
+	(window.innerWidth || document.documentElement.clientWidth);
     out.any =
-        out.top || out.left || out.bottom || out.right;
+	out.top || out.left || out.bottom || out.right;
     out.all = out.top &&
-        out.left && out.bottom && out.right;
+	out.left && out.bottom && out.right;
     return out;
 };
 
@@ -90,56 +90,56 @@ var reference_list_summary = new function () {
      * array.
      */
     this.simplify_integer_numbers = function (array) {
-        logger.trace("enter simplify_integer_numbers", array);
-        var set = Array.from(new Set(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]}`;
-        }
+	if (set.length === 0) {
+	    return ""
+	} else if (set.length === 1) {
+	    return `${set[0]}`;
+	}
 
-        // sort numerically
-        set.sort((a, b) => a - b);
+	// sort numerically
+	set.sort((a, b) => a - b);
 
-        if (set.length === 2) {
-            return `${set[0]}, ${set[1]}`;
-        }
+	if (set.length === 2) {
+	    return `${set[0]}, ${set[1]}`;
+	}
 
 
-        var ret = `${set[0]}`;
-        var last = undefined;
-        // set[0];
+	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 += "-";
-            }
+	// 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 (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;
-            }
+		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;
+	    last = next;
 
-        }
+	}
 
-        // e.g. "1-5, 8-10"
-        return ret;
+	// e.g. "1-5, 8-10"
+	return ret;
     }
 
     /**
@@ -158,19 +158,19 @@ var reference_list_summary = new function () {
      * @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;
+	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;
     }
 }
 
@@ -205,12 +205,12 @@ var resolve_references = new function () {
      * last scroll event.
      */
     var scroll_listener = () => {
-        if (_scroll_timeout) {
-            clearTimeout(_scroll_timeout);
-        }
-        _scroll_timeout = setTimeout(function () {
-            resolve_references.update_visible_references();
-        }, 500);
+	if (_scroll_timeout) {
+	    clearTimeout(_scroll_timeout);
+	}
+	_scroll_timeout = setTimeout(function () {
+	    resolve_references.update_visible_references();
+	}, 500);
     };
 
 
@@ -220,15 +220,15 @@ var resolve_references = new function () {
      * visible references.
      */
     this.init = function () {
-        if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
-            scroll_listener();
+	if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
+	    scroll_listener();
 
-            // mainly for vertical scrolling
-            $(window).scroll(scroll_listener);
+	    // mainly for vertical scrolling
+	    $(window).scroll(scroll_listener);
 
-            // for horizontal scrolling.
-            $(".caosdb-value-list").scroll(scroll_listener);
-        }
+	    // for horizontal scrolling.
+	    $(".caosdb-value-list").scroll(scroll_listener);
+	}
     }
 
     /**
@@ -241,9 +241,9 @@ var resolve_references = new function () {
      *
      */
     this.is_in_viewport_vertically = function (elem) {
-        var out =
-            isOutOfViewport(elem);
-        return !(out.top || out.bottom);
+	var out =
+	    isOutOfViewport(elem);
+	return !(out.top || out.bottom);
     }
 
     /** Check if an element is inside of the viewport on the horizontal axis.
@@ -257,19 +257,19 @@ var resolve_references = new function () {
      *
      */
     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;
-        }
+	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;
+	}
     }
 
 
@@ -280,13 +280,13 @@ var resolve_references = new function () {
      * {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;
+	var pars = resolve_references.getParents(entity);
+	for (const thispar of pars) {
+	    if (thispar.name === par) {
+		return true;
+	    }
+	}
+	return false;
     }
 
     /**
@@ -309,8 +309,8 @@ var resolve_references = new function () {
      * 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. 
-     * 
+     * the resolved custom reference as a `text` property.
+     *
      * See caosdb-webui/src/ext/js/person_reference_resolver.js for an
      * example.
      *
@@ -318,40 +318,40 @@ var resolve_references = new function () {
      * 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] || {};
-
-        var ret = {
-            "text": id
-        };
-        if (getEntityHeadingAttribute(entity, "path") !==
-            undefined || par.name == "Image") {
-            // show file name
-            var pths = getEntityHeadingAttribute(entity, "path")
-                .split("/");
-            ret["text"] = pths[pths.length - 1];
-        } else 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 {
-            var name = getEntityName(entity);
-            if (typeof name !== "undefined" && name.length > 0) {
-                ret["text"] = name;
-            }
-        }
-
-        return ret;
+	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] || {};
+
+	var ret = {
+	    "text": id
+	};
+	if (getEntityHeadingAttribute(entity, "path") !==
+	    undefined || par.name == "Image") {
+	    // show file name
+	    var pths = getEntityHeadingAttribute(entity, "path")
+		.split("/");
+	    ret["text"] = pths[pths.length - 1];
+	} else 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 {
+	    var name = getEntityName(entity);
+	    if (typeof name !== "undefined" && name.length > 0) {
+		ret["text"] = name;
+	    }
+	}
+
+	return ret;
     }
 
 
@@ -366,12 +366,12 @@ var resolve_references = new function () {
      * @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];
-        }
+	if(element.getElementsByClassName(this._target_class).length > 0){
+	    return element.getElementsByClassName(this._target_class);
+	} else {
+	    return $(`<span class="${this._target_class}"/>`)
+		.appendTo(element)[0];
+	}
     }
 
     /**
@@ -382,14 +382,14 @@ var resolve_references = new function () {
      * @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;
+	$(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;
     }
 
 
@@ -405,10 +405,10 @@ var resolve_references = new function () {
      * @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];
+	const summary = $(
+	    `<div class="${resolve_references._summary_class}"/>`);
+	$(list_values).prepend(summary);
+	return summary[0];
     }
 
     this._summary_class = "caosdb-resolve-reference-summary";
@@ -420,9 +420,9 @@ var resolve_references = new function () {
     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();
+	const _unresolved_class_name = this._unresolved_class_name;
+	return $(container).find(".caosdb-f-property-value").has(
+	    `.${_unresolved_class_name}`).toArray();
     }
 
 
@@ -436,115 +436,115 @@ var resolve_references = new function () {
      * @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]);
-                }
-            }
-        }
+	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]);
+		}
+	    }
+	}
     }
 }
 
 
 $(document).ready(function () {
     if ("${BUILD_MODULE_EXT_RESOLVE_REFERENCES}" === "ENABLED") {
-        caosdb_modules.register(resolve_references);
+	caosdb_modules.register(resolve_references);
     }
 });
diff --git a/src/ext/js/person_reference_resover.js b/src/ext/js/person_reference_resover.js
index 0af54106..a3b25f7f 100644
--- a/src/ext/js/person_reference_resover.js
+++ b/src/ext/js/person_reference_resover.js
@@ -42,23 +42,23 @@ var person_reference = new function () {
      * Return the name of a person as firstname + lastname
      */
     this.get_person_str = function (el) {
-        var valpr = getProperties(el);
-        if (valpr == undefined) {
-            return;
-        }
-        return valpr.filter(valprel =>
-                valprel.name.toLowerCase() == "firstname")[0].value +
-            " " +
-            valpr.filter(valprel => valprel.name.toLowerCase() ==
-                "lastname")[0].value;
+	var valpr = getProperties(el);
+	if (valpr == undefined) {
+	    return;
+	}
+	return valpr.filter(valprel =>
+	    valprel.name.toLowerCase() ==
+		firstname_prop_name.toLowerCase())[0].value +
+	    " " +
+	    valpr.filter(valprel => valprel.name.toLowerCase() ==
+			 lastname_prop_name.toLowerCase())[0].value;
     }
 
     this.resolve = async function (id) {
-	const entity = (await resolve_references.retrieve(id))[0];
 
-	const par = getParents(entity)[0] || {};
+	const entity = (await resolve_references.retrieve(id))[0];
 
-	if (par.name.toLowerCase() == person_rt_name.toLowerCase()) {
+	if (resolve_references.is_child(entity, person_rt_name)) {
 	    return {"text": person_reference.get_person_str(entity)};
 	}
     }
-- 
GitLab