Implement loading indicator

This commit is contained in:
Kajetan Dvoracek 2022-04-21 17:13:45 +02:00
parent 1e45e0c10e
commit d4e03ef980
8 changed files with 124 additions and 5 deletions

View File

@ -170,6 +170,7 @@ class PageViewController extends AbstractController
tx_dlf_viewer = new dlfViewer({
controls: ["' . implode('", "', $this->controls) . '"],
div: "' . $this->settings['elementId'] . '",
progressElementId: "' . $this->settings['progressElementId'] . '",
images: ' . json_encode($this->images) . ',
fulltexts: ' . json_encode($this->fulltexts) . ',
annotationContainers: ' . json_encode($this->annotationContainers) . ',

View File

@ -65,6 +65,17 @@
</config>
</TCEforms>
</settings.elementId>
<settings.elementId>
<TCEforms>
<exclude>1</exclude>
<label>LLL:EXT:dlf/Resources/Private/Language/locallang_be.xlf:plugins.pageview.flexform.progressElementId</label>
<config>
<type>input</type>
<eval>required,alphanum_x,nospace</eval>
<default>tx-dlf-page-progress</default>
</config>
</TCEforms>
</settings.elementId>
<settings.useInternalProxy>
<TCEforms>
<exclude>1</exclude>

View File

@ -93,6 +93,40 @@ Additional OpenLayers controls may be configured in TypoScript:
These are created in ``dlfViewer::createControls_()``.
Loading Indicator
-----------------
A progress element may be configured to be used as loading indicator for static images.
This requires CORS and possibly a non-mixed context, and the server must send a ``Content-Length`` header.
In TypoScript, set ``progressElementId`` to the ID of the ``<progress>`` element:
.. code-block:: typoscript
plugin.tx_dlf_pageview {
settings {
progressElementId = tx-dlf-page-progress
}
}
The element may be placed anywhere on the page.
.. code-block:: html
<progress id="tx-dlf-page-progress"></progress>
For styling, the CSS class ``loading`` is added whenever the loading indicator is in use:
.. code-block:: css
#tx-dlf-page-progress {
visibility: hidden;
}
#tx-dlf-page-progress.loading {
visibility: visible;
}
Tools for Basket Plugin
-----------------------

View File

@ -631,6 +631,13 @@ Page View
:Default:
tx-dlf-map
- :Property:
progressElementId
:Data Type:
:ref:`t3tsref:data-type-string`
:Default:
tx-dlf-page-progress
- :Property:
crop
:Data Type:

View File

@ -373,6 +373,10 @@
<source><![CDATA[Activate magnifier]]></source>
<target><![CDATA[Lupenfunktion aktivieren]]></target>
</trans-unit>
<trans-unit id="plugins.pageview.flexform.progressElementId" approved="yes">
<source><![CDATA[@ID value of the <progress> element used for loading indicator]]></source>
<target><![CDATA[@ID-Wert des für den Ladebalken genutzen <progress>-Elements]]></target>
</trans-unit>
<trans-unit id="plugins.pageview.flexform.useInternalProxy" approved="yes">
<source><![CDATA[Use Internal Proxy]]></source>
<target><![CDATA[Internen Proxy verwenden]]></target>

View File

@ -233,6 +233,9 @@
<trans-unit id="plugins.pageview.flexform.magnifier">
<source><![CDATA[Activate magnifier]]></source>
</trans-unit>
<trans-unit id="plugins.pageview.flexform.progressElementId">
<source><![CDATA[@ID value of the <progress> element used for loading indicator]]></source>
</trans-unit>
<trans-unit id="plugins.pageview.flexform.useInternalProxy">
<source><![CDATA[Use Internal Proxy]]></source>
</trans-unit>

View File

