diff --git a/iOS/Resources/main_ios.js b/iOS/Resources/main_ios.js index 7a87e51c1..ea2697da2 100644 --- a/iOS/Resources/main_ios.js +++ b/iOS/Resources/main_ios.js @@ -1,109 +1,136 @@ -var controller = new AbortController() +var activeImageViewer = null; -// Cancel any pending image loads (there might be none) and reset the controller -function cancelImageLoad() { - controller.abort(); - controller = new AbortController(); -} - -// Used to pop a resizable image view -async function imageWasClicked(img) { - cancelImageLoad(); - showNetworkLoading(img); - - try { - - const signal = controller.signal; - const response = await fetch(img.src, { signal: signal }); - if (!response.ok) { - throw new Error('Network response was not ok.'); - } - - const imgBlob = await response.blob(); - if (signal.aborted) { - throw new Error('Network response was aborted.'); - } - - hideNetworkLoading(img); - - var reader = new FileReader(); - reader.readAsDataURL(imgBlob); - reader.onloadend = function() { - - img.classList.add("nnwClicked"); - - const rect = img.getBoundingClientRect(); - var message = { - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height - }; - message.imageURL = reader.result; - - var jsonMessage = JSON.stringify(message); - window.webkit.messageHandlers.imageWasClicked.postMessage(jsonMessage); - - } - - } catch (error) { - hideNetworkLoading(img); - console.log('There has been a problem with your fetch operation: ', error.message); +class ImageViewer { + constructor(img) { + this.img = img; + this.loadingInterval = null; + this.activityIndicator = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSI2NHB4IiBoZWlnaHQ9IjY0cHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMwMDAwMDAiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNjY2NjY2MiIHRyYW5zZm9ybT0icm90YXRlKDMwIDY0IDY0KSIvPjxwYXRoIGQ9Ik01OS42IDBoOHY0MGgtOFYweiIgZmlsbD0iI2NjY2NjYyIgdHJhbnNmb3JtPSJyb3RhdGUoNjAgNjQgNjQpIi8+PHBhdGggZD0iTTU5LjYgMGg4djQwaC04VjB6IiBmaWxsPSIjY2NjY2NjIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNjY2NjY2MiIHRyYW5zZm9ybT0icm90YXRlKDEyMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNiMmIyYjIiIHRyYW5zZm9ybT0icm90YXRlKDE1MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM5OTk5OTkiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM3ZjdmN2YiIHRyYW5zZm9ybT0icm90YXRlKDIxMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM2NjY2NjYiIHRyYW5zZm9ybT0icm90YXRlKDI0MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM0YzRjNGMiIHRyYW5zZm9ybT0icm90YXRlKDI3MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMzMzMzMzMiIHRyYW5zZm9ybT0icm90YXRlKDMwMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMxOTE5MTkiIHRyYW5zZm9ybT0icm90YXRlKDMzMCA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzMwIDY0IDY0OzYwIDY0IDY0OzkwIDY0IDY0OzEyMCA2NCA2NDsxNTAgNjQgNjQ7MTgwIDY0IDY0OzIxMCA2NCA2NDsyNDAgNjQgNjQ7MjcwIDY0IDY0OzMwMCA2NCA2NDszMzAgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMDgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg=="; } - -} -function showNetworkLoading(img) { + isLoaded() { + return this.img.classList.contains("nnwLoaded"); + } - var wrapper = document.createElement("div"); - wrapper.classList.add("activityIndicatorWrap"); - img.parentNode.insertBefore(wrapper, img); - wrapper.appendChild(img); + clicked() { + this.showLoadingIndicator(); + if (this.isLoaded()) { + this.showViewer(); + } else { + var callback = () => { + if (this.isLoaded()) { + clearInterval(this.loadingInterval); + this.showViewer(); + } + } + this.loadingInterval = setInterval(callback, 100); + } + } + + cancel() { + clearInterval(this.loadingInterval); + this.hideLoadingIndicator(); + } + + showViewer() { + this.hideLoadingIndicator(); + + var canvas = document.createElement("canvas"); + canvas.width = this.img.naturalWidth; + canvas.height = this.img.naturalHeight; + canvas.getContext("2d").drawImage(this.img, 0, 0); + + const rect = this.img.getBoundingClientRect(); + const message = { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + imageURL: canvas.toDataURL(), + }; + + var jsonMessage = JSON.stringify(message); + window.webkit.messageHandlers.imageWasClicked.postMessage(jsonMessage); + } + + hideImage() { + this.img.style.opacity = 0; + } + + showImage() { + this.img.style.opacity = 1 + window.webkit.messageHandlers.imageWasShown.postMessage(""); + } + + showLoadingIndicator() { + var wrapper = document.createElement("div"); + wrapper.classList.add("activityIndicatorWrap"); + this.img.parentNode.insertBefore(wrapper, this.img); + wrapper.appendChild(this.img); + + var activityIndicatorImg = document.createElement("img"); + activityIndicatorImg.classList.add("activityIndicator"); + activityIndicatorImg.style.opacity = 0; + activityIndicatorImg.src = this.activityIndicator; + wrapper.appendChild(activityIndicatorImg); - var activityIndicatorImg = document.createElement("img"); - activityIndicatorImg.classList.add("activityIndicator"); - activityIndicatorImg.style.opacity = 0; - activityIndicatorImg.src = activityIndicator; - wrapper.appendChild(activityIndicatorImg); - - // Wait a bit before showing the indicator - function showActivityIndicator() { activityIndicatorImg.style.opacity = 1; } - setTimeout(showActivityIndicator, 300); + + hideLoadingIndicator() { + var wrapper = this.img.parentNode; + if (wrapper.classList.contains("activityIndicatorWrap")) { + var wrapperParent = wrapper.parentNode; + wrapperParent.insertBefore(this.img, wrapper); + wrapperParent.removeChild(wrapper); + } + } + + static init() { + cancelImageLoad(); + + // keep track of when an image has finished downloading for ImageViewer + document.querySelectorAll("img").forEach(element => { + element.onload = function() { + this.classList.add("nnwLoaded"); + } + }); + + // Add the click listener for images + window.onclick = function(event) { + if (event.target.matches("img")) { + if (activeImageViewer && activeImageViewer.img === event.target) { + cancelImageLoad(); + } else { + cancelImageLoad(); + activeImageViewer = new ImageViewer(event.target); + activeImageViewer.clicked(); + } + } + } + } } -function hideNetworkLoading(img) { - var wrapper = img.parentNode; - var wrapperParent = wrapper.parentNode; - wrapperParent.insertBefore(img, wrapper); - wrapperParent.removeChild(wrapper); +function cancelImageLoad() { + if (activeImageViewer) { + activeImageViewer.cancel(); + activeImageViewer = null; + } } -// Used to animate the transition to a fullscreen image function hideClickedImage() { - var img = document.querySelector('.nnwClicked') - img.style.opacity = 0 + if (activeImageViewer) { + activeImageViewer.hideImage(); + } } // Used to animate the transition from a fullscreen image function showClickedImage() { - var img = document.querySelector('.nnwClicked') - img.classList.remove("nnwClicked"); - img.style.opacity = 1 - window.webkit.messageHandlers.imageWasShown.postMessage(""); -} - -// Add the click listener for images -function imageClicks() { - window.onclick = function(event) { - if (event.target.matches('img')) { - imageWasClicked(event.target); - } + if (activeImageViewer) { + activeImageViewer.showImage(); } } -// Add the playsinline attribute to any HTML5 videos that don't have it. +// Add the playsinline attribute to any HTML5 videos that don"t have it. // Without this attribute videos may autoplay and take over the whole screen // on an iphone when viewing an article. function inlineVideos() { @@ -113,9 +140,6 @@ function inlineVideos() { } function postRenderProcessing() { - cancelImageLoad(); - imageClicks(); + ImageViewer.init(); inlineVideos(); } - -const activityIndicator = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjAiIHdpZHRoPSI2NHB4IiBoZWlnaHQ9IjY0cHgiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48Zz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMwMDAwMDAiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNjY2NjY2MiIHRyYW5zZm9ybT0icm90YXRlKDMwIDY0IDY0KSIvPjxwYXRoIGQ9Ik01OS42IDBoOHY0MGgtOFYweiIgZmlsbD0iI2NjY2NjYyIgdHJhbnNmb3JtPSJyb3RhdGUoNjAgNjQgNjQpIi8+PHBhdGggZD0iTTU5LjYgMGg4djQwaC04VjB6IiBmaWxsPSIjY2NjY2NjIiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNjY2NjY2MiIHRyYW5zZm9ybT0icm90YXRlKDEyMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiNiMmIyYjIiIHRyYW5zZm9ybT0icm90YXRlKDE1MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM5OTk5OTkiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM3ZjdmN2YiIHRyYW5zZm9ybT0icm90YXRlKDIxMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM2NjY2NjYiIHRyYW5zZm9ybT0icm90YXRlKDI0MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiM0YzRjNGMiIHRyYW5zZm9ybT0icm90YXRlKDI3MCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMzMzMzMzMiIHRyYW5zZm9ybT0icm90YXRlKDMwMCA2NCA2NCkiLz48cGF0aCBkPSJNNTkuNiAwaDh2NDBoLThWMHoiIGZpbGw9IiMxOTE5MTkiIHRyYW5zZm9ybT0icm90YXRlKDMzMCA2NCA2NCkiLz48YW5pbWF0ZVRyYW5zZm9ybSBhdHRyaWJ1dGVOYW1lPSJ0cmFuc2Zvcm0iIHR5cGU9InJvdGF0ZSIgdmFsdWVzPSIwIDY0IDY0OzMwIDY0IDY0OzYwIDY0IDY0OzkwIDY0IDY0OzEyMCA2NCA2NDsxNTAgNjQgNjQ7MTgwIDY0IDY0OzIxMCA2NCA2NDsyNDAgNjQgNjQ7MjcwIDY0IDY0OzMwMCA2NCA2NDszMzAgNjQgNjQiIGNhbGNNb2RlPSJkaXNjcmV0ZSIgZHVyPSIxMDgwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIj48L2FuaW1hdGVUcmFuc2Zvcm0+PC9nPjwvc3ZnPg==";