Source: jscore/MediaWatcher.js

(function() {
    /**
     * @namespace MediaWatcher
     * @memberof Help4.jscore
     */

    Help4.jscore.MediaWatcher = {
        /**
         * @param {HTMLElement} elem the to be observed element
         * @returns {Promise<Array<HTMLImageElement|HTMLVideoElement>>} indicates whether media is loaded
         */
        observe: async function(elem) {
            const nn = elem.nodeName;
            if (nn === 'IMG') {
                // single mode: IMG
                return [await _observeImage(elem)];
            } else if (nn === 'VIDEO') {
                // single mode: VIDEO
                return [await _observeVideo(elem)];
            }

            // multi mode

            const promises = /** @type {Array<Promise<HTMLImageElement|HTMLVideoElement>>} */ [];
            const images = elem.getElementsByTagName('img');
            for (const image of images) {
                promises.push(_observeImage(image));
            }
            const videos = elem.getElementsByTagName('video');
            for (const video of videos) {
                promises.push(_observeVideo(video))
            }
            return Help4.Promise.all(promises);
        }
    };

    /**
     * observes an image
     * @memberof Help4.jscore.MediaWatcher
     * @private
     * @param {HTMLImageElement} imgElem
     * @returns {Promise<HTMLImageElement>}
     */
    function _observeImage(imgElem) {
        return new Help4.Promise(resolve => {
            if (imgElem.complete) return resolve(imgElem);

            const done = () => {
                imgElem.removeEventListener('load', done);
                imgElem.removeEventListener('error', done);
                resolve(imgElem);
            }

            // media not yet loaded, observe it
            imgElem.addEventListener('load', done);
            imgElem.addEventListener('error', done);
        });
    }

    /**
     * observes a video
     * @memberof Help4.jscore.MediaWatcher
     * @private
     * @param {HTMLVideoElement} videoElem
     * @returns {Promise<HTMLVideoElement>}
     */
    function _observeVideo(videoElem) {
        return new Help4.Promise(resolve => {
            // data for the current playback position is available
            if (videoElem.readyState >= 2) return resolve(videoElem);

            const onLoadedData = () => {
                if (videoElem.readyState >= 2) {
                    videoElem.removeEventListener('loadeddata', onLoadedData);
                    resolve(videoElem);
                }
            }

            // wait for data load
            videoElem.addEventListener('loadeddata', onLoadedData);
        });
    }
})();