@ -8,6 +8,13 @@
* LICENSE.txt file that was distributed with this source code.
*/
/**
* @typedef {object} LoadingIndicator
* @property {(value: number) => void} progress
* @property {() => void} indeterminate
* @property {() => void} done
*/
/**
* @typedef {{
* url: string;
@ -20,6 +27,7 @@
*
* @typedef {{
* div: string;
* progressElementId?: string;
* images?: ImageDesc[] | [];
* fulltexts?: FulltextDesc[] | [];
* controls?: ('OverviewMap' | 'ZoomPanel')[];
@ -61,6 +69,13 @@ var dlfViewer = function(settings){
*/
this.images = [];
/**
* The <progress> element for loading indicator.
* @type {LoadingIndicator}
* @private
*/
this.loadingIndicator = this.makeLoadingIndicator(settings.progressElementId);
/**
* Fulltext information (e.g. URL)
* @type {Array.<string|?>}
@ -170,6 +185,37 @@ var dlfViewer = function(settings){
this.init(dlfUtils.exists(settings.controls) ? settings.controls : []);
};
/**
*
* @param {string | undefined}
* @returns {LoadingIndicator}
*/
dlfViewer.prototype.makeLoadingIndicator = function (progressElementId) {
// Query progress element on demand because it may only become available at a later point (Zeitungsportal)
return {
indeterminate() {
var progressElement = document.getElementById(progressElementId);
if (progressElement instanceof HTMLProgressElement) {
progressElement.classList.add('loading');
progressElement.removeAttribute("value");
}
},
progress(value) {
var progressElement = document.getElementById(progressElementId);
if (progressElement instanceof HTMLProgressElement) {
progressElement.classList.add('loading');
progressElement.value = value * progressElement.max;
}
},
done() {
var progressElement = document.getElementById(progressElementId);
if (progressElement instanceof HTMLProgressElement) {
progressElement.classList.remove('loading');
}
},
};
};
/**
* Get number of shown pages. Typically 1 (single page) or 2 (double page mode).
*
@ -528,7 +574,7 @@ dlfViewer.prototype.initLayer = function(imageSourceObjs) {
deferredResponse.resolve(layers);
}, this);
dlfUtils.fetchImageData(imageSourceObjs)
dlfUtils.fetchImageData(imageSourceObjs, this.loadingIndicator)
.done(function(imageSourceData) {
resolveCallback(imageSourceData, dlfUtils.createOlLayers(imageSourceData));
});

View File

@ -222,9 +222,10 @@ dlfUtils.exists = function (val) {
* Fetch image data for given image sources.
*
* @param {ImageDesc[]} imageSourceObjs
* @param {LoadingIndicator} loadingIndicator
* @return {JQueryStatic.Deferred}
*/
dlfUtils.fetchImageData = function (imageSourceObjs) {
dlfUtils.fetchImageData = function (imageSourceObjs, loadingIndicator) {
// use deferred for async behavior
var deferredResponse = new $.Deferred();
@ -264,7 +265,7 @@ dlfUtils.fetchImageData = function (imageSourceObjs) {
});
} else {
// In the worse case expect static image file
dlfUtils.fetchStaticImageData(imageSourceObj)
dlfUtils.fetchStaticImageData(imageSourceObj, loadingIndicator)
.done(function (imageSourceDataObj) {
imageSourceData[index] = imageSourceDataObj;
finishLoading();
@ -280,9 +281,10 @@ dlfUtils.fetchImageData = function (imageSourceObjs) {
* Fetches the image data for static images source.
*
* @param {ImageDesc} imageSourceObj
* @param {LoadingIndicator} loadingIndicator
* @return {JQueryStatic.Deferred}
*/
dlfUtils.fetchStaticImageData = function (imageSourceObj) {
dlfUtils.fetchStaticImageData = function (imageSourceObj, loadingIndicator) {
// Load the image while trying to reconcile the following constraints:
//
// - Determine width/height of the image before passing it to OpenLayers.
@ -305,12 +307,13 @@ dlfUtils.fetchStaticImageData = function (imageSourceObj) {
// -> Fall back to a "data:" URL.
//
// TODO: Revisit this. Perhaps we find a way to pass the Image directly to OpenLayers.
// Even so, loading via XHR is beneficial in that it will allow implementing a loading indicator.
// Even so, loading via XHR is beneficial in that it allows implementing a loading indicator.
// use deferred for async behavior
var deferredResponse = new $.Deferred();
var loadFailed = function () {
loadingIndicator.done();
deferredResponse.reject();
};
@ -322,6 +325,8 @@ dlfUtils.fetchStaticImageData = function (imageSourceObj) {
var makeImage = function (src, mimetype) {
var image = new Image();
image.onload = function () {
loadingIndicator.done();
var imageDataObj = {
src: this.src,
mimetype,
@ -357,6 +362,13 @@ dlfUtils.fetchStaticImageData = function (imageSourceObj) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onprogress = function (e) {
if (e.lengthComputable) {
loadingIndicator.progress(e.loaded / e.total);
} else {
loadingIndicator.indeterminate();
}
};
xhr.onload = function () {
if (200 <= xhr.status && xhr.status < 300) {
var blob = xhr.response;
@ -367,6 +379,7 @@ dlfUtils.fetchStaticImageData = function (imageSourceObj) {
};
xhr.onerror = function () {
// Mixed content or bad CORS headers? Try again using passive content.
loadingIndicator.indeterminate();
makeImage(imageSourceObj.url, imageSourceObj.mimetype);
};
xhr.open('GET', imageSourceObj.url);