Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
C
caosdb-webui
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
caosdb
Software
caosdb-webui
Commits
d05c7394
Commit
d05c7394
authored
8 months ago
by
Henrik tom Wörden
Browse files
Options
Downloads
Patches
Plain Diff
STY: styling only
parent
bbae3ca3
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/core/js/ext_references.js
+498
-482
498 additions, 482 deletions
src/core/js/ext_references.js
with
498 additions
and
482 deletions
src/core/js/ext_references.js
+
498
−
482
View file @
d05c7394
...
...
@@ -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
);
}
});
});
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment