From 4239f77dcb0006a1bca78658b5cc373ef42cb23b Mon Sep 17 00:00:00 2001 From: nymver <146001324+nymver@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:52:34 +0300 Subject: [PATCH 01/12] Update options.js Adding Alt key --- js/options.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/options.js b/js/options.js index 00e6e0d02..75c856ad1 100644 --- a/js/options.js +++ b/js/options.js @@ -52,6 +52,7 @@ function loadKeys(sel) { if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); + $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } From aa811600b24b5bab0cca15d79e449dd413937ec4 Mon Sep 17 00:00:00 2001 From: nymver <146001324+nymver@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:53:09 +0300 Subject: [PATCH 02/12] Update popup.js --- js/popup.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/popup.js b/js/popup.js index 8fea15928..782d57d49 100644 --- a/js/popup.js +++ b/js/popup.js @@ -45,6 +45,7 @@ function loadKeys(sel) { if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); + $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } From 6580f2429f9f37213ec7a7ec3ffc73190b554c59 Mon Sep 17 00:00:00 2001 From: nymver <146001324+nymver@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:20:01 +0300 Subject: [PATCH 03/12] Update options.html --- html/options.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/html/options.html b/html/options.html index 0a2957df7..d25d1998f 100644 --- a/html/options.html +++ b/html/options.html @@ -39,6 +39,14 @@

@@ -548,6 +666,19 @@

+
+ + +
diff --git a/js/background.js b/js/background.js index 487583e51..6b213749e 100644 --- a/js/background.js +++ b/js/background.js @@ -8,12 +8,12 @@ function cLog(msg) { // Performs an ajax request function ajaxRequest(request, callback) { - var xhr = new XMLHttpRequest(); - var response = request.response; - var method = request.method; - var url = request.url; - var filename = request.filename; - var conflictAction = request.conflictAction; + const response = request.response; + const method = request.method; + const filename = request.filename; + const conflictAction = request.conflictAction; + let xhr = new XMLHttpRequest(); + let url = request.url; if (response === 'DOWNLOAD') xhr.responseType = 'arraybuffer'; @@ -43,7 +43,7 @@ function ajaxRequest(request, callback) { } xhr.open(request.method, request.url, true); - for (var i in request.headers) { + for (let i in request.headers) { xhr.setRequestHeader(request.headers[i].header, request.headers[i].value); } xhr.send(request.data); @@ -80,25 +80,52 @@ function downloadFile(url, filename, conflictAction, callback) { function onMessage(message, sender, callback) { switch (message.action) { case 'downloadFileBlob': - // direct URL download through Chrome API might be prohibited (e.g: Pixiv) - // workaround: - // 1. obtain ArrayBuffer from XHR request (GET URL) - // 2. create Blob from ArrayBuffer - // 3. download Blob URL through Chrome API + /** + * direct URL download through Chrome API might be prohibited (e.g: Pixiv) + * workaround: + * 1. obtain ArrayBuffer from XHR request (GET URL) + * 2. create Blob from ArrayBuffer + * 3. download Blob URL through Chrome API + */ + + /* + * Workaround for permissions.request not returning a promise in Firefox + * First checks if permissions are availble. If true, downloads file. If not, requests them. + * Not as clean or effecient as just using 'permissions.request'. + */ cLog('downloadFileBlob: ' + message); - chrome.permissions.request({permissions: ['downloads']}, function (granted) { - cLog('downloadFile granted: ' + granted); - if (granted) { + chrome.permissions.contains({permissions: ['downloads']}, (contained) => { + cLog('downloadFile contains: ' + contained); + if (contained) { ajaxRequest({method:'GET', response:'DOWNLOAD', url:message.url, filename:message.filename, conflictAction:message.conflictAction, headers:message.headers}, callback); + } else { + chrome.permissions.request({permissions: ['downloads']}, (granted) => { + cLog('downloadFile granted: ' + granted); + if (granted) { + ajaxRequest({method:'GET', response:'DOWNLOAD', url:message.url, filename:message.filename, conflictAction:message.conflictAction, headers:message.headers}, callback); + } + }) } }); return true; case 'downloadFile': cLog('downloadFile: ' + message); - chrome.permissions.request({permissions: ['downloads']}, function (granted) { - cLog('downloadFile granted: ' + granted); - if (granted) { + /* + * Workaround for permissions.request not returning a promise in Firefox + * First checks if permissions are availble. If true, downloads file. If not, requests them. + * Not as clean or effecient as just using 'permissions.request'. + */ + chrome.permissions.contains({permissions: ['downloads']}, (contained) => { + cLog('downloadFile contains: ' + contained); + if (contained) { downloadFile(message.url, message.filename, message.conflictAction, callback); + } else { + chrome.permissions.request({permissions: ['downloads']}, (granted) => { + cLog('downloadFile granted: ' + granted); + if (granted) { + downloadFile(message.url, message.filename, message.conflictAction, callback); + } + }) } }); return true; @@ -151,7 +178,7 @@ function onMessage(message, sender, callback) { localStorage.removeItem(message.id); break; case 'openViewWindow': - var url = message.createData.url; + let url = message.createData.url; if (url.indexOf('facebook.com/photo/download') !== -1) { message.createData.url = 'data:text/html,'; } @@ -164,7 +191,7 @@ function onMessage(message, sender, callback) { message.createData.index = tabs[0].index; if (!message.createData.active) message.createData.index++; - var url = message.createData.url; + let url = message.createData.url; if (url.indexOf('facebook.com/photo/download') !== -1) { message.createData.url = 'data:text/html,'; } @@ -247,15 +274,19 @@ chrome.runtime.onInstalled.addListener((details) => { } }) -// - store request's header(s) setting(s) = modification(s) to be applied to request's header(s) just before sending request to server -// e.g: add/modify "Origin" header to deal with CORS limitations -// - store response's header(s) setting(s) = modification(s) to be applied to response's header(s) just after receiving response from server -// e.g: add/modify "Access-Control-Allow-Origin" header so browser allows content display +/** +* - store request's header(s) setting(s) = modification(s) to be applied to request's header(s) just before sending request to server +* e.g: add/modify "Origin" header to deal with CORS limitations +* - store response's header(s) setting(s) = modification(s) to be applied to response's header(s) just after receiving response from server +* e.g: add/modify "Access-Control-Allow-Origin" header so browser allows content display +*/ function storeHeaderSettings(message) { - // check that: - // - header(s) rewrite is allowed - // and - // - permissions are granted + /** + * check that: + * - header(s) rewrite is allowed + * and + * - permissions are granted + */ if (!options.allowHeadersRewrite) { return; } @@ -311,10 +342,12 @@ function findHeaderSettings(url, requestOrResponse) { // update header(s) according to plug-in settings function updateHeaders(headers, settings) { settings.headers.forEach(h => { - // types of update: - // - 'remove': remove header - // - 'replace': replace header, if header does not exist then do nothing - // - 'add': add header, if header already exists then replace it + /** + * types of update: + * - 'remove': remove header + * - 'replace': replace header, if header does not exist then do nothing + * - 'add': add header, if header already exists then replace it + */ if (h.typeOfUpdate === 'remove') { let index = headers.findIndex(rh => rh.name.toLowerCase() === h.name.toLowerCase()); if (index !== -1) headers.splice(index, 1); @@ -332,26 +365,43 @@ function updateHeaders(headers, settings) { return headers; } -// add listeners for web requests: -// - onBeforeSendHeaders -// - onHeadersReceived -// so they can be edited on-the-fly to enable API calls from plug-ins -// https://developer.chrome.com/docs/extensions/reference/webRequest/ +/** +* add listeners for web requests: +* - onBeforeSendHeaders +* - onHeadersReceived +* so they can be edited on-the-fly to enable API calls from plug-ins +* https://developer.chrome.com/docs/extensions/reference/webRequest/ +* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest +*/ + function addWebRequestListeners() { - if (!chrome.webRequest.onBeforeSendHeaders.hasListeners()) - chrome.webRequest.onBeforeSendHeaders.addListener(updateRequestHeaders, { urls : [""] }, ["blocking", "requestHeaders", "extraHeaders"]); - if (!chrome.webRequest.onHeadersReceived.hasListeners()) - chrome.webRequest.onHeadersReceived.addListener(updateResponseHeaders, { urls : [""] }, ["blocking", "responseHeaders", "extraHeaders"]); + if (!chrome.webRequest.onBeforeSendHeaders.hasListener(updateRequestHeaders)) { + chrome.webRequest.onBeforeSendHeaders.addListener(updateRequestHeaders, { urls : [""] }, [ + 'blocking', + 'requestHeaders', + chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS, + ].filter(Boolean)); + } + if (!chrome.webRequest.onHeadersReceived.hasListener(updateResponseHeaders)){ + chrome.webRequest.onHeadersReceived.addListener(updateResponseHeaders, { urls : [""] }, [ + 'blocking', + 'responseHeaders', + chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS, + ].filter(Boolean)); + } + } -// remove listeners for web requests: -// - onBeforeSendHeaders -// - onHeadersReceived -// also remove headers settings since they are not used anymore +/** +* remove listeners for web requests: +* - onBeforeSendHeaders +* - onHeadersReceived +* also remove headers settings since they are not used anymore +*/ function removeWebRequestListeners() { - if (chrome.webRequest.onBeforeSendHeaders.hasListeners()) + if (chrome.webRequest.onBeforeSendHeaders.hasListener(updateRequestHeaders)) chrome.webRequest.onBeforeSendHeaders.removeListener(updateRequestHeaders); - if (chrome.webRequest.onHeadersReceived.hasListeners()) + if (chrome.webRequest.onHeadersReceived.hasListener(updateResponseHeaders)) chrome.webRequest.onHeadersReceived.removeListener(updateResponseHeaders); sessionStorage.removeItem('HoverZoomHeaderSettings'); diff --git a/js/common.js b/js/common.js index ec4ea2b8b..67f4cae74 100644 --- a/js/common.js +++ b/js/common.js @@ -36,8 +36,13 @@ var factorySettings = { ambilightBackgroundOpacity : 0.9, disabledPlugins : [], centerImages : false, + autoLockImages : false, frameBackgroundColor: "#ffffff", frameThickness: 4, + belowPositionOffset: 0, + abovePositionOffset: 0, + captionOpacity: 1, + detailsOpacity: 1, displayImageLoader: false, enlargementThresholdEnabled : true, enlargementThreshold : 2, @@ -49,10 +54,23 @@ var factorySettings = { addDownloadOrigin : false, addDownloadSize : false, addDownloadDuration : false, + addDownloadIndex : false, + addDownloadCaption : false, + replaceOriginalFilename : false, + downloadFilename : '', useSeparateTabOrWindowForUnloadableUrlsEnabled: false, useSeparateTabOrWindowForUnloadableUrls: 'window', captionLocation : 'below', detailsLocation : 'none', + showDetailFilename : true, + showDetailHost : true, + showDetailLastModified : true, + showDetailExtension : true, + showDetailContentLength : true, + showDetailDuration : true, + showDetailScale : true, + showDetailRatio : true, + showDetailDimensions : true, fontSize : 11, fontOutline : false, actionKey : 0, @@ -126,6 +144,7 @@ function loadOptions() { options.ambilightBackgroundOpacity = options.hasOwnProperty('ambilightBackgroundOpacity') ? options.ambilightBackgroundOpacity : factorySettings.ambilightBackgroundOpacity; options.disabledPlugins = options.hasOwnProperty('disabledPlugins') ? options.disabledPlugins : factorySettings.disabledPlugins; options.centerImages = options.hasOwnProperty('centerImages') ? options.centerImages : factorySettings.centerImages; + options.autoLockImages = options.hasOwnProperty('autoLockImages') ? options.autoLockImages : factorySettings.autoLockImages; options.frameBackgroundColor = options.hasOwnProperty('frameBackgroundColor') ? options.frameBackgroundColor : factorySettings.frameBackgroundColor; options.frameThickness = options.hasOwnProperty('frameThickness') ? options.frameThickness : factorySettings.frameThickness; options.displayImageLoader = options.hasOwnProperty('displayImageLoader') ? options.displayImageLoader : factorySettings.displayImageLoader; @@ -139,9 +158,24 @@ function loadOptions() { options.addDownloadOrigin = options.hasOwnProperty('addDownloadOrigin') ? options.addDownloadOrigin : factorySettings.addDownloadOrigin; options.addDownloadSize = options.hasOwnProperty('addDownloadSize') ? options.addDownloadSize : factorySettings.addDownloadSize; options.addDownloadDuration = options.hasOwnProperty('addDownloadDuration') ? options.addDownloadDuration : factorySettings.addDownloadDuration; + options.addDownloadIndex = options.hasOwnProperty('addDownloadIndex') ? options.addDownloadIndex : factorySettings.addDownloadIndex; + options.addDownloadCaption = options.hasOwnProperty('addDownloadCaption') ? options.addDownloadCaption : factorySettings.addDownloadCaption; + options.replaceOriginalFilename = options.hasOwnProperty('replaceOriginalFilename') ? options.replaceOriginalFilename : factorySettings.replaceOriginalFilename; + options.downloadFilename = options.hasOwnProperty('downloadFilename') ? options.downloadFilename : factorySettings.downloadFilename; options.useSeparateTabOrWindowForUnloadableUrlsEnabled = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrlsEnabled') ? options.useSeparateTabOrWindowForUnloadableUrlsEnabled : factorySettings.useSeparateTabOrWindowForUnloadableUrlsEnabled; options.useSeparateTabOrWindowForUnloadableUrls = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrls') ? options.useSeparateTabOrWindowForUnloadableUrls : factorySettings.useSeparateTabOrWindowForUnloadableUrls; + // Show details options + options.showDetailFilename = options.hasOwnProperty('showDetailFilename') ? options.showDetailFilename : factorySettings.showDetailFilename; + options.showDetailHost = options.hasOwnProperty('showDetailHost') ? options.showDetailHost : factorySettings.showDetailHost; + options.showDetailLastModified = options.hasOwnProperty('showDetailLastModified') ? options.showDetailLastModified : factorySettings.showDetailLastModified; + options.showDetailExtension = options.hasOwnProperty('showDetailExtension') ? options.showDetailExtension : factorySettings.showDetailExtension; + options.showDetailContentLength = options.hasOwnProperty('showDetailContentLength') ? options.showDetailContentLength : factorySettings.showDetailContentLength; + options.showDetailDuration = options.hasOwnProperty('showDetailDuration') ? options.showDetailDuration : factorySettings.showDetailDuration; + options.showDetailScale = options.hasOwnProperty('showDetailScale') ? options.showDetailScale : factorySettings.showDetailScale; + options.showDetailRatio = options.hasOwnProperty('showDetailRatio') ? options.showDetailRatio : factorySettings.showDetailRatio; + options.showDetailDimensions = options.hasOwnProperty('showDetailDimensions') ? options.showDetailDimensions : factorySettings.showDetailDimensions; + // Used old showCaptions option for backwards compatibility var showCaptions = options.hasOwnProperty('showCaptions') ? options.showCaptions : true; options.captionLocation = options.hasOwnProperty('captionLocation') ? options.captionLocation : (showCaptions ? factorySettings.captionLocation : 'none'); @@ -149,6 +183,10 @@ function loadOptions() { options.detailsLocation = options.hasOwnProperty('detailsLocation') ? options.detailsLocation : factorySettings.detailsLocation; options.fontSize = options.hasOwnProperty('fontSize') ? options.fontSize : factorySettings.fontSize; options.fontOutline = options.hasOwnProperty('fontOutline') ? options.fontOutline : factorySettings.fontOutline; + options.belowPositionOffset = options.hasOwnProperty('belowPositionOffset') ? options.belowPositionOffset : factorySettings.belowPositionOffset; + options.abovePositionOffset = options.hasOwnProperty('abovePositionOffset') ? options.abovePositionOffset : factorySettings.abovePositionOffset; + options.captionOpacity = options.hasOwnProperty('captionOpacity') ? options.captionOpacity : factorySettings.captionOpacity; + options.detailsOpacity = options.hasOwnProperty('detailsOpacity') ? options.detailsOpacity : factorySettings.detailsOpacity; // Action keys options.actionKey = options.hasOwnProperty('actionKey') ? options.actionKey : factorySettings.actionKey; diff --git a/js/hoverzoom.js b/js/hoverzoom.js index 48f5a6441..09f086e27 100644 --- a/js/hoverzoom.js +++ b/js/hoverzoom.js @@ -125,7 +125,6 @@ var hoverZoom = { arrowUpKeyDown = false, arrowDownKeyDown = false, viewerLocked = false, - lockViewerClickTime = 0, zoomFactor = 1, zoomSpeedFactor = 1, pageActionShown = false, @@ -285,14 +284,14 @@ var hoverZoom = { 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'align-items':'flex-end' + 'align-items':'flex-end', }, hzBelowCss = { 'background':'none', 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'align-items':'flex-start' + 'align-items':'flex-start', }, hzCaptionMiscellaneousCss = { 'background':'none', @@ -306,7 +305,7 @@ var hoverZoom = { 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'min-width':'25%' + 'min-width':'25%', }, hzGalleryInfoCss = { 'position':'absolute', @@ -412,14 +411,26 @@ var hoverZoom = { imgFullSize.height(wndHeight - padding - statusBarHeight - scrollBarHeight - (hzAbove ? hzAbove.height() : 0) - (hzBelow ? hzBelow.height() : 0)).width('auto'); } - if (hzCaptionMiscellaneous) + if (hzCaptionMiscellaneous) { hzCaptionMiscellaneous.css('max-width', imgFullSize[0].clientWidth); - if (hzDetails) + hzCaptionMiscellaneous.css('opacity', options.captionOpacity); + } + if (hzDetails) { hzDetails.css('max-width', imgFullSize[0].clientWidth); - if (hzAbove) + hzDetails.css('opacity', options.detailsOpacity); + } + if (hzAbove) { hzAbove.css('max-width', imgFullSize[0].clientWidth); - if (hzBelow) + hzAbove.css('top', options.abovePositionOffset + '%'); + if (options.abovePositionOffset != 0) + hzAbove.css('position', 'absolute'); + } + if (hzBelow) { hzBelow.css('max-width', imgFullSize[0].clientWidth); + hzBelow.css('bottom', options.belowPositionOffset + '%'); + if (options.belowPositionOffset != 0) + hzBelow.css('position', 'absolute'); + } // do not display caption nor details if img is too small if (imgFullSize[0].clientWidth < 50) { @@ -485,9 +496,13 @@ var hoverZoom = { } // width adjustment - var fullZoom = options.mouseUnderlap || fullZoomKeyDown || viewerLocked; + const fullZoom = options.mouseUnderlap || viewerLocked; + const fullZoomKey = fullZoomKeyDown; if (viewerLocked) { imgFullSize.width(srcDetails.naturalWidth * zoomFactor); + } else if (fullZoomKey) { + // naturalWidth replaced with wndWidth to make image fill window + imgFullSize.width(Math.min(wndWidth, wndWidth - padding - 2 * scrollBarWidth)); } else if (fullZoom) { imgFullSize.width(Math.min(srcDetails.naturalWidth * zoomFactor, wndWidth - padding - 2 * scrollBarWidth)); } else if (displayOnRight) { @@ -618,6 +633,10 @@ var hoverZoom = { } function isAudioLink(url) { + if (url.indexOf('.audio') !== -1) { + return true; + } + if (url.lastIndexOf('?') > 0) url = url.substring(0, url.lastIndexOf('?')); const ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(); @@ -1048,26 +1067,183 @@ var hoverZoom = { } } + let longRightPressTimer; // create timer + let longMiddlePressTimer; // creates separate timer so they don't interfere + let longRightPress = false; + + function mouseButtonKeyHandler(mouseButtonKey, img) { + const timerDelay = 150; + if (mouseButtonKey === -1) { + longRightPressTimer = setTimeout(longClick.bind(img), timerDelay, mouseButtonKey); + } else { + longMiddlePressTimer = setTimeout(longClick.bind(img), timerDelay, mouseButtonKey); + } + } + + function clearMouseButtonTimers(mouseButtonKey) { + if (mouseButtonKey === -1) { + clearTimeout(longRightPressTimer); + } else { + clearTimeout(longMiddlePressTimer); + } + } + + function longClick(mouseButtonKey) { + if (mouseButtonKey == -1) longRightPress = true; + switch (mouseButtonKey) { + case options.actionKey: + actionKeyDown = true; + $(this).mousemove(); + if (loading || imgFullSize) { + return false; + } + break; + case options.lockImageKey: + lockViewer(); + return; + case options.toggleKey: + toggleKey() + return; + case options.fullZoomKey: + if (!fullZoomKeyDown) { + fullZoomKeyDown = true; + posViewer(); + if (imgFullSize) { + return false; + } + } + return; + case options.closeKey: + closeKey() + return; + case options.hideKey: + if (!hideKeyDown) { + hideKey() + } + return; + case options.copyImageKey: + if (isChromiumBased) { + if (keyCode === options.copyImageKey) { + copyImage(); + return false; + } + } + return false; + case options.copyImageUrlKey: + copyLink(); + return false; + // "Previous image" key + case options.prevImgKey: + var linkData = hz.currentLink.data(); + if (linkData.hoverZoomGallerySrc && linkData.hoverZoomGallerySrc.length > 1) rotateGalleryImg(-1); + else changeVideoPosition(-parseInt(options.videoPositionStep)); + return false; + // "Next image" key + case options.nextImgKey: + var linkData = hz.currentLink.data(); + if (linkData.hoverZoomGallerySrc && linkData.hoverZoomGallerySrc.length > 1) rotateGalleryImg(1); + else changeVideoPosition(parseInt(options.videoPositionStep)); + return false; + // "Flip image" key + case options.flipImageKey: + flipImage(); + return false; + case options.openImageInWindowKey: + if (srcDetails.video) openVideoInWindow(); + else if (srcDetails.audio) openAudioInWindow(); + else openImageInWindow(); + return false; + case options.openImageInTabKey: + if (srcDetails.video) openVideoInTab(event.shiftKey); + else if (srcDetails.audio) openAudioInTab(); + else openImageInTab(event.shiftKey); + return false; + case options.saveImageKey: + saveImage(); + return false; + default: + return; + } + } + function documentContextMenu(event) { - // If it's been less than 300ms since right click, lock viewer and prevent context menu. - var lockElapsed = event.timeStamp - lockViewerClickTime; - if (imgFullSize && !viewerLocked && options.lockImageKey === -1 && lockElapsed < 300) { - lockViewer(); + // If right click is a long press, prevent context menu + if (longRightPress) { + longRightPress = false; event.preventDefault(); } } function documentMouseDown(event) { - // Right click pressed and lockImageKey is set to special value for right click (-1). - if (imgFullSize && !viewerLocked && options.lockImageKey === -1 && event.button === 2) { - lockViewerClickTime = event.timeStamp; - } else if (imgFullSize && event.target !== hz.hzViewer[0] && event.target !== imgFullSize[0]) { - if (viewerLocked && event.button === 0) { + // if image is locked and left click is pressed outside of locked image + if (event.button === 0 && imgFullSize && event.target !== hz.hzViewer[0] && event.target !== imgFullSize[0]) { + if (viewerLocked) { viewerLocked = false; } cancelSourceLoading(); restoreTitles(); + return; + } else if (event.button === 0) { // We don't need left click + return; + } + + // Gets mouse button key from event.button + const mouseButtonKey = [null,-2,-1,null,null][event.button]; // -2 is middle click, -1 is right click + switch (mouseButtonKey) { + case options.actionKey: + case options.toggleKey: + case options.fullZoomKey: + case options.closeKey: + case options.hideKey: + mouseButtonKeyHandler(mouseButtonKey, this); + return; + default: + // The following only trigger when image is displayed + if (imgFullSize) { + switch (mouseButtonKey) { + case options.lockImageKey: + case options.copyImageKey: + case options.copyImageUrlKey: + case options.flipImageKey: + case options.openImageInWindowKey: + case options.openImageInTabKey: + case options.saveImageKey: + mouseButtonKeyHandler(mouseButtonKey); + return; + default: + break; + } + } + return; + } + } + + function documentMouseUp(event) { + if (event.button === 0) return; // If left click, return + const mouseButtonKey = [null,-2,-1,null,null][event.button]; // -2 is middle click, -1 is right click + switch (mouseButtonKey) { + case options.actionKey: + if (actionKeyDown) { + actionKeyDown = false; + closeHoverZoomViewer(); + } + break; + case options.fullZoomKey: + fullZoomKeyDown = false; + $(this).mousemove(); + break; + case options.hideKey: + hideKeyDown = false; + if (imgFullSize) { + hz.hzViewer.show(); + playMedias(); + } + $(this).mousemove(); + break; + default: + break; } + clearMouseButtonTimers(mouseButtonKey); } // select correct font size for msg depending on img or video width @@ -1184,7 +1360,7 @@ var hoverZoom = { return; } - var src = (srcDetails.audioUrl ? srcDetails.audioUrl : srcDetails.url); + var src = (srcDetails.audioUrl ? srcDetails.audioUrl : srcDetails.url).replace('.audio', ''); // audio controls are displayed on top of an image provided by extension: 'images/spectrogram.png' srcDetails.url = chrome.extension.getURL('images/spectrogram.png'); @@ -1196,7 +1372,7 @@ var hoverZoom = { audio.controls = true; // controls always visible even if not locked audio.autoplay = true; audio.volume = options.audioVolume; - audio.src = src; + audio.src = srcDetails.audioUrl; audioControls = $(audio).appendTo(hz.hzViewer); audio.addEventListener('error', srcFullSizeOnError); @@ -1583,6 +1759,11 @@ var hoverZoom = { function displayFullSizeImage() { cLog('displayFullSizeImage'); + + // if autoLockImages option is checked + if (options.autoLockImages) + viewerLocked = true; + // check focus let focus = document.hasFocus(); @@ -1786,51 +1967,60 @@ var hoverZoom = { if (options.detailsLocation === "below") if (hzBelow.find('#hzDetails').length == 0) hzDetails = $('
', {id:'hzDetails'}).css(hzDetailsCss).appendTo(hzBelow); - - if (hzDetails.find('#hzDetailFilename').length == 0) - $('
', {id:'hzDetailFilename', text:details.filename, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailFilename').text(details.filename); - - if (hzDetails.find('#hzDetailHost').length == 0) - $('
', {id:'hzDetailHost', text:details.host, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailHost').text(details.host); - - if (hzDetails.find('#hzDetailLastModified').length == 0) - $('
', {id:'hzDetailLastModified', text:details.lastModified, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailLastModified').text(details.lastModified); - - if (hzDetails.find('#hzDetailExtension').length == 0) - $('
', {id:'hzDetailExtension', text:details.extension.toUpperCase(), class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailExtension').text(details.extension.toUpperCase()); - - if (hzDetails.find('#hzDetailContentLength').length == 0) - $('
', {id:'hzDetailContentLength', text:details.contentLength, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailContentLength').text(details.contentLength); - - if (hzDetails.find('#hzDetailDuration').length == 0) - $('
', {id:'hzDetailDuration', text:details.duration, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailDuration').text(details.duration); - - if (hzDetails.find('#hzDetailScale').length == 0) - $('
', {id:'hzDetailScale', text:details.scale, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailScale').text(details.scale); - - if (hzDetails.find('#hzDetailRatio').length == 0) - $('
', {id:'hzDetailRatio', text:details.ratio, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailRatio').text(details.ratio); - - if (hzDetails.find('#hzDetailDimensions').length == 0) - $('
', {id:'hzDetailDimensions', text:details.dimensions, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailDimensions').text(details.dimensions); + if (options.showDetailFilename) { + if (hzDetails.find('#hzDetailFilename').length == 0) + $('
', {id:'hzDetailFilename', text:details.filename, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailFilename').text(details.filename); + } + if (options.showDetailHost) { + if (hzDetails.find('#hzDetailHost').length == 0) + $('
', {id:'hzDetailHost', text:details.host, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailHost').text(details.host); + } + if (options.showDetailLastModified) { + if (hzDetails.find('#hzDetailLastModified').length == 0) + $('
', {id:'hzDetailLastModified', text:details.lastModified, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailLastModified').text(details.lastModified); + } + if (options.showDetailExtension) { + if (hzDetails.find('#hzDetailExtension').length == 0) + $('
', {id:'hzDetailExtension', text:details.extension.toUpperCase(), class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailExtension').text(details.extension.toUpperCase()); + } + if (options.showDetailContentLength) { + if (hzDetails.find('#hzDetailContentLength').length == 0) + $('
', {id:'hzDetailContentLength', text:details.contentLength, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailContentLength').text(details.contentLength); + } + if (options.showDetailDuration) { + if (hzDetails.find('#hzDetailDuration').length == 0) + $('
', {id:'hzDetailDuration', text:details.duration, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailDuration').text(details.duration); + } + if (options.showDetailScale) { + if (hzDetails.find('#hzDetailScale').length == 0) + $('
', {id:'hzDetailScale', text:details.scale, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailScale').text(details.scale); + } + if (options.showDetailRatio) { + if (hzDetails.find('#hzDetailRatio').length == 0) + $('
', {id:'hzDetailRatio', text:details.ratio, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailRatio').text(details.ratio); + } + if (options.showDetailDimensions) { + if (hzDetails.find('#hzDetailDimensions').length == 0) + $('
', {id:'hzDetailDimensions', text:details.dimensions, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailDimensions').text(details.dimensions); + } } } @@ -1946,7 +2136,7 @@ var hoverZoom = { // if video comes with distinct url for audio then extension = video's extension details.extension = getExtensionFromUrl(srcDetails.audio && !srcDetails.video ? srcDetails.audioUrl : srcDetails.url, srcDetails.video, srcDetails.playlist, srcDetails.audio); details.host = srcDetails.host; - let filename = getDownloadFilename(); + let filename = getFilename(); if (filename) details.filename = filename; let duration = (srcDetails.audio && !srcDetails.video ? getDurationFromAudio() : getDurationFromVideo()); if (duration) details.duration = duration.replace(/ /g, ':'); @@ -2028,7 +2218,7 @@ var hoverZoom = { // Skip if the image has the same URL as the thumbnail. if (linkData.hoverZoomSrc && linkData.hoverZoomSrc.length) { var url = linkData.hoverZoomSrc[0], - skip = url === link.attr('src'); + skip = (link.is('img') && url === link.attr('src')); if (!skip) { link.find('img[src]').each(function () { if (this.src === url) { @@ -2346,7 +2536,7 @@ var hoverZoom = { // for instance, on TripAdvisor: // img's src placeholder is replaced by real img url stored in data-lazyurl as user scrolls down $(document).on('scroll mousewheel', function() { - let scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" + let scrollTop = window.scrollY || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" if (scrollTop < lastScrollTop) { lastScrollTop = scrollTop < 0 ? 0 : scrollTop; // For Mobile or negative scrolling } else if (scrollTop > lastScrollTop + deltaMin) { @@ -2361,6 +2551,7 @@ var hoverZoom = { $(document).contextmenu(documentContextMenu); $(document).mousemove(documentMouseMove).mousedown(documentMouseDown).mouseleave(cancelSourceLoading); + $(document).on('mouseup', function(event) { documentMouseUp(event); }) $(document).keydown(documentOnKeyDown).keyup(documentOnKeyUp); if (options.galleriesMouseWheel) { window.addEventListener('wheel', documentOnMouseWheel, {passive: false}); @@ -2428,6 +2619,43 @@ var hoverZoom = { } } + function toggleKey() { + options.extensionEnabled = !options.extensionEnabled; + if (!options.extensionEnabled) { + // close zoomed image or video + viewerLocked = false; + if (hz.hzViewer) { + stopMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } + } + } + + function closeKey() { + viewerLocked = false; + if (hz.hzViewer) { + stopMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } + } + + function hideKey(){ + hideKeyDown = true; + if (hz.hzViewer) { + pauseMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } + } + function documentOnKeyDown(event) { // Skips if an input controlled is focused if (event.target && ['INPUT','TEXTAREA','SELECT'].indexOf(event.target.tagName) > -1) { @@ -2438,18 +2666,7 @@ var hoverZoom = { // Toggle key is pressed down if (keyCode === options.toggleKey) { - options.extensionEnabled = !options.extensionEnabled; - if (!options.extensionEnabled) { - // close zoomed image or video - viewerLocked = false; - if (hz.hzViewer) { - stopMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } - } + toggleKey(); } // Action key (zoom image) is pressed down @@ -2473,27 +2690,13 @@ var hoverZoom = { // close key (close zoomed image) is pressed down // => zoomed image is closed immediately if (keyCode === options.closeKey) { - viewerLocked = false; - if (hz.hzViewer) { - stopMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } + closeKey(); } // hide key (hide zoomed image) is pressed down // => zoomed image remains hidden until key is released if (keyCode === options.hideKey && !hideKeyDown) { - hideKeyDown = true; - if (hz.hzViewer) { - pauseMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } + hideKey(); } // the following keys are processed only if an image is displayed @@ -3046,7 +3249,7 @@ var hoverZoom = { function downloadResource(url, filename, callback) { cLog('download: ' + url); if (!filename) filename = url.split('\\').pop().split('/').pop(); - + if (filename.startsWith('.')) filename = 'download' + filename; // prefix with download folder if needed if (options.downloadFolder) { cLog('options.downloadFolder: ' + options.downloadFolder); @@ -3079,8 +3282,8 @@ var hoverZoom = { } } - // 4 types of media can be saved to disk: image, video, audio, playlist - const downloadMedias = { + // 5 types of media can be saved to disk: image, video, audio, playlist, subtitles + const fileMedias = { IMG : "IMG", VIDEO : "VIDEO", AUDIO : "AUDIO", @@ -3088,89 +3291,118 @@ var hoverZoom = { SUBTITLES : "SUBTITLES" } - // return download filename without knowing type of download - function getDownloadFilename() { + // return filename without knowing type of media displayed + function getFilename() { - let filename = getDownloadFilenameByMedia(downloadMedias.IMG); + let filename = getFilenameByMedia(fileMedias.IMG, false); if (filename) return filename; - filename = getDownloadFilenameByMedia(downloadMedias.VIDEO); + filename = getFilenameByMedia(fileMedias.VIDEO, false); if (filename) return filename; - filename = getDownloadFilenameByMedia(downloadMedias.AUDIO); + filename = getFilenameByMedia(fileMedias.AUDIO, false); if (filename) return filename; - filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST); + filename = getFilenameByMedia(fileMedias.PLAYLIST, false); if (filename) return filename; - filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES); + filename = getFilenameByMedia(fileMedias.SUBTITLES, false); if (filename) return filename; return ''; } - // return download filename according to type of download in param - function getDownloadFilenameByMedia(downloadMedia) { + function replaceOriginalFilename(filename) { + if (options.replaceOriginalFilename) { + if (filename.indexOf('.') !== -1) filename = filename.replace(/(.*)\.(.*)/, `${options.downloadFilename}.$2`); + else filename = options.downloadFilename; + } + return filename; + } + + // return original or download filename according to type of media in param + function getFilenameByMedia(fileMedia, download = true) { let src, filename; - switch (downloadMedia) { - case downloadMedias.IMG: - if (!hz.hzViewer) return ''; + switch (fileMedia) { + case fileMedias.IMG: + if (!hz.hzViewer) return undefined; let img = hz.hzViewer.find('img:not(.hzPlaceholder)').get(0); - if (!img) return ''; + if (!img) return undefined; src = img.src; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename == '') filename = 'image'; - if (filename.indexOf('.') === -1) filename = filename + '.jpg'; + if (filename === '') { + filename = 'image'; + } + if (download) { + filename = replaceOriginalFilename(filename); + if (filename.indexOf('.') === -1) filename = filename + '.jpg'; // add default extension for download + } return filename; - case downloadMedias.VIDEO: - if (!hz.hzViewer) return ''; + case fileMedias.VIDEO: + if (!hz.hzViewer) return undefined; let video = hz.hzViewer.find('video').get(0); - if (!video) return ''; + if (!video) return undefined; src = video.src; - if (src.startsWith('blob:')) return ''; + if (src.startsWith('blob:')) return undefined; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename == '') filename = 'video'; - if (filename.indexOf('.') === -1) filename = filename + '.mp4'; + if (filename === '') { + filename = 'video'; + } + if (download) { + filename = replaceOriginalFilename(filename); + if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download + } return filename; - case downloadMedias.AUDIO: - if (!hz.hzViewer) return ''; + case fileMedias.AUDIO: + if (!hz.hzViewer) return undefined; let audio = hz.hzViewer.find('audio').get(0); - if (!audio) return ''; + if (!audio) return undefined; src = audio.src; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename == '') filename = 'audio'; - if (filename.indexOf('.') === -1) filename = filename + '.mp4'; + if (filename === '') { + filename = 'audio'; + } + if (download) { + filename = replaceOriginalFilename(filename); + if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download + } return filename; - case downloadMedias.PLAYLIST: - if (!hz.hzViewer) return ''; - if (!srcDetails.playlist) return ''; + case fileMedias.PLAYLIST: + if (!hz.hzViewer) return undefined; + if (!srcDetails.playlist) return undefined; src = srcDetails.url; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - filename = 'playlist-' + filename; - if (filename.indexOf('.') === -1) filename = filename + '.m3u8'; + if (download) { + filename = replaceOriginalFilename(filename); + filename = 'playlist-' + filename; + if (filename.indexOf('.') === -1) filename = filename + '.m3u8'; // add default extension for download + } return filename; - case downloadMedias.SUBTITLES: - if (!hz.hzViewer) return ''; - if (!srcDetails.subtitlesUrl) return ''; + case fileMedias.SUBTITLES: + if (!hz.hzViewer) return undefined; + if (!srcDetails.subtitlesUrl) return undefined; src = srcDetails.subtitlesUrl; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - filename = 'subtitles-' + filename; - if (filename.indexOf('.') === -1) filename = filename + '.txt'; + if (download) { + filename = replaceOriginalFilename(filename); + filename = 'subtitles-' + filename; + if (filename.indexOf('.') === -1) filename = filename + '.txt'; // add default extension for download + } return filename; } return ''; @@ -3248,14 +3480,33 @@ var hoverZoom = { let img = hz.hzViewer.find('img').get(0); if (!img) return; let src = img.src; - let filename = getDownloadFilenameByMedia(downloadMedias.IMG); + let filename = getFilenameByMedia(fileMedias.IMG); if (!filename) return; + if (options.addDownloadCaption) { + // prefix with caption + let caption = getCaption(); + if (caption) { + caption = '[' + caption + ']'; + filename = caption + filename; + } + } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + img.naturalWidth + 'x' + img.naturalHeight + ']'; + let size = '[' + getSizeImage(img) + ']'; filename = size + filename; } + if (options.addDownloadIndex) { + let gallery = hz.currentLink.data().hoverZoomGallerySrc; + let index = hz.currentLink.data().hoverZoomGalleryIndex; + if (gallery) { + index++; + let indexLen = index.toString().length; + let galleryLen = gallery.length.toString().length + let galleryIndex = `[${index.toString().padStart(galleryLen,'0')}-${gallery.length}]`; + filename = galleryIndex + filename; + } + } if (options.addDownloadOrigin) { // prefix with origin let origin = '[' + getOrigin() + ']'; @@ -3270,12 +3521,20 @@ var hoverZoom = { if (!video) return; let src = video.src; if (src.startsWith('blob:')) return; - let filename = getDownloadFilenameByMedia(downloadMedias.VIDEO); + let filename = getFilenameByMedia(fileMedias.VIDEO); if (!filename) return; + if (options.addDownloadCaption) { + // prefix with caption + let caption = getCaption(); + if (caption) { + caption = '[' + caption + ']'; + filename = caption + filename; + } + } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + video.videoWidth + 'x' + video.videoHeight + ']'; + let size = '[' + getSizeVideo(video) + ']'; filename = size + filename; } if (options.addDownloadDuration) { @@ -3296,9 +3555,17 @@ var hoverZoom = { let audio = hz.hzViewer.find('audio').get(0); if (!audio) return; let src = audio.src; - let filename = getDownloadFilenameByMedia(downloadMedias.AUDIO); + let filename = getFilenameByMedia(fileMedias.AUDIO); if (!filename) return; + if (options.addDownloadCaption) { + // prefix with caption + let caption = getCaption(); + if (caption) { + caption = '[' + caption + ']'; + filename = caption + filename; + } + } if (options.addDownloadDuration) { // prefix with duration [hh mm ss] let duration = hz.secondsToHms(audio.duration); @@ -3317,9 +3584,17 @@ var hoverZoom = { let video = hz.hzViewer.find('video').get(0); let audio = hz.hzViewer.find('audio').get(0); if (!video && !audio) return; - let filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES); + let filename = getFilenameByMedia(fileMedias.SUBTITLES); if (!filename) return; + if (options.addDownloadCaption) { + // prefix with caption + let caption = getCaption(); + if (caption) { + caption = '[' + caption + ']'; + filename = caption + filename; + } + } if (options.addDownloadOrigin) { // prefix with origin let origin = '[' + getOrigin() + ']'; @@ -3334,12 +3609,20 @@ var hoverZoom = { if (!hz.hzViewer) return; let video = hz.hzViewer.find('video').get(0); if (!video) return; - let filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST); + let filename = getFilenameByMedia(fileMedias.PLAYLIST); if (!filename) return; + if (options.addDownloadCaption) { + // prefix with caption + let caption = getCaption(); + if (caption) { + caption = '[' + caption + ']'; + filename = caption + filename; + } + } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + video.videoWidth + 'x' + video.videoHeight + ']'; + let size = '[' + getSizeVideo(video) + ']'; filename = size + filename; } if (options.addDownloadDuration) { @@ -3353,12 +3636,6 @@ var hoverZoom = { filename = origin + filename; } - // prefix with download folder if needed - if (options.downloadFolder) { - let downloadFolder = options.downloadFolder; - filename = downloadFolder + filename; - } - // download KO: This function must be called during a user gesture => debugger must be closed downloadResource(srcDetails.url, filename); savePlaylistAsMP3MP4(filename); @@ -3367,6 +3644,14 @@ var hoverZoom = { // - filename.m3u8.mp4 (video part) // - filename.m3u8.mp3 (audio part) function savePlaylistAsMP3MP4(filename) { + // prefix with download folder if needed + if (options.downloadFolder) { + cLog.log('options.downloadFolder: ' + options.downloadFolder); + let downloadFolder = options.downloadFolder; + filename = downloadFolder + filename; + cLog.log('filename: ' + filename); + } + // audio if (fmp4Data['audio'].length) { const blobAudio = new Blob([arrayConcat(fmp4Data['audio'])], {type:'application/octet-stream'}); @@ -3424,6 +3709,22 @@ var hoverZoom = { return window.location.hostname.replace(regexForbiddenChars, '_'); } + // return displayed size (W x H) + function getSizeVideo(video) { + return video.videoWidth + 'x' + video.videoHeight; + } + + // return displayed size (W x H) + function getSizeImage(img) { + return img.naturalWidth + 'x' + img.naturalHeight; + } + + // return caption with forbidden characters replaced by '_' + function getCaption() { + let caption = hz.currentLink.data().hoverZoomCaption || hz.currentLink.data().hoverZoomGalleryCaption || ''; + return caption.replace(regexForbiddenChars, '_'); + } + function rotateGalleryImg(rot) { cLog('rotateGalleryImg(' + rot + ')'); var link = hz.currentLink, data = link.data(); @@ -3508,7 +3809,7 @@ var hoverZoom = { maxHeight(options.maxHeight); webSiteExcluded = null; - body100pct = (body.css('position') != 'static') || (body.css('padding-left') == '0px' && body.css('padding-right') == '0px' && body.css('margin-left') == '0px' && body.css('margin-right') == '0px'); + body100pct = (body.css('position') != 'static') || (body.css('padding-left') == '0px' && body.css('padding-right') == '0px' && body.css('margin-left') == '0px' && body.css('margin-right') == '0px' && (body.css('max-width') == 'none' || body.css('max-width') == '100%')); hz.pageGenerator = $('meta[name="generator"]').attr('content'); prepareImgLinks(); bindEvents(); @@ -3627,7 +3928,7 @@ var hoverZoom = { // Simulates a mousemove event to force a zoom call displayPicFromElement:function (el, force) { - if (el.is(':hover') || force) { + if (el.filter(':hover').length > 0 || force) { hoverZoom.currentLink = el; $(document).mousemove(); } @@ -3776,28 +4077,26 @@ var hoverZoom = { prepareFromDocument:function (link, url, getSrc, isAsync = false) { url = url.replace('http:', location.protocol); chrome.runtime.sendMessage({action:'ajaxRequest', url: url, method: 'GET'}, function(data) { - var doc = document.implementation.createHTMLDocument(); - doc.open(); - doc.write(data); - doc.close(); - var httpRefresh = doc.querySelector('meta[http-equiv="refresh"][content]'); + let doc = document.implementation.createHTMLDocument(); + doc.body.innerHTML = data; + const httpRefresh = doc.querySelector('meta[http-equiv="refresh"][content]'); if (httpRefresh) { - var redirUrl = httpRefresh.content.substr(httpRefresh.content.toLowerCase().indexOf('url=') + 4); + let redirUrl = httpRefresh.content.substr(httpRefresh.content.toLowerCase().indexOf('url=') + 4); if (redirUrl) { redirUrl = redirUrl.replace('http:', location.protocol); hoverZoom.prepareFromDocument(link, redirUrl, getSrc, isAsync); } } - var handleSrc = function (src) { - if (src) - hoverZoom.prepareLink(link, src); + const handleSrc = function (src) { + if (src) + hoverZoom.prepareLink(link, src); }; if (isAsync) { getSrc(doc, handleSrc); } else { - var src = getSrc(doc); + let src = getSrc(doc); handleSrc(src); } }); @@ -4139,6 +4438,88 @@ var hoverZoom = { } result.openPos = result.closePos = -1; return result; + }, + + // Return largest src available in srcset according to width and density + // samples srcsets: + // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg" + // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg 489w, http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble-768x1099.jpg 768w" + // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg 1x, http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble-768x1099.jpg 2x" + // "resize1-lejdd.ladmedia.fr/rcrop/620,310/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 620w, + // resize1-lejdd.ladmedia.fr/rcrop/300,150/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 300w, + // resize1-lejdd.ladmedia.fr/rcrop/710,355/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 710w, + // resize1-lejdd.ladmedia.fr/rcrop/940,470/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 940w" + // "https://video-images.vice.com/_uncategorized/1522934375314-retinite1.png?resize=400:*, https://video-images.vice.com/_uncategorized/1522934375314-retinite1.png?resize=600:* 2x" + // "https://www.parismatch.com/lmnr/f/webp/r/72,48,forcex,center-middle/img/var/pm/public/media/image/2024/03/31/12/2024-03-31t094641z_493903911_rc2xw6a0kxis_rtrmadp_3_britain-royals.jpg?VersionId=V90sjBHDp8nZOHJEoSwrJK3SThPIaGtD, + // https://www.parismatch.com/lmnr/f/webp/r/144,96,forcex,center-middle/img/var/pm/public/media/image/2024/03/31/12/2024-03-31t094641z_493903911_rc2xw6a0kxis_rtrmadp_3_britain-royals.jpg?VersionId=V90sjBHDp8nZOHJEoSwrJK3SThPIaGtD 2x" + getBiggestSrcFromSrcset:function(srcset) { + + if (srcset == undefined) + return undefined; + + // discard inline images + if (hoverZoom.isEmbeddedImg(srcset)) + return undefined; + + var src = undefined; + srcset = srcset.trim(); + + srcset = srcset.replace(/,http/g, ', http'); + if (srcset.indexOf(", ") != -1) { + + if (srcset.indexOf("x, ") != -1) { srcset = srcset.split("x, "); } + else if (srcset.indexOf("w, ") != -1) { srcset = srcset.split("w, "); } + else { srcset = srcset.split(", "); } + + var urls = new Map(); + var xws = []; + // separate urls and density/width + for (var i = 0; i < srcset.length; i++) { + var el = srcset[i].trim(); + var url, xw; + if (el.indexOf(' ') == -1) { + url = el; + xw = "1x"; // default value + } + else { + url = el.split(' ')[0]; + xw = el.split(' ')[1]; + } + xw = xw.replace('x','').replace('w',''); + urls.set(parseInt(xw), url); + xws.push(parseInt(xw)); + } + // sort density/width + xws.sort(function(a, b){return b-a}); + // select url associated to largest density/width + src = urls.get(xws[0]); + } + else { + srcset = srcset.trim(); + if (srcset.indexOf(' ') == -1) { src = srcset; } + else { + src = srcset.split(' ')[0].trim(); + } + } + return src; + }, + + emptyHoverZoomViewer:function(now) { + if (!hoverZoom.hzViewer) return; + hoverZoom.hzViewer.stop(true, true).fadeOut(now ? 0 : options.fadeDuration, function () { + hoverZoom.hzViewer.empty(); + }); + }, + + // In JavaScript, keys can be strings, numbers, or identifier names WITHOUT single or double quotes + // e.g: person = {name:"John", age:31, city:"New York"}; + strToJavascriptObj:function(e) { + if (typeof e == "string") { + let obj = new Function("return" + e); + try { + return obj(); + } catch {} + } } }; diff --git a/js/options.js b/js/options.js index 6a987773b..09c5dc57b 100644 --- a/js/options.js +++ b/js/options.js @@ -47,12 +47,15 @@ function initActionKeys() { function loadKeys(sel) { $('').appendTo(sel); - if (sel.attr('id') != 'lockImageKey') + if (sel.attr('id') != 'selPrevImgKey' || sel.attr('id') != 'selNextImgKey'){ $('').appendTo(sel); + $('').appendTo(sel); + } if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); $('').appendTo(sel); + $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } @@ -77,7 +80,7 @@ function loadKeys(sel) { // Saves options to localStorage. // TODO: Migrate to https://developer.chrome.com/extensions/storage -function saveOptions() { +function saveOptions(exportSettings = false) { options.extensionEnabled = $('#chkExtensionEnabled')[0].checked; options.darkMode = $('#chkDarkMode')[0].checked; options.zoomFactor = $('#txtZoomFactor')[0].value; @@ -105,6 +108,7 @@ function saveOptions() { options.ambilightHaloSize = $('#txtAmbilightHaloSize')[0].value / 100; options.ambilightBackgroundOpacity = $('#txtAmbilightBackgroundOpacity')[0].value / 100; options.centerImages = $('#chkCenterImages')[0].checked; + options.autoLockImages = $('#chkAutoLockImages')[0].checked; options.frameBackgroundColor = $('#pickerFrameBackgroundColor')[0].value; options.frameThickness = $('#txtFrameThickness')[0].value; @@ -125,6 +129,16 @@ function saveOptions() { options[key] = parseInt($('#sel' + id).val()); }); + options.showDetailFilename = $('#chkShowDetailFilename')[0].checked; + options.showDetailHost = $('#chkShowDetailHost')[0].checked; + options.showDetailLastModified = $('#chkShowDetailLastModified')[0].checked; + options.showDetailExtension = $('#chkShowDetailExtension')[0].checked; + options.showDetailContentLength = $('#chkShowDetailContentLength')[0].checked; + options.showDetailDuration = $('#chkShowDetailDuration')[0].checked; + options.showDetailScale = $('#chkShowDetailScale')[0].checked; + options.showDetailRatio = $('#chkShowDetailRatio')[0].checked; + options.showDetailDimensions = $('#chkShowDetailDimensions')[0].checked; + options.addToHistory = $('#chkAddToHistory')[0].checked; options.allowHeadersRewrite = $('#chkAllowHeadersRewrite')[0].checked; @@ -137,20 +151,31 @@ function saveOptions() { options.detailsLocation = $('#selectDetailsLocation').val(); options.fontSize = $('#txtFontSize')[0].value; options.fontOutline = $('#chkFontOutline')[0].checked; + options.belowPositionOffset = $('#txtBelowPositionOffset')[0].value; + options.abovePositionOffset = $('#txtAbovePositionOffset')[0].value; + options.captionOpacity = $('#txtCaptionOpacity')[0].value / 100; + options.detailsOpacity = $('#txtDetailsOpacity')[0].value / 100; options.displayImageLoader = $('#chkDisplayImageLoader')[0].checked; options.downloadFolder = $('#txtDownloadFolder')[0].value; options.addDownloadOrigin = $('#chkAddDownloadOrigin')[0].checked; options.addDownloadSize = $('#chkAddDownloadSize')[0].checked; - options.debug = $('#chkEnableDebug')[0].checked; options.addDownloadDuration = $('#chkAddDownloadDuration')[0].checked; + options.addDownloadIndex = $('#chkAddDownloadIndex')[0].checked; + options.addDownloadCaption = $('#chkAddDownloadCaption')[0].checked; + options.replaceOriginalFilename = $('#chkDownloadReplaceOriginalFilename')[0].checked; + options.downloadFilename = $('#txtDownloadReplaceOriginalFilename')[0].value; + options.debug = $('#chkEnableDebug')[0].checked; options.useSeparateTabOrWindowForUnloadableUrlsEnabled = $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked; options.useSeparateTabOrWindowForUnloadableUrls = $('#selectUseSeparateTabOrWindowForUnloadableUrls').val(); - localStorage.options = JSON.stringify(options); - - sendOptions(options); - restoreOptions(); + if (exportSettings) { + $('#txtBoxImportExportSettings').val(JSON.stringify(options)); + } else { + localStorage.options = JSON.stringify(options); + sendOptions(options); + restoreOptions(); + } return false; } @@ -200,6 +225,7 @@ function restoreOptions(optionsFromFactorySettings) { $('#rngAmbilightBackgroundOpacity').val(parseInt(options.ambilightBackgroundOpacity * 100)); $('#txtAmbilightBackgroundOpacity').val(parseInt(options.ambilightBackgroundOpacity * 100)); $('#chkCenterImages').trigger(options.centerImages ? 'gumby.check' : 'gumby.uncheck'); + $('#chkAutoLockImages').trigger(options.autoLockImages ? 'gumby.check' : 'gumby.uncheck'); $('#pickerFrameBackgroundColor').val(options.frameBackgroundColor); $('#rngFrameThickness').val(parseInt(options.frameThickness)); $('#txtFrameThickness').val(parseInt(options.frameThickness)); @@ -208,6 +234,10 @@ function restoreOptions(optionsFromFactorySettings) { $('#rngFontSize').val(parseInt(options.fontSize)); $('#txtFontSize').val(parseInt(options.fontSize)); $('#chkFontOutline').trigger(options.fontOutline ? 'gumby.check' : 'gumby.uncheck'); + $('#txtBelowPositionOffset').val(parseFloat(options.belowPositionOffset)); + $('#txtAbovePositionOffset').val(parseFloat(options.abovePositionOffset)); + $('#txtCaptionOpacity').val(parseInt(options.captionOpacity * 100)); + $('#txtDetailsOpacity').val(parseInt(options.detailsOpacity * 100)); if (options.frameBackgroundColor == "") { initColorPicker('#ffffff'); @@ -240,6 +270,16 @@ function restoreOptions(optionsFromFactorySettings) { $('#sel' + id).val(options[key]); }); + $('#chkShowDetailFilename').trigger(options.showDetailFilename ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailHost').trigger(options.showDetailHost ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailLastModified').trigger(options.showDetailLastModified ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailExtension').trigger(options.showDetailExtension ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailContentLength').trigger(options.showDetailContentLength ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailDuration').trigger(options.showDetailDuration ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailScale').trigger(options.showDetailScale ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailRatio').trigger(options.showDetailRatio ? 'gumby.check' : 'gumby.uncheck'); + $('#chkShowDetailDimensions').trigger(options.showDetailDimensions ? 'gumby.check' : 'gumby.uncheck'); + $('#chkAddToHistory').trigger(options.addToHistory ? 'gumby.check' : 'gumby.uncheck'); $('#chkAllowHeadersRewrite').trigger(options.allowHeadersRewrite ? 'gumby.check' : 'gumby.uncheck'); @@ -259,6 +299,10 @@ function restoreOptions(optionsFromFactorySettings) { $('#chkAddDownloadOrigin').trigger(options.addDownloadOrigin ? 'gumby.check' : 'gumby.uncheck'); $('#chkAddDownloadSize').trigger(options.addDownloadSize ? 'gumby.check' : 'gumby.uncheck'); $('#chkAddDownloadDuration').trigger(options.addDownloadDuration ? 'gumby.check' : 'gumby.uncheck'); + $('#chkAddDownloadIndex').trigger(options.addDownloadIndex ? 'gumby.check' : 'gumby.uncheck'); + $('#chkAddDownloadCaption').trigger(options.addDownloadCaption ? 'gumby.check' : 'gumby.uncheck'); + $('#chkDownloadReplaceOriginalFilename').trigger(options.replaceOriginalFilename ? 'gumby.check' : 'gumby.uncheck'); + $('#txtDownloadReplaceOriginalFilename').val(options.downloadFilename); $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').trigger(options.useSeparateTabOrWindowForUnloadableUrlsEnabled ? 'gumby.check' : 'gumby.uncheck'); $('#selectUseSeparateTabOrWindowForUnloadableUrls').val(options.useSeparateTabOrWindowForUnloadableUrls); $('#chkEnableDebug').trigger(options.debug ? 'gumby.check' : 'gumby.uncheck'); @@ -351,14 +395,15 @@ function btnRemoveExcludedSiteOnClick() { } function selKeyOnChange(event) { - var currSel = $(event.target); + const noneKey = '0'; // sel key code for 'none' + let currSel = $(event.target); if (currSel[0].dataset.val0 == undefined) return; // event fired before init currSel[0].dataset.val1 = currSel.val(); checkModification(currSel); - if (currSel.val() != '0') { + if (currSel.val() != noneKey) { $('.actionKey').each(function () { if (!$(this).is(currSel) && $(this).val() == currSel.val()) { - $(this).val('0'); + $(this).val(noneKey); $(this)[0].dataset.val1 = $(this).val(); checkModification($(this)); } @@ -453,6 +498,16 @@ function downloadFolderOnChange(val) { return this.value; } +// validate user input +function replaceOriginalFilenameOnChange(val) { + let value = (typeof val == 'string' ? val : this.value); + value = value.trim(); + // remove Windows Explorer forbidden characters for file name -> : * ? " < > | / + value = value.replace(/[!*:?"<>|\/\\]/g, ''); + this.value = value; + return this.value; +} + function updateDivAmbilight() { if ($('#chkAmbilightEnabled')[0].checked) { $('#divAmbilight').removeClass('disabled'); @@ -461,6 +516,14 @@ function updateDivAmbilight() { } } +function updateDownloadReplaceOriginalFilename() { + if ($('#chkDownloadReplaceOriginalFilename')[0].checked) { + $('#txtDownloadReplaceOriginalFilename').removeClass('disabled'); + } else { + $('#txtDownloadReplaceOriginalFilename').addClass('disabled'); + } +} + function updateUseSeparateTabOrWindowForUnloadableUrls() { if ($('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked) { $('#selectUseSeparateTabOrWindowForUnloadableUrls').removeClass('disabled'); @@ -508,6 +571,21 @@ function updateTxtFontSize() { $('#txtFontSize')[0].value = this.value; } +function updateTxtBelowPositionOffset() { + $('#txtBelowPositionOffset')[0].value = this.value; +} + +function updateTxtAbovePositionOffset() { + $('#txtAbovePositionOffset')[0].value = this.value; +} + +function updateTxtCaptionOpacity() { + $('#txtCaptionOpacity')[0].value = this.value; +} +function updateTxtDetailsOpacity() { + $('#txtDetailsOpacity')[0].value = this.value; +} + function updateRngFontSize() { this.value = percentageOnChange(this.value); $('#rngFontSize').val(this.value); @@ -607,6 +685,8 @@ function initColorPicker(color){ const Saved = Symbol("saved"); const Cancel = Symbol("cancel"); const Reset = Symbol("reset"); +const Imported = Symbol("imported"); +const ImportFail = Symbol("importFail"); function displayMsg(msg) { switch (msg) { case Saved: @@ -618,6 +698,12 @@ function displayMsg(msg) { case Reset: $('#msgtxt').removeClass().addClass('centered text-center alert info').text(chrome.i18n.getMessage('optReset')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); break; + case Imported: + $('#msgtxt').removeClass().addClass('centered text-center alert success').text(chrome.i18n.getMessage('optImport')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); + break; + case ImportFail: + $('#msgtxt').removeClass().addClass('centered text-center alert danger').text(chrome.i18n.getMessage('optImportFailed')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); + break; default: break; } @@ -637,6 +723,8 @@ $(function () { $('#btnReset').click(function() { restoreOptionsFromFactorySettings(); displayMsg(Reset); return false; }); $('#btnDisableAllPlugins').click(function() { disableAllPlugins(); return false; }); $('#btnEnableAllPlugins').click(function() { enableAllPlugins(); return false; }); + $('#btnImportSettings').click(function() { importSettings(); return false; }); + $('#btnExportSettings').click(function() { exportSettings(); return false; }); $('#chkWhiteListMode').parent().on('gumby.onChange', chkWhiteListModeOnChange); $('#txtZoomFactor').change(percentageOnChange); $('#txtPicturesOpacity').change(percentageOnChange); @@ -653,11 +741,17 @@ $(function () { $('#txtFrameThickness').change(updateRngFrameThickness); $('#rngFontSize').on('input change', updateTxtFontSize); $('#txtFontSize').change(updateRngFontSize); + $('#txtBelowPositionOffset').change(updateTxtBelowPositionOffset); + $('#txtAbovePositionOffset').change(updateTxtAbovePositionOffset); + $('#txtCaptionOpacity').change(updateTxtCaptionOpacity); + $('#txtDetailsOpacity').change(updateTxtDetailsOpacity); $('#txtVideoPositionStep').change(percentageOnChange); $('.actionKey').change(selKeyOnChange); $('#btnAddExcludedSite').click(btnAddExcludedSiteOnClick); $('#btnRemoveExcludedSite').click(btnRemoveExcludedSiteOnClick); $('#txtDownloadFolder').change(downloadFolderOnChange); + $('#chkDownloadReplaceOriginalFilename').parent().on('gumby.onChange', updateDownloadReplaceOriginalFilename); + $('#txtDownloadReplaceOriginalFilename').change(replaceOriginalFilenameOnChange); $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').parent().on('gumby.onChange', updateUseSeparateTabOrWindowForUnloadableUrls); $('#chkHideMouseCursor').parent().on('gumby.onChange', updateDivHideMouseCursor); $('#chkDarkMode').parent().on('gumby.onChange', updateDarkMode); @@ -681,6 +775,32 @@ function enableAllPlugins() { $('input.chkPlugin').each(function() { $(this).trigger('gumby.check'); }) } +//Checks if string is JSON. +//If yes, imports settings and clears textarea. +function importSettings() { + let jsonImport; + try { + jsonImport = JSON.parse($('#txtBoxImportExportSettings')[0].value); + // Checks if a few HZ+ settings are defined to test if it's a valid HZ+ JSON + const jsonTest = [jsonImport.centerImages, jsonImport.fullZoomKey, jsonImport.hideMouseCursor]; + jsonTest.forEach((variable) => { + if (typeof variable === 'undefined') { + throw new Error('Not a valid HZ+ import JSON'); + } + }); + } catch (e) { + displayMsg(ImportFail); + return false; + } + displayMsg(Imported); + restoreOptions({jsonImport}); + $('#txtBoxImportExportSettings').val(''); +} + +function exportSettings() { + saveOptions(true); +} + // highlight item if modified, unhighlight if not modified function checkModification(item) { if (item[0].dataset.val1 == undefined) return; diff --git a/js/popup.js b/js/popup.js index 782d57d49..160abb0ff 100644 --- a/js/popup.js +++ b/js/popup.js @@ -40,12 +40,15 @@ function initActionKeys() { function loadKeys(sel) { $('').appendTo(sel); - if (sel.attr('id') != 'lockImageKey') + if (sel.attr('id') != 'selPrevImgKey' || sel.attr('id') != 'selNextImgKey'){ $('').appendTo(sel); + $('').appendTo(sel); + } if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); $('').appendTo(sel); + $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } @@ -156,14 +159,15 @@ function restoreOptions(optionsFromFactorySettings) { } function selKeyOnChange(event) { - var currSel = $(event.target); + const noneKey = '0'; // sel key code for 'none' + let currSel = $(event.target); if (currSel[0].dataset.val0 == undefined) return; // event fired before init currSel[0].dataset.val1 = currSel.val(); checkModification(currSel); - if (currSel.val() != '0') { + if (currSel.val() != noneKey) { $('.actionKey').each(function () { if (!$(this).is(currSel) && $(this).val() == currSel.val()) { - $(this).val('0'); + $(this).val(noneKey); $(this)[0].dataset.val1 = $(this).val(); checkModification($(this)); } diff --git a/manifest.json b/manifest.json index 49b3a3e30..57fb8e482 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_extName__", "short_name": "__MSG_extShortName__", - "version": "1.0.213", + "version": "1.0.222", "description": "__MSG_extDescription__", "homepage_url": "https://github.com/extesy/hoverzoom/", "author": "Oleg Anashkin", @@ -101,6 +101,7 @@ "plugins/itaku_a.js", "plugins/kick_a.js", "plugins/lensdump_a.js", + "plugins/media_a.js", "plugins/mediawiki_a.js", "plugins/medium_a.js", "plugins/niconico_a.js", @@ -475,6 +476,7 @@ { "js": ["plugins/twitter.js"], "matches": [ + "*://*.x.com/*", "*://*.twitter.com/*", "*://*.tweetdeck.com/*" ] @@ -483,6 +485,10 @@ "js": ["plugins/twitpic.js"], "matches": ["*://*.twitter.com/*"] }, + { + "js": ["plugins/gifbin.js"], + "matches": ["*://gifbin.com/*"] + }, { "js": ["plugins/ravelry.js"], "matches": ["*://*.ravelry.com/*"] @@ -976,6 +982,7 @@ "js": ["plugins/e621.js"], "matches": [ "*://*.e621.net/*", + "*://*.e6ai.net/*", "*://*.e926.net/*", "*://danbooru.donmai.us/*" ] @@ -1025,6 +1032,10 @@ "js": ["plugins/furaffinity.js"], "matches": ["*://*.furaffinity.net/*"] }, + { + "js": ["plugins/furrynetwork.js"], + "matches": ["*://*.furrynetwork.com/*"] + }, { "js": ["plugins/alicdn.js"], "matches": [ @@ -1267,6 +1278,10 @@ "js": ["plugins/artstation.js"], "matches": ["*://*.artstation.com/*"] }, + { + "js": ["plugins/artfol.js"], + "matches": ["*://*.artfol.co/*"] + }, { "js": ["plugins/artsper.js"], "matches": ["*://*.artsper.com/*"] @@ -1929,6 +1944,10 @@ "js": ["plugins/head-fi.js"], "matches": ["*://*.head-fi.org/*"] }, + { + "js": ["plugins/etejo.js"], + "matches": ["*://*.etejo.com/*"] + }, { "js": ["plugins/wildcritters.js"], "matches": ["*://*.wildcritters.ws/*"] @@ -1973,6 +1992,10 @@ "js": ["plugins/kleinanzeigen.js"], "matches": ["*://*.kleinanzeigen.de/*"] }, + { + "js": ["plugins/tenor.js"], + "matches": ["*://*.tenor.com/*"] + }, { "js": ["plugins/trakttv.js"], "matches": ["*://*.trakt.tv/*"] @@ -2024,6 +2047,118 @@ { "js": ["plugins/bluesky.js"], "matches": ["*://*.bsky.app/*"] + }, + { + "js": ["plugins/onzemondial.js"], + "matches": ["*://*.onzemondial.com/*"] + }, + { + "js": ["plugins/cineserie.js"], + "matches": ["*://*.cineserie.com/*"] + }, + { + "js": ["plugins/opendata92.js"], + "matches": ["*://*.opendata.hauts-de-seine.fr/*"] + }, + { + "js": ["plugins/nature.js"], + "matches": ["*://*.nature.com/*"] + }, + { + "js": ["plugins/galerie9art.js"], + "matches": ["*://*.galerie9art.fr/*"] + }, + { + "js": ["plugins/worldatlas.js"], + "matches": ["*://*.worldatlas.com/*"] + }, + { + "js": ["plugins/kotnauction.js"], + "matches": ["*://*.kotnauction.com/*"] + }, + { + "js": ["plugins/techradar.js"], + "matches": ["*://*.techradar.com/*"] + }, + { + "js": ["plugins/feedly.js"], + "matches": ["*://*.feedly.com/*"] + }, + { + "js": ["plugins/inoreader.js"], + "matches": ["*://*.inoreader.com/*"] + }, + { + "js": ["plugins/unsplash.js"], + "matches": ["*://*.unsplash.com/*"] + }, + { + "js": ["plugins/usarmy.js"], + "matches": ["*://*.defense.gov/*", "*://*.mil/*"] + }, + { + "js": ["plugins/lacroix.js"], + "matches": ["*://*.la-croix.com/*"] + }, + { + "js": ["plugins/routard.js"], + "matches": ["*://*.routard.com/*"] + }, + { + "js": ["plugins/podcasts_apple.js"], + "matches": ["*://podcasts.apple.com/*"] + }, + { + "js": ["plugins/apple.js"], + "matches": ["*://apps.apple.com/*", "*://books.apple.com/*", "*://music.apple.com/*", "*://podcasts.apple.com/*"] + }, + { + "js": ["plugins/monuments_nationaux.js"], + "matches": ["*://*.monuments-nationaux.fr/*"] + }, + { + "js": ["plugins/physorg.js"], + "matches": ["*://*.phys.org/*", "*://*.medicalxpress.com/*", "*://*.sciencex.com/*", "*://*.techxplore.com/*"] + }, + { + "js": ["plugins/spotify.js"], + "matches": ["*://*.spotify.com/*"] + }, + { + "js": ["plugins/songkick.js"], + "matches": ["*://*.songkick.com/*"] + }, + { + "js": ["plugins/nextdoor.js"], + "matches": ["*://*.nextdoor.com/*"] + }, + { + "js": ["plugins/polona.js"], + "matches": ["*://*.polona.pl/*"] + }, + { + "js": ["plugins/raindrop.js"], + "matches": ["*://*.raindrop.io/*"] + }, + { + "js": ["plugins/stackoverflow.js"], + "matches": ["*://*.stackoverflow.com/*"] + }, + { + "js": ["plugins/uinotes.js"], + "matches": ["*://*.uinotes.com/*"] + }, + { + "js": ["plugins/meiye.js"], + "matches": ["*://*.meiye.art/*"] + }, + { + "js": ["plugins/lummi.js"], + "matches": ["*://*.lummi.ai/*"] + }, + { + "js": ["plugins/brave.js"], + "matches": ["*://*.search.brave.com/*"] } ] } diff --git a/plugins/apple.js b/plugins/apple.js new file mode 100644 index 000000000..4ca603710 --- /dev/null +++ b/plugins/apple.js @@ -0,0 +1,94 @@ +var hoverZoomPlugins = hoverZoomPlugins || []; +hoverZoomPlugins.push( { + name: 'podcasts_apple', + version: '1.0', + prepareImgLinks: function(callback) { + var name = this.name; + + // This plug-in: + // - zoom music, apps & books covers + // - play podcasts + // - do NOT play music tracks + + // sample: https://is1-ssl.mzstatic.com/image/thumb/Music124/v4/20/d5/9f/20d59f6d-89e4-61fe-d3d4-e5680cd5f8b3/5099922840455.jpg/48x48bb.webp + // -> https://is1-ssl.mzstatic.com/image/thumb/Music124/v4/20/d5/9f/20d59f6d-89e4-61fe-d3d4-e5680cd5f8b3/5099922840455.jpg/9999x0w.png + + $('a[href*="/podcast/"]').on('mouseover', function() { + const link = $(this); + let data = link.data(); + + if (data.hoverZoomMouseOver) return; + data.hoverZoomMouseOver = true; + + const source = link.find('source'); + if (source[0] == undefined) return; + var url = source[0].srcset.split(' ')[0]; + url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); + + data.hoverZoomSrc = [url]; + callback(link, name); + + // Cover is displayed iff the cursor is still over the image + if (link.data().hoverZoomMouseOver) + hoverZoom.displayPicFromElement(link, true); + + }).on('mouseleave', function () { + const link = $(this); + link.data().hoverZoomMouseOver = false; + }); + + $('img, div.songs-list-row__song-index, div.artwork-with-badge, div.artwork-with-badge__artwork, div.artwork-wrapper, div.artwork__radiosity, div.ellipse-lockup, div.product-lockup, div.top-search-lockup, div.track-lockup__artwork-wrapper, div.vertical-video, div.we-lockup__overlay, div.we-book-artwork').on('mouseover', function() { + const link = $(this); + let data = link.data(); + + if (data.hoverZoomMouseOver) return; + data.hoverZoomMouseOver = true; + + const source = link.find('source')[0] || link.siblings('source')[0]; + if (source == undefined) return; + var url = source.srcset.replace(',http', ', http').split(' ')[0].replace(/,$/, ''); + url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); + + data.hoverZoomSrc = [url]; + callback(link, name); + + // Cover is displayed iff the cursor is still over the image + if (link.data().hoverZoomMouseOver) + hoverZoom.displayPicFromElement(link, true); + + }).on('mouseleave', function () { + const link = $(this); + link.data().hoverZoomMouseOver = false; + }); + + // deal with shadowRoot + $('amp-lcd').on('mouseover', function() { + if (this.shadowRoot == undefined) return; + + const link = $(this); + let data = link.data(); + + if (data.hoverZoomMouseOver) return; + data.hoverZoomMouseOver = true; + + const img = $(this.shadowRoot).find('img')[0]; + if (img == undefined) return; + const src = img.src; + if (src == undefined) return; + var url = src.replace(',http', ', http').split(' ')[0].replace(/,$/, ''); + url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); + + data.hoverZoomSrc = [url]; + callback(link, name); + + // Cover is displayed iff the cursor is still over the image + if (link.data().hoverZoomMouseOver) + hoverZoom.displayPicFromElement(link, true); + + }).on('mouseleave', function () { + const link = $(this); + link.data().hoverZoomMouseOver = false; + }); + + } +}); diff --git a/plugins/artfol.js b/plugins/artfol.js new file mode 100644 index 000000000..dac9a6e9d --- /dev/null +++ b/plugins/artfol.js @@ -0,0 +1,19 @@ +var hoverZoomPlugins = hoverZoomPlugins || []; +hoverZoomPlugins.push({ + name:'artfol.co', + version:'0.1', + + prepareImgLinks: function (callback) { + const res = []; + + $('a[href*="a/"][title]:not(.hoverZoomMouseover)').each(function() { + let img = $(this); + let src = img[0].innerHTML.match(/\/medium(.*\.jpg)/)[1]; + + img.data().hoverZoomSrc = ['https://www.artfol-image.me' + src]; + res.push(img); + }); + + callback($(res), this.name); + } +}); diff --git a/plugins/brave.js b/plugins/brave.js new file mode 100644 index 000000000..fb6278edf --- /dev/null +++ b/plugins/brave.js @@ -0,0 +1,82 @@ +var hoverZoomPlugins = hoverZoomPlugins || []; +hoverZoomPlugins.push( { + name: 'brave', + version: '1.0', + prepareImgLinks: function(callback) { + const pluginName = this.name; + + // sample: https://imgs.search.brave.com/-X9Un7ROC7nDmrcHTiYUf-WyLXXs36rD7Cy-31tlE2k/rs:fit:500:0:0:0/g:ce/aHR0cHM6Ly9pbWcu/ZnJlZXBpay5jb20v/cHJlbWl1bS1waG90/by9hdXN0cmFsaWFu/LXNoZXBoZXJkLWRv/Z18xMDE1Mzg0LTE2/MDM2NC5qcGc_c2l6/ZT02MjYmZXh0PWpw/Zw + $('img[src]').on('mouseover', function() { + const link = $(this); + if (link.data().hoverZoomMouseOver) return; + link.data().hoverZoomMouseOver = true; + const src = this.src; + + var HZbrave = sessionStorage.getItem('HZbrave'); + const jsObj = hoverZoom.strToJavascriptObj(HZbrave); + + var o = undefined; + try { + o = jsObj?.find(d => d.thumbnail.src == src); + } catch {} + if (o) { + const fullsize = o.thumbnail.original || o.thumbnail.src; + const caption = o.title; + link.data().hoverZoomSrc = [fullsize]; + link.data().hoverZoomCaption = caption; + var res = []; + res.push(link); + callback($(res), pluginName); + // Image is displayed if the cursor is still over the link + if (link.data().hoverZoomMouseOver) + hoverZoom.displayPicFromElement(link); + } else { + chrome.runtime.sendMessage({action:'ajaxRequest', + method:'GET', + url:window.location.href}, + function (response) { + if (response == null) { return; } + + const parser = new DOMParser(); + const doc = parser.parseFromString(response, "text/html"); + + if (doc.scripts == undefined) return; + let scripts = Array.from(doc.scripts); + scripts = scripts.filter(script => /results:\[/.test(script.text)); + if (scripts.length != 1) return; + const data = scripts[0].text; + const index1 = data.indexOf('results:[') + 8; // open [ + const index2 = hoverZoom.matchBracket(data, index1); // close ] + const usefulData = data.substring(index1, index2 + 1); + + // store for reuse + sessionStorage.setItem("HZbrave", usefulData); + + try { + const jsObj = hoverZoom.strToJavascriptObj(usefulData); + var o = undefined; + try { + o = jsObj?.find(d => d.thumbnail.src == src); + } catch {} + if (o == undefined) { return; } + const fullsize = o.thumbnail.original || o.thumbnail.src; + const caption = o.title; + link.data().hoverZoomSrc = [fullsize]; + link.data().hoverZoomCaption = caption; + var res = []; + res.push(link); + callback($(res), pluginName); + // Image is displayed if the cursor is still over the link + if (link.data().hoverZoomMouseOver) + hoverZoom.displayPicFromElement(link); + + } catch {} + }); + } + }).on('mouseleave', function() { + const link = $(this); + link.data().hoverZoomMouseOver = false; + }); + + } +}); diff --git a/plugins/cineserie.js b/plugins/cineserie.js new file mode 100644 index 000000000..678c261cd --- /dev/null +++ b/plugins/cineserie.js @@ -0,0 +1,42 @@ +var hoverZoomPlugins = hoverZoomPlugins || []; +hoverZoomPlugins.push({ + name:'cineserie', + version:'0.1', + prepareImgLinks:function (callback) { + var res = []; + + // sample: https://imgr.cineserie.com/2016/05/143726.jpg?imgeng=/f_jpg/cmpr_0/w_225/h_337/m_cropbox&ver=1 + // -> https://imgr.cineserie.com/2016/05/143726.jpg + + function findFullsizeUrl(link, src) { + let fullsizeUrl = src.replace(/(png|jpe?g)\?.*/, '$1'); + if (fullsizeUrl == src) return; + + if (link.data().hoverZoomSrc == undefined) { link.data().hoverZoomSrc = [] } + if (link.data().hoverZoomSrc.indexOf(fullsizeUrl) == -1) { + link.data().hoverZoomSrc.unshift(fullsizeUrl); + res.push(link); + } + } + + $('img[src*="png?"], img[src*="jpg?"], img[src*="jpeg?"]').each(function() { + findFullsizeUrl($(this), this.src); + }); + + $('[style*="png?"], [style*="jpg?"], [style*="jpeg?"]').each(function() { + // extract url from style + var backgroundImage = this.style.backgroundImage; + if (backgroundImage && /png|jpe?g/.test(backgroundImage)) { + const reUrl = /.*url\s*\(\s*(.*)\s*\).*/i + backgroundImage = backgroundImage.replace(reUrl, '$1'); + // remove leading & trailing quotes + var backgroundImageUrl = backgroundImage.replace(/^['"]/, '').replace(/['"]+$/, ''); + findFullsizeUrl($(this), backgroundImageUrl); + } + }); + + if (res.length) { + callback($(res), this.name); + } + } +}); diff --git a/plugins/cloudflare_a.js b/plugins/cloudflare_a.js index 41a9bd063..c1235abe1 100644 --- a/plugins/cloudflare_a.js +++ b/plugins/cloudflare_a.js @@ -1,24 +1,26 @@ var hoverZoomPlugins = hoverZoomPlugins || []; hoverZoomPlugins.push({ name:'cloudflare_a', - version:'0.2', + version:'0.3', prepareImgLinks:function (callback) { var res = []; // sample: https://prod.cdn-medias.jeuneafrique.com/cdn-cgi/image/q=100,f=auto,metadata=none,width=640,height=320/https://prod.cdn-medias.jeuneafrique.com/medias/2023/05/23/jad20230523-ass-tchad-idriss-deby-le-sud-1256x628.jpg // -> https://prod.cdn-medias.jeuneafrique.com/medias/2023/05/23/jad20230523-ass-tchad-idriss-deby-le-sud-1256x628.jpg - const reThumb = /^.*\/cdn-cgi\/image\/.*\/(http.*)/; - const reReplace = '$1'; + // sample: https://cdn.wamiz.fr/cdn-cgi/image/format=auto,quality=80,width=200,height=200,fit=cover/adoption/pet/picture/6624916d91181835733923.jpg + // -> https://cdn.wamiz.fr/adoption/pet/picture/6624916d91181835733923.jpg - function findFullsizeUrl(link, src) { - let fullsizeUrl = src.replace(reThumb, reReplace); - if (fullsizeUrl == src) return; + const reFind1 = /^.*\/cdn-cgi\/image\/.*\/(http.*)/; + const reFind2 = /(^.*)\/cdn-cgi\/image\/[^\/]{1,}\/(?!http)(.*)/; + const reReplace1 = '$1'; + const reReplace2 = '$1/$2'; - // decode ASCII characters, for instance: '%2C' -> ',' - // NB: this operation must be try/catched because url might not be well-formed - try { - fullsizeUrl = decodeURIComponent(fullsizeUrl); - } catch {} + function findFullsizeUrl(link, src) { + let fullsizeUrl = src.replace(reFind1, reReplace1); + if (fullsizeUrl == src) { + fullsizeUrl = src.replace(reFind2, reReplace2); + if (fullsizeUrl == src) return; + } if (link.data().hoverZoomSrc == undefined) { link.data().hoverZoomSrc = [] } if (link.data().hoverZoomSrc.indexOf(fullsizeUrl) == -1) { diff --git a/plugins/default.js b/plugins/default.js index 46bc87641..4d7a58fa2 100644 --- a/plugins/default.js +++ b/plugins/default.js @@ -1,13 +1,14 @@ var hoverZoomPlugins = hoverZoomPlugins || []; hoverZoomPlugins.push({ name: 'Default', - version:'0.6', + version:'0.7', prepareImgLinks: function (callback) { const res = []; const reVideos = /\/[^:]+\.(?:3gpp|m4v|mkv|mp4|ogv|webm)(?:[\?#].*)?(?:\/)?$/i const reImages = /\/[^:]+\.(?:avif|bmp|gifv?|ico|jfif|jpe|jpe?g|png|svg|webp|xbm)(?:[\?#].*)?(?:\/)?$/i const rePlaylists = /\/[^:]+\.(?:m3u8)(?:[\?#].*)?(?:\/)?$/i const reAudios = /\/[^:]+\.(?:flac|m4a|mp3|oga|ogg|opus|wav)(?:[\?#].*)?(?:\/)?$/i + $('a[href]').filter(function () { if (typeof(this.href) != 'string') return false; @@ -32,6 +33,98 @@ hoverZoomPlugins.push({ } } }); + + // handle elements + $('video[src]').filter(function () { + $(this).data().hoverZoomSrc = []; + if (!options.zoomVideos) + return false; + if (typeof(this.src) != 'string') + return false; + if (this.src.match(/^blob:/)) + return false; + return true; + }).each(function () { + var _this = $(this), data = _this.data(); + // discard video already being played + if (this.paused || this.controls === false) { + var src = this.src; + if (!src.match(reVideos)) + src += '.video'; + data.hoverZoomSrc = [src]; + res.push(_this); + } + }); + + // handle
-
-
  • - -
  • -
  • @@ -448,91 +440,6 @@

  • -
  • - -
  • -
  • - -
  • -
    -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
    -
    -
  • - -
  • -
    -
    -
  • - -
  • -
  • diff --git a/js/background.js b/js/background.js index 6b213749e..487583e51 100644 --- a/js/background.js +++ b/js/background.js @@ -8,12 +8,12 @@ function cLog(msg) { // Performs an ajax request function ajaxRequest(request, callback) { - const response = request.response; - const method = request.method; - const filename = request.filename; - const conflictAction = request.conflictAction; - let xhr = new XMLHttpRequest(); - let url = request.url; + var xhr = new XMLHttpRequest(); + var response = request.response; + var method = request.method; + var url = request.url; + var filename = request.filename; + var conflictAction = request.conflictAction; if (response === 'DOWNLOAD') xhr.responseType = 'arraybuffer'; @@ -43,7 +43,7 @@ function ajaxRequest(request, callback) { } xhr.open(request.method, request.url, true); - for (let i in request.headers) { + for (var i in request.headers) { xhr.setRequestHeader(request.headers[i].header, request.headers[i].value); } xhr.send(request.data); @@ -80,52 +80,25 @@ function downloadFile(url, filename, conflictAction, callback) { function onMessage(message, sender, callback) { switch (message.action) { case 'downloadFileBlob': - /** - * direct URL download through Chrome API might be prohibited (e.g: Pixiv) - * workaround: - * 1. obtain ArrayBuffer from XHR request (GET URL) - * 2. create Blob from ArrayBuffer - * 3. download Blob URL through Chrome API - */ - - /* - * Workaround for permissions.request not returning a promise in Firefox - * First checks if permissions are availble. If true, downloads file. If not, requests them. - * Not as clean or effecient as just using 'permissions.request'. - */ + // direct URL download through Chrome API might be prohibited (e.g: Pixiv) + // workaround: + // 1. obtain ArrayBuffer from XHR request (GET URL) + // 2. create Blob from ArrayBuffer + // 3. download Blob URL through Chrome API cLog('downloadFileBlob: ' + message); - chrome.permissions.contains({permissions: ['downloads']}, (contained) => { - cLog('downloadFile contains: ' + contained); - if (contained) { + chrome.permissions.request({permissions: ['downloads']}, function (granted) { + cLog('downloadFile granted: ' + granted); + if (granted) { ajaxRequest({method:'GET', response:'DOWNLOAD', url:message.url, filename:message.filename, conflictAction:message.conflictAction, headers:message.headers}, callback); - } else { - chrome.permissions.request({permissions: ['downloads']}, (granted) => { - cLog('downloadFile granted: ' + granted); - if (granted) { - ajaxRequest({method:'GET', response:'DOWNLOAD', url:message.url, filename:message.filename, conflictAction:message.conflictAction, headers:message.headers}, callback); - } - }) } }); return true; case 'downloadFile': cLog('downloadFile: ' + message); - /* - * Workaround for permissions.request not returning a promise in Firefox - * First checks if permissions are availble. If true, downloads file. If not, requests them. - * Not as clean or effecient as just using 'permissions.request'. - */ - chrome.permissions.contains({permissions: ['downloads']}, (contained) => { - cLog('downloadFile contains: ' + contained); - if (contained) { + chrome.permissions.request({permissions: ['downloads']}, function (granted) { + cLog('downloadFile granted: ' + granted); + if (granted) { downloadFile(message.url, message.filename, message.conflictAction, callback); - } else { - chrome.permissions.request({permissions: ['downloads']}, (granted) => { - cLog('downloadFile granted: ' + granted); - if (granted) { - downloadFile(message.url, message.filename, message.conflictAction, callback); - } - }) } }); return true; @@ -178,7 +151,7 @@ function onMessage(message, sender, callback) { localStorage.removeItem(message.id); break; case 'openViewWindow': - let url = message.createData.url; + var url = message.createData.url; if (url.indexOf('facebook.com/photo/download') !== -1) { message.createData.url = 'data:text/html,'; } @@ -191,7 +164,7 @@ function onMessage(message, sender, callback) { message.createData.index = tabs[0].index; if (!message.createData.active) message.createData.index++; - let url = message.createData.url; + var url = message.createData.url; if (url.indexOf('facebook.com/photo/download') !== -1) { message.createData.url = 'data:text/html,'; } @@ -274,19 +247,15 @@ chrome.runtime.onInstalled.addListener((details) => { } }) -/** -* - store request's header(s) setting(s) = modification(s) to be applied to request's header(s) just before sending request to server -* e.g: add/modify "Origin" header to deal with CORS limitations -* - store response's header(s) setting(s) = modification(s) to be applied to response's header(s) just after receiving response from server -* e.g: add/modify "Access-Control-Allow-Origin" header so browser allows content display -*/ +// - store request's header(s) setting(s) = modification(s) to be applied to request's header(s) just before sending request to server +// e.g: add/modify "Origin" header to deal with CORS limitations +// - store response's header(s) setting(s) = modification(s) to be applied to response's header(s) just after receiving response from server +// e.g: add/modify "Access-Control-Allow-Origin" header so browser allows content display function storeHeaderSettings(message) { - /** - * check that: - * - header(s) rewrite is allowed - * and - * - permissions are granted - */ + // check that: + // - header(s) rewrite is allowed + // and + // - permissions are granted if (!options.allowHeadersRewrite) { return; } @@ -342,12 +311,10 @@ function findHeaderSettings(url, requestOrResponse) { // update header(s) according to plug-in settings function updateHeaders(headers, settings) { settings.headers.forEach(h => { - /** - * types of update: - * - 'remove': remove header - * - 'replace': replace header, if header does not exist then do nothing - * - 'add': add header, if header already exists then replace it - */ + // types of update: + // - 'remove': remove header + // - 'replace': replace header, if header does not exist then do nothing + // - 'add': add header, if header already exists then replace it if (h.typeOfUpdate === 'remove') { let index = headers.findIndex(rh => rh.name.toLowerCase() === h.name.toLowerCase()); if (index !== -1) headers.splice(index, 1); @@ -365,43 +332,26 @@ function updateHeaders(headers, settings) { return headers; } -/** -* add listeners for web requests: -* - onBeforeSendHeaders -* - onHeadersReceived -* so they can be edited on-the-fly to enable API calls from plug-ins -* https://developer.chrome.com/docs/extensions/reference/webRequest/ -* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest -*/ - +// add listeners for web requests: +// - onBeforeSendHeaders +// - onHeadersReceived +// so they can be edited on-the-fly to enable API calls from plug-ins +// https://developer.chrome.com/docs/extensions/reference/webRequest/ function addWebRequestListeners() { - if (!chrome.webRequest.onBeforeSendHeaders.hasListener(updateRequestHeaders)) { - chrome.webRequest.onBeforeSendHeaders.addListener(updateRequestHeaders, { urls : [""] }, [ - 'blocking', - 'requestHeaders', - chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS, - ].filter(Boolean)); - } - if (!chrome.webRequest.onHeadersReceived.hasListener(updateResponseHeaders)){ - chrome.webRequest.onHeadersReceived.addListener(updateResponseHeaders, { urls : [""] }, [ - 'blocking', - 'responseHeaders', - chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS, - ].filter(Boolean)); - } - + if (!chrome.webRequest.onBeforeSendHeaders.hasListeners()) + chrome.webRequest.onBeforeSendHeaders.addListener(updateRequestHeaders, { urls : [""] }, ["blocking", "requestHeaders", "extraHeaders"]); + if (!chrome.webRequest.onHeadersReceived.hasListeners()) + chrome.webRequest.onHeadersReceived.addListener(updateResponseHeaders, { urls : [""] }, ["blocking", "responseHeaders", "extraHeaders"]); } -/** -* remove listeners for web requests: -* - onBeforeSendHeaders -* - onHeadersReceived -* also remove headers settings since they are not used anymore -*/ +// remove listeners for web requests: +// - onBeforeSendHeaders +// - onHeadersReceived +// also remove headers settings since they are not used anymore function removeWebRequestListeners() { - if (chrome.webRequest.onBeforeSendHeaders.hasListener(updateRequestHeaders)) + if (chrome.webRequest.onBeforeSendHeaders.hasListeners()) chrome.webRequest.onBeforeSendHeaders.removeListener(updateRequestHeaders); - if (chrome.webRequest.onHeadersReceived.hasListener(updateResponseHeaders)) + if (chrome.webRequest.onHeadersReceived.hasListeners()) chrome.webRequest.onHeadersReceived.removeListener(updateResponseHeaders); sessionStorage.removeItem('HoverZoomHeaderSettings'); diff --git a/js/common.js b/js/common.js index 67f4cae74..ec4ea2b8b 100644 --- a/js/common.js +++ b/js/common.js @@ -36,13 +36,8 @@ var factorySettings = { ambilightBackgroundOpacity : 0.9, disabledPlugins : [], centerImages : false, - autoLockImages : false, frameBackgroundColor: "#ffffff", frameThickness: 4, - belowPositionOffset: 0, - abovePositionOffset: 0, - captionOpacity: 1, - detailsOpacity: 1, displayImageLoader: false, enlargementThresholdEnabled : true, enlargementThreshold : 2, @@ -54,23 +49,10 @@ var factorySettings = { addDownloadOrigin : false, addDownloadSize : false, addDownloadDuration : false, - addDownloadIndex : false, - addDownloadCaption : false, - replaceOriginalFilename : false, - downloadFilename : '', useSeparateTabOrWindowForUnloadableUrlsEnabled: false, useSeparateTabOrWindowForUnloadableUrls: 'window', captionLocation : 'below', detailsLocation : 'none', - showDetailFilename : true, - showDetailHost : true, - showDetailLastModified : true, - showDetailExtension : true, - showDetailContentLength : true, - showDetailDuration : true, - showDetailScale : true, - showDetailRatio : true, - showDetailDimensions : true, fontSize : 11, fontOutline : false, actionKey : 0, @@ -144,7 +126,6 @@ function loadOptions() { options.ambilightBackgroundOpacity = options.hasOwnProperty('ambilightBackgroundOpacity') ? options.ambilightBackgroundOpacity : factorySettings.ambilightBackgroundOpacity; options.disabledPlugins = options.hasOwnProperty('disabledPlugins') ? options.disabledPlugins : factorySettings.disabledPlugins; options.centerImages = options.hasOwnProperty('centerImages') ? options.centerImages : factorySettings.centerImages; - options.autoLockImages = options.hasOwnProperty('autoLockImages') ? options.autoLockImages : factorySettings.autoLockImages; options.frameBackgroundColor = options.hasOwnProperty('frameBackgroundColor') ? options.frameBackgroundColor : factorySettings.frameBackgroundColor; options.frameThickness = options.hasOwnProperty('frameThickness') ? options.frameThickness : factorySettings.frameThickness; options.displayImageLoader = options.hasOwnProperty('displayImageLoader') ? options.displayImageLoader : factorySettings.displayImageLoader; @@ -158,24 +139,9 @@ function loadOptions() { options.addDownloadOrigin = options.hasOwnProperty('addDownloadOrigin') ? options.addDownloadOrigin : factorySettings.addDownloadOrigin; options.addDownloadSize = options.hasOwnProperty('addDownloadSize') ? options.addDownloadSize : factorySettings.addDownloadSize; options.addDownloadDuration = options.hasOwnProperty('addDownloadDuration') ? options.addDownloadDuration : factorySettings.addDownloadDuration; - options.addDownloadIndex = options.hasOwnProperty('addDownloadIndex') ? options.addDownloadIndex : factorySettings.addDownloadIndex; - options.addDownloadCaption = options.hasOwnProperty('addDownloadCaption') ? options.addDownloadCaption : factorySettings.addDownloadCaption; - options.replaceOriginalFilename = options.hasOwnProperty('replaceOriginalFilename') ? options.replaceOriginalFilename : factorySettings.replaceOriginalFilename; - options.downloadFilename = options.hasOwnProperty('downloadFilename') ? options.downloadFilename : factorySettings.downloadFilename; options.useSeparateTabOrWindowForUnloadableUrlsEnabled = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrlsEnabled') ? options.useSeparateTabOrWindowForUnloadableUrlsEnabled : factorySettings.useSeparateTabOrWindowForUnloadableUrlsEnabled; options.useSeparateTabOrWindowForUnloadableUrls = options.hasOwnProperty('useSeparateTabOrWindowForUnloadableUrls') ? options.useSeparateTabOrWindowForUnloadableUrls : factorySettings.useSeparateTabOrWindowForUnloadableUrls; - // Show details options - options.showDetailFilename = options.hasOwnProperty('showDetailFilename') ? options.showDetailFilename : factorySettings.showDetailFilename; - options.showDetailHost = options.hasOwnProperty('showDetailHost') ? options.showDetailHost : factorySettings.showDetailHost; - options.showDetailLastModified = options.hasOwnProperty('showDetailLastModified') ? options.showDetailLastModified : factorySettings.showDetailLastModified; - options.showDetailExtension = options.hasOwnProperty('showDetailExtension') ? options.showDetailExtension : factorySettings.showDetailExtension; - options.showDetailContentLength = options.hasOwnProperty('showDetailContentLength') ? options.showDetailContentLength : factorySettings.showDetailContentLength; - options.showDetailDuration = options.hasOwnProperty('showDetailDuration') ? options.showDetailDuration : factorySettings.showDetailDuration; - options.showDetailScale = options.hasOwnProperty('showDetailScale') ? options.showDetailScale : factorySettings.showDetailScale; - options.showDetailRatio = options.hasOwnProperty('showDetailRatio') ? options.showDetailRatio : factorySettings.showDetailRatio; - options.showDetailDimensions = options.hasOwnProperty('showDetailDimensions') ? options.showDetailDimensions : factorySettings.showDetailDimensions; - // Used old showCaptions option for backwards compatibility var showCaptions = options.hasOwnProperty('showCaptions') ? options.showCaptions : true; options.captionLocation = options.hasOwnProperty('captionLocation') ? options.captionLocation : (showCaptions ? factorySettings.captionLocation : 'none'); @@ -183,10 +149,6 @@ function loadOptions() { options.detailsLocation = options.hasOwnProperty('detailsLocation') ? options.detailsLocation : factorySettings.detailsLocation; options.fontSize = options.hasOwnProperty('fontSize') ? options.fontSize : factorySettings.fontSize; options.fontOutline = options.hasOwnProperty('fontOutline') ? options.fontOutline : factorySettings.fontOutline; - options.belowPositionOffset = options.hasOwnProperty('belowPositionOffset') ? options.belowPositionOffset : factorySettings.belowPositionOffset; - options.abovePositionOffset = options.hasOwnProperty('abovePositionOffset') ? options.abovePositionOffset : factorySettings.abovePositionOffset; - options.captionOpacity = options.hasOwnProperty('captionOpacity') ? options.captionOpacity : factorySettings.captionOpacity; - options.detailsOpacity = options.hasOwnProperty('detailsOpacity') ? options.detailsOpacity : factorySettings.detailsOpacity; // Action keys options.actionKey = options.hasOwnProperty('actionKey') ? options.actionKey : factorySettings.actionKey; diff --git a/js/hoverzoom.js b/js/hoverzoom.js index 09f086e27..48f5a6441 100644 --- a/js/hoverzoom.js +++ b/js/hoverzoom.js @@ -125,6 +125,7 @@ var hoverZoom = { arrowUpKeyDown = false, arrowDownKeyDown = false, viewerLocked = false, + lockViewerClickTime = 0, zoomFactor = 1, zoomSpeedFactor = 1, pageActionShown = false, @@ -284,14 +285,14 @@ var hoverZoom = { 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'align-items':'flex-end', + 'align-items':'flex-end' }, hzBelowCss = { 'background':'none', 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'align-items':'flex-start', + 'align-items':'flex-start' }, hzCaptionMiscellaneousCss = { 'background':'none', @@ -305,7 +306,7 @@ var hoverZoom = { 'display':'flex', 'flex-direction':'row', 'flex-wrap':'nowrap', - 'min-width':'25%', + 'min-width':'25%' }, hzGalleryInfoCss = { 'position':'absolute', @@ -411,26 +412,14 @@ var hoverZoom = { imgFullSize.height(wndHeight - padding - statusBarHeight - scrollBarHeight - (hzAbove ? hzAbove.height() : 0) - (hzBelow ? hzBelow.height() : 0)).width('auto'); } - if (hzCaptionMiscellaneous) { + if (hzCaptionMiscellaneous) hzCaptionMiscellaneous.css('max-width', imgFullSize[0].clientWidth); - hzCaptionMiscellaneous.css('opacity', options.captionOpacity); - } - if (hzDetails) { + if (hzDetails) hzDetails.css('max-width', imgFullSize[0].clientWidth); - hzDetails.css('opacity', options.detailsOpacity); - } - if (hzAbove) { + if (hzAbove) hzAbove.css('max-width', imgFullSize[0].clientWidth); - hzAbove.css('top', options.abovePositionOffset + '%'); - if (options.abovePositionOffset != 0) - hzAbove.css('position', 'absolute'); - } - if (hzBelow) { + if (hzBelow) hzBelow.css('max-width', imgFullSize[0].clientWidth); - hzBelow.css('bottom', options.belowPositionOffset + '%'); - if (options.belowPositionOffset != 0) - hzBelow.css('position', 'absolute'); - } // do not display caption nor details if img is too small if (imgFullSize[0].clientWidth < 50) { @@ -496,13 +485,9 @@ var hoverZoom = { } // width adjustment - const fullZoom = options.mouseUnderlap || viewerLocked; - const fullZoomKey = fullZoomKeyDown; + var fullZoom = options.mouseUnderlap || fullZoomKeyDown || viewerLocked; if (viewerLocked) { imgFullSize.width(srcDetails.naturalWidth * zoomFactor); - } else if (fullZoomKey) { - // naturalWidth replaced with wndWidth to make image fill window - imgFullSize.width(Math.min(wndWidth, wndWidth - padding - 2 * scrollBarWidth)); } else if (fullZoom) { imgFullSize.width(Math.min(srcDetails.naturalWidth * zoomFactor, wndWidth - padding - 2 * scrollBarWidth)); } else if (displayOnRight) { @@ -633,10 +618,6 @@ var hoverZoom = { } function isAudioLink(url) { - if (url.indexOf('.audio') !== -1) { - return true; - } - if (url.lastIndexOf('?') > 0) url = url.substring(0, url.lastIndexOf('?')); const ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase(); @@ -1067,183 +1048,26 @@ var hoverZoom = { } } - let longRightPressTimer; // create timer - let longMiddlePressTimer; // creates separate timer so they don't interfere - let longRightPress = false; - - function mouseButtonKeyHandler(mouseButtonKey, img) { - const timerDelay = 150; - if (mouseButtonKey === -1) { - longRightPressTimer = setTimeout(longClick.bind(img), timerDelay, mouseButtonKey); - } else { - longMiddlePressTimer = setTimeout(longClick.bind(img), timerDelay, mouseButtonKey); - } - } - - function clearMouseButtonTimers(mouseButtonKey) { - if (mouseButtonKey === -1) { - clearTimeout(longRightPressTimer); - } else { - clearTimeout(longMiddlePressTimer); - } - } - - function longClick(mouseButtonKey) { - if (mouseButtonKey == -1) longRightPress = true; - switch (mouseButtonKey) { - case options.actionKey: - actionKeyDown = true; - $(this).mousemove(); - if (loading || imgFullSize) { - return false; - } - break; - case options.lockImageKey: - lockViewer(); - return; - case options.toggleKey: - toggleKey() - return; - case options.fullZoomKey: - if (!fullZoomKeyDown) { - fullZoomKeyDown = true; - posViewer(); - if (imgFullSize) { - return false; - } - } - return; - case options.closeKey: - closeKey() - return; - case options.hideKey: - if (!hideKeyDown) { - hideKey() - } - return; - case options.copyImageKey: - if (isChromiumBased) { - if (keyCode === options.copyImageKey) { - copyImage(); - return false; - } - } - return false; - case options.copyImageUrlKey: - copyLink(); - return false; - // "Previous image" key - case options.prevImgKey: - var linkData = hz.currentLink.data(); - if (linkData.hoverZoomGallerySrc && linkData.hoverZoomGallerySrc.length > 1) rotateGalleryImg(-1); - else changeVideoPosition(-parseInt(options.videoPositionStep)); - return false; - // "Next image" key - case options.nextImgKey: - var linkData = hz.currentLink.data(); - if (linkData.hoverZoomGallerySrc && linkData.hoverZoomGallerySrc.length > 1) rotateGalleryImg(1); - else changeVideoPosition(parseInt(options.videoPositionStep)); - return false; - // "Flip image" key - case options.flipImageKey: - flipImage(); - return false; - case options.openImageInWindowKey: - if (srcDetails.video) openVideoInWindow(); - else if (srcDetails.audio) openAudioInWindow(); - else openImageInWindow(); - return false; - case options.openImageInTabKey: - if (srcDetails.video) openVideoInTab(event.shiftKey); - else if (srcDetails.audio) openAudioInTab(); - else openImageInTab(event.shiftKey); - return false; - case options.saveImageKey: - saveImage(); - return false; - default: - return; - } - } - function documentContextMenu(event) { - // If right click is a long press, prevent context menu - if (longRightPress) { - longRightPress = false; + // If it's been less than 300ms since right click, lock viewer and prevent context menu. + var lockElapsed = event.timeStamp - lockViewerClickTime; + if (imgFullSize && !viewerLocked && options.lockImageKey === -1 && lockElapsed < 300) { + lockViewer(); event.preventDefault(); } } function documentMouseDown(event) { - // if image is locked and left click is pressed outside of locked image - if (event.button === 0 && imgFullSize && event.target !== hz.hzViewer[0] && event.target !== imgFullSize[0]) { - if (viewerLocked) { + // Right click pressed and lockImageKey is set to special value for right click (-1). + if (imgFullSize && !viewerLocked && options.lockImageKey === -1 && event.button === 2) { + lockViewerClickTime = event.timeStamp; + } else if (imgFullSize && event.target !== hz.hzViewer[0] && event.target !== imgFullSize[0]) { + if (viewerLocked && event.button === 0) { viewerLocked = false; } cancelSourceLoading(); restoreTitles(); - return; - } else if (event.button === 0) { // We don't need left click - return; - } - - // Gets mouse button key from event.button - const mouseButtonKey = [null,-2,-1,null,null][event.button]; // -2 is middle click, -1 is right click - switch (mouseButtonKey) { - case options.actionKey: - case options.toggleKey: - case options.fullZoomKey: - case options.closeKey: - case options.hideKey: - mouseButtonKeyHandler(mouseButtonKey, this); - return; - default: - // The following only trigger when image is displayed - if (imgFullSize) { - switch (mouseButtonKey) { - case options.lockImageKey: - case options.copyImageKey: - case options.copyImageUrlKey: - case options.flipImageKey: - case options.openImageInWindowKey: - case options.openImageInTabKey: - case options.saveImageKey: - mouseButtonKeyHandler(mouseButtonKey); - return; - default: - break; - } - } - return; - } - } - - function documentMouseUp(event) { - if (event.button === 0) return; // If left click, return - const mouseButtonKey = [null,-2,-1,null,null][event.button]; // -2 is middle click, -1 is right click - switch (mouseButtonKey) { - case options.actionKey: - if (actionKeyDown) { - actionKeyDown = false; - closeHoverZoomViewer(); - } - break; - case options.fullZoomKey: - fullZoomKeyDown = false; - $(this).mousemove(); - break; - case options.hideKey: - hideKeyDown = false; - if (imgFullSize) { - hz.hzViewer.show(); - playMedias(); - } - $(this).mousemove(); - break; - default: - break; } - clearMouseButtonTimers(mouseButtonKey); } // select correct font size for msg depending on img or video width @@ -1360,7 +1184,7 @@ var hoverZoom = { return; } - var src = (srcDetails.audioUrl ? srcDetails.audioUrl : srcDetails.url).replace('.audio', ''); + var src = (srcDetails.audioUrl ? srcDetails.audioUrl : srcDetails.url); // audio controls are displayed on top of an image provided by extension: 'images/spectrogram.png' srcDetails.url = chrome.extension.getURL('images/spectrogram.png'); @@ -1372,7 +1196,7 @@ var hoverZoom = { audio.controls = true; // controls always visible even if not locked audio.autoplay = true; audio.volume = options.audioVolume; - audio.src = srcDetails.audioUrl; + audio.src = src; audioControls = $(audio).appendTo(hz.hzViewer); audio.addEventListener('error', srcFullSizeOnError); @@ -1759,11 +1583,6 @@ var hoverZoom = { function displayFullSizeImage() { cLog('displayFullSizeImage'); - - // if autoLockImages option is checked - if (options.autoLockImages) - viewerLocked = true; - // check focus let focus = document.hasFocus(); @@ -1967,60 +1786,51 @@ var hoverZoom = { if (options.detailsLocation === "below") if (hzBelow.find('#hzDetails').length == 0) hzDetails = $('
    ', {id:'hzDetails'}).css(hzDetailsCss).appendTo(hzBelow); - if (options.showDetailFilename) { - if (hzDetails.find('#hzDetailFilename').length == 0) - $('
    ', {id:'hzDetailFilename', text:details.filename, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailFilename').text(details.filename); - } - if (options.showDetailHost) { - if (hzDetails.find('#hzDetailHost').length == 0) - $('
    ', {id:'hzDetailHost', text:details.host, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailHost').text(details.host); - } - if (options.showDetailLastModified) { - if (hzDetails.find('#hzDetailLastModified').length == 0) - $('
    ', {id:'hzDetailLastModified', text:details.lastModified, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailLastModified').text(details.lastModified); - } - if (options.showDetailExtension) { - if (hzDetails.find('#hzDetailExtension').length == 0) - $('
    ', {id:'hzDetailExtension', text:details.extension.toUpperCase(), class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailExtension').text(details.extension.toUpperCase()); - } - if (options.showDetailContentLength) { - if (hzDetails.find('#hzDetailContentLength').length == 0) - $('
    ', {id:'hzDetailContentLength', text:details.contentLength, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailContentLength').text(details.contentLength); - } - if (options.showDetailDuration) { - if (hzDetails.find('#hzDetailDuration').length == 0) - $('
    ', {id:'hzDetailDuration', text:details.duration, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailDuration').text(details.duration); - } - if (options.showDetailScale) { - if (hzDetails.find('#hzDetailScale').length == 0) - $('
    ', {id:'hzDetailScale', text:details.scale, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailScale').text(details.scale); - } - if (options.showDetailRatio) { - if (hzDetails.find('#hzDetailRatio').length == 0) - $('
    ', {id:'hzDetailRatio', text:details.ratio, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailRatio').text(details.ratio); - } - if (options.showDetailDimensions) { - if (hzDetails.find('#hzDetailDimensions').length == 0) - $('
    ', {id:'hzDetailDimensions', text:details.dimensions, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); - else - $('#hzDetailDimensions').text(details.dimensions); - } + + if (hzDetails.find('#hzDetailFilename').length == 0) + $('
    ', {id:'hzDetailFilename', text:details.filename, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailFilename').text(details.filename); + + if (hzDetails.find('#hzDetailHost').length == 0) + $('
    ', {id:'hzDetailHost', text:details.host, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailHost').text(details.host); + + if (hzDetails.find('#hzDetailLastModified').length == 0) + $('
    ', {id:'hzDetailLastModified', text:details.lastModified, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailLastModified').text(details.lastModified); + + if (hzDetails.find('#hzDetailExtension').length == 0) + $('
    ', {id:'hzDetailExtension', text:details.extension.toUpperCase(), class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailExtension').text(details.extension.toUpperCase()); + + if (hzDetails.find('#hzDetailContentLength').length == 0) + $('
    ', {id:'hzDetailContentLength', text:details.contentLength, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailContentLength').text(details.contentLength); + + if (hzDetails.find('#hzDetailDuration').length == 0) + $('
    ', {id:'hzDetailDuration', text:details.duration, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailDuration').text(details.duration); + + if (hzDetails.find('#hzDetailScale').length == 0) + $('
    ', {id:'hzDetailScale', text:details.scale, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailScale').text(details.scale); + + if (hzDetails.find('#hzDetailRatio').length == 0) + $('
    ', {id:'hzDetailRatio', text:details.ratio, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailRatio').text(details.ratio); + + if (hzDetails.find('#hzDetailDimensions').length == 0) + $('
    ', {id:'hzDetailDimensions', text:details.dimensions, class:'hzDetail'}).css(hzDetailCss).prependTo(hzDetails); + else + $('#hzDetailDimensions').text(details.dimensions); } } @@ -2136,7 +1946,7 @@ var hoverZoom = { // if video comes with distinct url for audio then extension = video's extension details.extension = getExtensionFromUrl(srcDetails.audio && !srcDetails.video ? srcDetails.audioUrl : srcDetails.url, srcDetails.video, srcDetails.playlist, srcDetails.audio); details.host = srcDetails.host; - let filename = getFilename(); + let filename = getDownloadFilename(); if (filename) details.filename = filename; let duration = (srcDetails.audio && !srcDetails.video ? getDurationFromAudio() : getDurationFromVideo()); if (duration) details.duration = duration.replace(/ /g, ':'); @@ -2218,7 +2028,7 @@ var hoverZoom = { // Skip if the image has the same URL as the thumbnail. if (linkData.hoverZoomSrc && linkData.hoverZoomSrc.length) { var url = linkData.hoverZoomSrc[0], - skip = (link.is('img') && url === link.attr('src')); + skip = url === link.attr('src'); if (!skip) { link.find('img[src]').each(function () { if (this.src === url) { @@ -2536,7 +2346,7 @@ var hoverZoom = { // for instance, on TripAdvisor: // img's src placeholder is replaced by real img url stored in data-lazyurl as user scrolls down $(document).on('scroll mousewheel', function() { - let scrollTop = window.scrollY || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" + let scrollTop = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426" if (scrollTop < lastScrollTop) { lastScrollTop = scrollTop < 0 ? 0 : scrollTop; // For Mobile or negative scrolling } else if (scrollTop > lastScrollTop + deltaMin) { @@ -2551,7 +2361,6 @@ var hoverZoom = { $(document).contextmenu(documentContextMenu); $(document).mousemove(documentMouseMove).mousedown(documentMouseDown).mouseleave(cancelSourceLoading); - $(document).on('mouseup', function(event) { documentMouseUp(event); }) $(document).keydown(documentOnKeyDown).keyup(documentOnKeyUp); if (options.galleriesMouseWheel) { window.addEventListener('wheel', documentOnMouseWheel, {passive: false}); @@ -2619,43 +2428,6 @@ var hoverZoom = { } } - function toggleKey() { - options.extensionEnabled = !options.extensionEnabled; - if (!options.extensionEnabled) { - // close zoomed image or video - viewerLocked = false; - if (hz.hzViewer) { - stopMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } - } - } - - function closeKey() { - viewerLocked = false; - if (hz.hzViewer) { - stopMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } - } - - function hideKey(){ - hideKeyDown = true; - if (hz.hzViewer) { - pauseMedias(); - hz.hzViewer.hide(); - } - if (imgFullSize) { - return false; - } - } - function documentOnKeyDown(event) { // Skips if an input controlled is focused if (event.target && ['INPUT','TEXTAREA','SELECT'].indexOf(event.target.tagName) > -1) { @@ -2666,7 +2438,18 @@ var hoverZoom = { // Toggle key is pressed down if (keyCode === options.toggleKey) { - toggleKey(); + options.extensionEnabled = !options.extensionEnabled; + if (!options.extensionEnabled) { + // close zoomed image or video + viewerLocked = false; + if (hz.hzViewer) { + stopMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } + } } // Action key (zoom image) is pressed down @@ -2690,13 +2473,27 @@ var hoverZoom = { // close key (close zoomed image) is pressed down // => zoomed image is closed immediately if (keyCode === options.closeKey) { - closeKey(); + viewerLocked = false; + if (hz.hzViewer) { + stopMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } } // hide key (hide zoomed image) is pressed down // => zoomed image remains hidden until key is released if (keyCode === options.hideKey && !hideKeyDown) { - hideKey(); + hideKeyDown = true; + if (hz.hzViewer) { + pauseMedias(); + hz.hzViewer.hide(); + } + if (imgFullSize) { + return false; + } } // the following keys are processed only if an image is displayed @@ -3249,7 +3046,7 @@ var hoverZoom = { function downloadResource(url, filename, callback) { cLog('download: ' + url); if (!filename) filename = url.split('\\').pop().split('/').pop(); - if (filename.startsWith('.')) filename = 'download' + filename; + // prefix with download folder if needed if (options.downloadFolder) { cLog('options.downloadFolder: ' + options.downloadFolder); @@ -3282,8 +3079,8 @@ var hoverZoom = { } } - // 5 types of media can be saved to disk: image, video, audio, playlist, subtitles - const fileMedias = { + // 4 types of media can be saved to disk: image, video, audio, playlist + const downloadMedias = { IMG : "IMG", VIDEO : "VIDEO", AUDIO : "AUDIO", @@ -3291,118 +3088,89 @@ var hoverZoom = { SUBTITLES : "SUBTITLES" } - // return filename without knowing type of media displayed - function getFilename() { + // return download filename without knowing type of download + function getDownloadFilename() { - let filename = getFilenameByMedia(fileMedias.IMG, false); + let filename = getDownloadFilenameByMedia(downloadMedias.IMG); if (filename) return filename; - filename = getFilenameByMedia(fileMedias.VIDEO, false); + filename = getDownloadFilenameByMedia(downloadMedias.VIDEO); if (filename) return filename; - filename = getFilenameByMedia(fileMedias.AUDIO, false); + filename = getDownloadFilenameByMedia(downloadMedias.AUDIO); if (filename) return filename; - filename = getFilenameByMedia(fileMedias.PLAYLIST, false); + filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST); if (filename) return filename; - filename = getFilenameByMedia(fileMedias.SUBTITLES, false); + filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES); if (filename) return filename; return ''; } - function replaceOriginalFilename(filename) { - if (options.replaceOriginalFilename) { - if (filename.indexOf('.') !== -1) filename = filename.replace(/(.*)\.(.*)/, `${options.downloadFilename}.$2`); - else filename = options.downloadFilename; - } - return filename; - } - - // return original or download filename according to type of media in param - function getFilenameByMedia(fileMedia, download = true) { + // return download filename according to type of download in param + function getDownloadFilenameByMedia(downloadMedia) { let src, filename; - switch (fileMedia) { - case fileMedias.IMG: - if (!hz.hzViewer) return undefined; + switch (downloadMedia) { + case downloadMedias.IMG: + if (!hz.hzViewer) return ''; let img = hz.hzViewer.find('img:not(.hzPlaceholder)').get(0); - if (!img) return undefined; + if (!img) return ''; src = img.src; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename === '') { - filename = 'image'; - } - if (download) { - filename = replaceOriginalFilename(filename); - if (filename.indexOf('.') === -1) filename = filename + '.jpg'; // add default extension for download - } + if (filename == '') filename = 'image'; + if (filename.indexOf('.') === -1) filename = filename + '.jpg'; return filename; - case fileMedias.VIDEO: - if (!hz.hzViewer) return undefined; + case downloadMedias.VIDEO: + if (!hz.hzViewer) return ''; let video = hz.hzViewer.find('video').get(0); - if (!video) return undefined; + if (!video) return ''; src = video.src; - if (src.startsWith('blob:')) return undefined; + if (src.startsWith('blob:')) return ''; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename === '') { - filename = 'video'; - } - if (download) { - filename = replaceOriginalFilename(filename); - if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download - } + if (filename == '') filename = 'video'; + if (filename.indexOf('.') === -1) filename = filename + '.mp4'; return filename; - case fileMedias.AUDIO: - if (!hz.hzViewer) return undefined; + case downloadMedias.AUDIO: + if (!hz.hzViewer) return ''; let audio = hz.hzViewer.find('audio').get(0); - if (!audio) return undefined; + if (!audio) return ''; src = audio.src; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (filename === '') { - filename = 'audio'; - } - if (download) { - filename = replaceOriginalFilename(filename); - if (filename.indexOf('.') === -1) filename = filename + '.mp4'; // add default extension for download - } + if (filename == '') filename = 'audio'; + if (filename.indexOf('.') === -1) filename = filename + '.mp4'; return filename; - case fileMedias.PLAYLIST: - if (!hz.hzViewer) return undefined; - if (!srcDetails.playlist) return undefined; + case downloadMedias.PLAYLIST: + if (!hz.hzViewer) return ''; + if (!srcDetails.playlist) return ''; src = srcDetails.url; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (download) { - filename = replaceOriginalFilename(filename); - filename = 'playlist-' + filename; - if (filename.indexOf('.') === -1) filename = filename + '.m3u8'; // add default extension for download - } + filename = 'playlist-' + filename; + if (filename.indexOf('.') === -1) filename = filename + '.m3u8'; return filename; - case fileMedias.SUBTITLES: - if (!hz.hzViewer) return undefined; - if (!srcDetails.subtitlesUrl) return undefined; + case downloadMedias.SUBTITLES: + if (!hz.hzViewer) return ''; + if (!srcDetails.subtitlesUrl) return ''; src = srcDetails.subtitlesUrl; // remove trailing / & trailing query src = src.replace(/\/$/, '').split(/[\?!#&]/)[0]; // extract filename filename = src.split('/').pop().split(':')[0].replace(regexForbiddenChars, ''); - if (download) { - filename = replaceOriginalFilename(filename); - filename = 'subtitles-' + filename; - if (filename.indexOf('.') === -1) filename = filename + '.txt'; // add default extension for download - } + filename = 'subtitles-' + filename; + if (filename.indexOf('.') === -1) filename = filename + '.txt'; return filename; } return ''; @@ -3480,33 +3248,14 @@ var hoverZoom = { let img = hz.hzViewer.find('img').get(0); if (!img) return; let src = img.src; - let filename = getFilenameByMedia(fileMedias.IMG); + let filename = getDownloadFilenameByMedia(downloadMedias.IMG); if (!filename) return; - if (options.addDownloadCaption) { - // prefix with caption - let caption = getCaption(); - if (caption) { - caption = '[' + caption + ']'; - filename = caption + filename; - } - } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + getSizeImage(img) + ']'; + let size = '[' + img.naturalWidth + 'x' + img.naturalHeight + ']'; filename = size + filename; } - if (options.addDownloadIndex) { - let gallery = hz.currentLink.data().hoverZoomGallerySrc; - let index = hz.currentLink.data().hoverZoomGalleryIndex; - if (gallery) { - index++; - let indexLen = index.toString().length; - let galleryLen = gallery.length.toString().length - let galleryIndex = `[${index.toString().padStart(galleryLen,'0')}-${gallery.length}]`; - filename = galleryIndex + filename; - } - } if (options.addDownloadOrigin) { // prefix with origin let origin = '[' + getOrigin() + ']'; @@ -3521,20 +3270,12 @@ var hoverZoom = { if (!video) return; let src = video.src; if (src.startsWith('blob:')) return; - let filename = getFilenameByMedia(fileMedias.VIDEO); + let filename = getDownloadFilenameByMedia(downloadMedias.VIDEO); if (!filename) return; - if (options.addDownloadCaption) { - // prefix with caption - let caption = getCaption(); - if (caption) { - caption = '[' + caption + ']'; - filename = caption + filename; - } - } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + getSizeVideo(video) + ']'; + let size = '[' + video.videoWidth + 'x' + video.videoHeight + ']'; filename = size + filename; } if (options.addDownloadDuration) { @@ -3555,17 +3296,9 @@ var hoverZoom = { let audio = hz.hzViewer.find('audio').get(0); if (!audio) return; let src = audio.src; - let filename = getFilenameByMedia(fileMedias.AUDIO); + let filename = getDownloadFilenameByMedia(downloadMedias.AUDIO); if (!filename) return; - if (options.addDownloadCaption) { - // prefix with caption - let caption = getCaption(); - if (caption) { - caption = '[' + caption + ']'; - filename = caption + filename; - } - } if (options.addDownloadDuration) { // prefix with duration [hh mm ss] let duration = hz.secondsToHms(audio.duration); @@ -3584,17 +3317,9 @@ var hoverZoom = { let video = hz.hzViewer.find('video').get(0); let audio = hz.hzViewer.find('audio').get(0); if (!video && !audio) return; - let filename = getFilenameByMedia(fileMedias.SUBTITLES); + let filename = getDownloadFilenameByMedia(downloadMedias.SUBTITLES); if (!filename) return; - if (options.addDownloadCaption) { - // prefix with caption - let caption = getCaption(); - if (caption) { - caption = '[' + caption + ']'; - filename = caption + filename; - } - } if (options.addDownloadOrigin) { // prefix with origin let origin = '[' + getOrigin() + ']'; @@ -3609,20 +3334,12 @@ var hoverZoom = { if (!hz.hzViewer) return; let video = hz.hzViewer.find('video').get(0); if (!video) return; - let filename = getFilenameByMedia(fileMedias.PLAYLIST); + let filename = getDownloadFilenameByMedia(downloadMedias.PLAYLIST); if (!filename) return; - if (options.addDownloadCaption) { - // prefix with caption - let caption = getCaption(); - if (caption) { - caption = '[' + caption + ']'; - filename = caption + filename; - } - } if (options.addDownloadSize) { // prefix with size [WxH] - let size = '[' + getSizeVideo(video) + ']'; + let size = '[' + video.videoWidth + 'x' + video.videoHeight + ']'; filename = size + filename; } if (options.addDownloadDuration) { @@ -3636,6 +3353,12 @@ var hoverZoom = { filename = origin + filename; } + // prefix with download folder if needed + if (options.downloadFolder) { + let downloadFolder = options.downloadFolder; + filename = downloadFolder + filename; + } + // download KO: This function must be called during a user gesture => debugger must be closed downloadResource(srcDetails.url, filename); savePlaylistAsMP3MP4(filename); @@ -3644,14 +3367,6 @@ var hoverZoom = { // - filename.m3u8.mp4 (video part) // - filename.m3u8.mp3 (audio part) function savePlaylistAsMP3MP4(filename) { - // prefix with download folder if needed - if (options.downloadFolder) { - cLog.log('options.downloadFolder: ' + options.downloadFolder); - let downloadFolder = options.downloadFolder; - filename = downloadFolder + filename; - cLog.log('filename: ' + filename); - } - // audio if (fmp4Data['audio'].length) { const blobAudio = new Blob([arrayConcat(fmp4Data['audio'])], {type:'application/octet-stream'}); @@ -3709,22 +3424,6 @@ var hoverZoom = { return window.location.hostname.replace(regexForbiddenChars, '_'); } - // return displayed size (W x H) - function getSizeVideo(video) { - return video.videoWidth + 'x' + video.videoHeight; - } - - // return displayed size (W x H) - function getSizeImage(img) { - return img.naturalWidth + 'x' + img.naturalHeight; - } - - // return caption with forbidden characters replaced by '_' - function getCaption() { - let caption = hz.currentLink.data().hoverZoomCaption || hz.currentLink.data().hoverZoomGalleryCaption || ''; - return caption.replace(regexForbiddenChars, '_'); - } - function rotateGalleryImg(rot) { cLog('rotateGalleryImg(' + rot + ')'); var link = hz.currentLink, data = link.data(); @@ -3809,7 +3508,7 @@ var hoverZoom = { maxHeight(options.maxHeight); webSiteExcluded = null; - body100pct = (body.css('position') != 'static') || (body.css('padding-left') == '0px' && body.css('padding-right') == '0px' && body.css('margin-left') == '0px' && body.css('margin-right') == '0px' && (body.css('max-width') == 'none' || body.css('max-width') == '100%')); + body100pct = (body.css('position') != 'static') || (body.css('padding-left') == '0px' && body.css('padding-right') == '0px' && body.css('margin-left') == '0px' && body.css('margin-right') == '0px'); hz.pageGenerator = $('meta[name="generator"]').attr('content'); prepareImgLinks(); bindEvents(); @@ -3928,7 +3627,7 @@ var hoverZoom = { // Simulates a mousemove event to force a zoom call displayPicFromElement:function (el, force) { - if (el.filter(':hover').length > 0 || force) { + if (el.is(':hover') || force) { hoverZoom.currentLink = el; $(document).mousemove(); } @@ -4077,26 +3776,28 @@ var hoverZoom = { prepareFromDocument:function (link, url, getSrc, isAsync = false) { url = url.replace('http:', location.protocol); chrome.runtime.sendMessage({action:'ajaxRequest', url: url, method: 'GET'}, function(data) { - let doc = document.implementation.createHTMLDocument(); - doc.body.innerHTML = data; - const httpRefresh = doc.querySelector('meta[http-equiv="refresh"][content]'); + var doc = document.implementation.createHTMLDocument(); + doc.open(); + doc.write(data); + doc.close(); + var httpRefresh = doc.querySelector('meta[http-equiv="refresh"][content]'); if (httpRefresh) { - let redirUrl = httpRefresh.content.substr(httpRefresh.content.toLowerCase().indexOf('url=') + 4); + var redirUrl = httpRefresh.content.substr(httpRefresh.content.toLowerCase().indexOf('url=') + 4); if (redirUrl) { redirUrl = redirUrl.replace('http:', location.protocol); hoverZoom.prepareFromDocument(link, redirUrl, getSrc, isAsync); } } - const handleSrc = function (src) { - if (src) - hoverZoom.prepareLink(link, src); + var handleSrc = function (src) { + if (src) + hoverZoom.prepareLink(link, src); }; if (isAsync) { getSrc(doc, handleSrc); } else { - let src = getSrc(doc); + var src = getSrc(doc); handleSrc(src); } }); @@ -4438,88 +4139,6 @@ var hoverZoom = { } result.openPos = result.closePos = -1; return result; - }, - - // Return largest src available in srcset according to width and density - // samples srcsets: - // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg" - // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg 489w, http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble-768x1099.jpg 768w" - // "http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble.jpg 1x, http://static.picto.fr/wp-content/uploads/2017/05/Grand-Trouble-768x1099.jpg 2x" - // "resize1-lejdd.ladmedia.fr/rcrop/620,310/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 620w, - // resize1-lejdd.ladmedia.fr/rcrop/300,150/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 300w, - // resize1-lejdd.ladmedia.fr/rcrop/710,355/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 710w, - // resize1-lejdd.ladmedia.fr/rcrop/940,470/img/var/europe1/storage/images/lejdd/jdd-paris/paris-le-nouveau-palais-de-justice-ou-lon-ne-peut-pas-se-garer-3627203/47706995-1-fre-FR/Paris-Le-nouveau-palais-de-justice-ou-l-on-ne-peut-pas-se-garer.jpg 940w" - // "https://video-images.vice.com/_uncategorized/1522934375314-retinite1.png?resize=400:*, https://video-images.vice.com/_uncategorized/1522934375314-retinite1.png?resize=600:* 2x" - // "https://www.parismatch.com/lmnr/f/webp/r/72,48,forcex,center-middle/img/var/pm/public/media/image/2024/03/31/12/2024-03-31t094641z_493903911_rc2xw6a0kxis_rtrmadp_3_britain-royals.jpg?VersionId=V90sjBHDp8nZOHJEoSwrJK3SThPIaGtD, - // https://www.parismatch.com/lmnr/f/webp/r/144,96,forcex,center-middle/img/var/pm/public/media/image/2024/03/31/12/2024-03-31t094641z_493903911_rc2xw6a0kxis_rtrmadp_3_britain-royals.jpg?VersionId=V90sjBHDp8nZOHJEoSwrJK3SThPIaGtD 2x" - getBiggestSrcFromSrcset:function(srcset) { - - if (srcset == undefined) - return undefined; - - // discard inline images - if (hoverZoom.isEmbeddedImg(srcset)) - return undefined; - - var src = undefined; - srcset = srcset.trim(); - - srcset = srcset.replace(/,http/g, ', http'); - if (srcset.indexOf(", ") != -1) { - - if (srcset.indexOf("x, ") != -1) { srcset = srcset.split("x, "); } - else if (srcset.indexOf("w, ") != -1) { srcset = srcset.split("w, "); } - else { srcset = srcset.split(", "); } - - var urls = new Map(); - var xws = []; - // separate urls and density/width - for (var i = 0; i < srcset.length; i++) { - var el = srcset[i].trim(); - var url, xw; - if (el.indexOf(' ') == -1) { - url = el; - xw = "1x"; // default value - } - else { - url = el.split(' ')[0]; - xw = el.split(' ')[1]; - } - xw = xw.replace('x','').replace('w',''); - urls.set(parseInt(xw), url); - xws.push(parseInt(xw)); - } - // sort density/width - xws.sort(function(a, b){return b-a}); - // select url associated to largest density/width - src = urls.get(xws[0]); - } - else { - srcset = srcset.trim(); - if (srcset.indexOf(' ') == -1) { src = srcset; } - else { - src = srcset.split(' ')[0].trim(); - } - } - return src; - }, - - emptyHoverZoomViewer:function(now) { - if (!hoverZoom.hzViewer) return; - hoverZoom.hzViewer.stop(true, true).fadeOut(now ? 0 : options.fadeDuration, function () { - hoverZoom.hzViewer.empty(); - }); - }, - - // In JavaScript, keys can be strings, numbers, or identifier names WITHOUT single or double quotes - // e.g: person = {name:"John", age:31, city:"New York"}; - strToJavascriptObj:function(e) { - if (typeof e == "string") { - let obj = new Function("return" + e); - try { - return obj(); - } catch {} - } } }; diff --git a/js/options.js b/js/options.js index 09c5dc57b..6a987773b 100644 --- a/js/options.js +++ b/js/options.js @@ -47,15 +47,12 @@ function initActionKeys() { function loadKeys(sel) { $('').appendTo(sel); - if (sel.attr('id') != 'selPrevImgKey' || sel.attr('id') != 'selNextImgKey'){ + if (sel.attr('id') != 'lockImageKey') $('').appendTo(sel); - $('').appendTo(sel); - } if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); $('').appendTo(sel); - $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } @@ -80,7 +77,7 @@ function loadKeys(sel) { // Saves options to localStorage. // TODO: Migrate to https://developer.chrome.com/extensions/storage -function saveOptions(exportSettings = false) { +function saveOptions() { options.extensionEnabled = $('#chkExtensionEnabled')[0].checked; options.darkMode = $('#chkDarkMode')[0].checked; options.zoomFactor = $('#txtZoomFactor')[0].value; @@ -108,7 +105,6 @@ function saveOptions(exportSettings = false) { options.ambilightHaloSize = $('#txtAmbilightHaloSize')[0].value / 100; options.ambilightBackgroundOpacity = $('#txtAmbilightBackgroundOpacity')[0].value / 100; options.centerImages = $('#chkCenterImages')[0].checked; - options.autoLockImages = $('#chkAutoLockImages')[0].checked; options.frameBackgroundColor = $('#pickerFrameBackgroundColor')[0].value; options.frameThickness = $('#txtFrameThickness')[0].value; @@ -129,16 +125,6 @@ function saveOptions(exportSettings = false) { options[key] = parseInt($('#sel' + id).val()); }); - options.showDetailFilename = $('#chkShowDetailFilename')[0].checked; - options.showDetailHost = $('#chkShowDetailHost')[0].checked; - options.showDetailLastModified = $('#chkShowDetailLastModified')[0].checked; - options.showDetailExtension = $('#chkShowDetailExtension')[0].checked; - options.showDetailContentLength = $('#chkShowDetailContentLength')[0].checked; - options.showDetailDuration = $('#chkShowDetailDuration')[0].checked; - options.showDetailScale = $('#chkShowDetailScale')[0].checked; - options.showDetailRatio = $('#chkShowDetailRatio')[0].checked; - options.showDetailDimensions = $('#chkShowDetailDimensions')[0].checked; - options.addToHistory = $('#chkAddToHistory')[0].checked; options.allowHeadersRewrite = $('#chkAllowHeadersRewrite')[0].checked; @@ -151,31 +137,20 @@ function saveOptions(exportSettings = false) { options.detailsLocation = $('#selectDetailsLocation').val(); options.fontSize = $('#txtFontSize')[0].value; options.fontOutline = $('#chkFontOutline')[0].checked; - options.belowPositionOffset = $('#txtBelowPositionOffset')[0].value; - options.abovePositionOffset = $('#txtAbovePositionOffset')[0].value; - options.captionOpacity = $('#txtCaptionOpacity')[0].value / 100; - options.detailsOpacity = $('#txtDetailsOpacity')[0].value / 100; options.displayImageLoader = $('#chkDisplayImageLoader')[0].checked; options.downloadFolder = $('#txtDownloadFolder')[0].value; options.addDownloadOrigin = $('#chkAddDownloadOrigin')[0].checked; options.addDownloadSize = $('#chkAddDownloadSize')[0].checked; - options.addDownloadDuration = $('#chkAddDownloadDuration')[0].checked; - options.addDownloadIndex = $('#chkAddDownloadIndex')[0].checked; - options.addDownloadCaption = $('#chkAddDownloadCaption')[0].checked; - options.replaceOriginalFilename = $('#chkDownloadReplaceOriginalFilename')[0].checked; - options.downloadFilename = $('#txtDownloadReplaceOriginalFilename')[0].value; options.debug = $('#chkEnableDebug')[0].checked; + options.addDownloadDuration = $('#chkAddDownloadDuration')[0].checked; options.useSeparateTabOrWindowForUnloadableUrlsEnabled = $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked; options.useSeparateTabOrWindowForUnloadableUrls = $('#selectUseSeparateTabOrWindowForUnloadableUrls').val(); - if (exportSettings) { - $('#txtBoxImportExportSettings').val(JSON.stringify(options)); - } else { - localStorage.options = JSON.stringify(options); + localStorage.options = JSON.stringify(options); + + sendOptions(options); + restoreOptions(); - sendOptions(options); - restoreOptions(); - } return false; } @@ -225,7 +200,6 @@ function restoreOptions(optionsFromFactorySettings) { $('#rngAmbilightBackgroundOpacity').val(parseInt(options.ambilightBackgroundOpacity * 100)); $('#txtAmbilightBackgroundOpacity').val(parseInt(options.ambilightBackgroundOpacity * 100)); $('#chkCenterImages').trigger(options.centerImages ? 'gumby.check' : 'gumby.uncheck'); - $('#chkAutoLockImages').trigger(options.autoLockImages ? 'gumby.check' : 'gumby.uncheck'); $('#pickerFrameBackgroundColor').val(options.frameBackgroundColor); $('#rngFrameThickness').val(parseInt(options.frameThickness)); $('#txtFrameThickness').val(parseInt(options.frameThickness)); @@ -234,10 +208,6 @@ function restoreOptions(optionsFromFactorySettings) { $('#rngFontSize').val(parseInt(options.fontSize)); $('#txtFontSize').val(parseInt(options.fontSize)); $('#chkFontOutline').trigger(options.fontOutline ? 'gumby.check' : 'gumby.uncheck'); - $('#txtBelowPositionOffset').val(parseFloat(options.belowPositionOffset)); - $('#txtAbovePositionOffset').val(parseFloat(options.abovePositionOffset)); - $('#txtCaptionOpacity').val(parseInt(options.captionOpacity * 100)); - $('#txtDetailsOpacity').val(parseInt(options.detailsOpacity * 100)); if (options.frameBackgroundColor == "") { initColorPicker('#ffffff'); @@ -270,16 +240,6 @@ function restoreOptions(optionsFromFactorySettings) { $('#sel' + id).val(options[key]); }); - $('#chkShowDetailFilename').trigger(options.showDetailFilename ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailHost').trigger(options.showDetailHost ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailLastModified').trigger(options.showDetailLastModified ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailExtension').trigger(options.showDetailExtension ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailContentLength').trigger(options.showDetailContentLength ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailDuration').trigger(options.showDetailDuration ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailScale').trigger(options.showDetailScale ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailRatio').trigger(options.showDetailRatio ? 'gumby.check' : 'gumby.uncheck'); - $('#chkShowDetailDimensions').trigger(options.showDetailDimensions ? 'gumby.check' : 'gumby.uncheck'); - $('#chkAddToHistory').trigger(options.addToHistory ? 'gumby.check' : 'gumby.uncheck'); $('#chkAllowHeadersRewrite').trigger(options.allowHeadersRewrite ? 'gumby.check' : 'gumby.uncheck'); @@ -299,10 +259,6 @@ function restoreOptions(optionsFromFactorySettings) { $('#chkAddDownloadOrigin').trigger(options.addDownloadOrigin ? 'gumby.check' : 'gumby.uncheck'); $('#chkAddDownloadSize').trigger(options.addDownloadSize ? 'gumby.check' : 'gumby.uncheck'); $('#chkAddDownloadDuration').trigger(options.addDownloadDuration ? 'gumby.check' : 'gumby.uncheck'); - $('#chkAddDownloadIndex').trigger(options.addDownloadIndex ? 'gumby.check' : 'gumby.uncheck'); - $('#chkAddDownloadCaption').trigger(options.addDownloadCaption ? 'gumby.check' : 'gumby.uncheck'); - $('#chkDownloadReplaceOriginalFilename').trigger(options.replaceOriginalFilename ? 'gumby.check' : 'gumby.uncheck'); - $('#txtDownloadReplaceOriginalFilename').val(options.downloadFilename); $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').trigger(options.useSeparateTabOrWindowForUnloadableUrlsEnabled ? 'gumby.check' : 'gumby.uncheck'); $('#selectUseSeparateTabOrWindowForUnloadableUrls').val(options.useSeparateTabOrWindowForUnloadableUrls); $('#chkEnableDebug').trigger(options.debug ? 'gumby.check' : 'gumby.uncheck'); @@ -395,15 +351,14 @@ function btnRemoveExcludedSiteOnClick() { } function selKeyOnChange(event) { - const noneKey = '0'; // sel key code for 'none' - let currSel = $(event.target); + var currSel = $(event.target); if (currSel[0].dataset.val0 == undefined) return; // event fired before init currSel[0].dataset.val1 = currSel.val(); checkModification(currSel); - if (currSel.val() != noneKey) { + if (currSel.val() != '0') { $('.actionKey').each(function () { if (!$(this).is(currSel) && $(this).val() == currSel.val()) { - $(this).val(noneKey); + $(this).val('0'); $(this)[0].dataset.val1 = $(this).val(); checkModification($(this)); } @@ -498,16 +453,6 @@ function downloadFolderOnChange(val) { return this.value; } -// validate user input -function replaceOriginalFilenameOnChange(val) { - let value = (typeof val == 'string' ? val : this.value); - value = value.trim(); - // remove Windows Explorer forbidden characters for file name -> : * ? " < > | / - value = value.replace(/[!*:?"<>|\/\\]/g, ''); - this.value = value; - return this.value; -} - function updateDivAmbilight() { if ($('#chkAmbilightEnabled')[0].checked) { $('#divAmbilight').removeClass('disabled'); @@ -516,14 +461,6 @@ function updateDivAmbilight() { } } -function updateDownloadReplaceOriginalFilename() { - if ($('#chkDownloadReplaceOriginalFilename')[0].checked) { - $('#txtDownloadReplaceOriginalFilename').removeClass('disabled'); - } else { - $('#txtDownloadReplaceOriginalFilename').addClass('disabled'); - } -} - function updateUseSeparateTabOrWindowForUnloadableUrls() { if ($('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled')[0].checked) { $('#selectUseSeparateTabOrWindowForUnloadableUrls').removeClass('disabled'); @@ -571,21 +508,6 @@ function updateTxtFontSize() { $('#txtFontSize')[0].value = this.value; } -function updateTxtBelowPositionOffset() { - $('#txtBelowPositionOffset')[0].value = this.value; -} - -function updateTxtAbovePositionOffset() { - $('#txtAbovePositionOffset')[0].value = this.value; -} - -function updateTxtCaptionOpacity() { - $('#txtCaptionOpacity')[0].value = this.value; -} -function updateTxtDetailsOpacity() { - $('#txtDetailsOpacity')[0].value = this.value; -} - function updateRngFontSize() { this.value = percentageOnChange(this.value); $('#rngFontSize').val(this.value); @@ -685,8 +607,6 @@ function initColorPicker(color){ const Saved = Symbol("saved"); const Cancel = Symbol("cancel"); const Reset = Symbol("reset"); -const Imported = Symbol("imported"); -const ImportFail = Symbol("importFail"); function displayMsg(msg) { switch (msg) { case Saved: @@ -698,12 +618,6 @@ function displayMsg(msg) { case Reset: $('#msgtxt').removeClass().addClass('centered text-center alert info').text(chrome.i18n.getMessage('optReset')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); break; - case Imported: - $('#msgtxt').removeClass().addClass('centered text-center alert success').text(chrome.i18n.getMessage('optImport')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); - break; - case ImportFail: - $('#msgtxt').removeClass().addClass('centered text-center alert danger').text(chrome.i18n.getMessage('optImportFailed')).clearQueue().animate({opacity:1}, 500).delay(5000).animate({opacity:0}, 500); - break; default: break; } @@ -723,8 +637,6 @@ $(function () { $('#btnReset').click(function() { restoreOptionsFromFactorySettings(); displayMsg(Reset); return false; }); $('#btnDisableAllPlugins').click(function() { disableAllPlugins(); return false; }); $('#btnEnableAllPlugins').click(function() { enableAllPlugins(); return false; }); - $('#btnImportSettings').click(function() { importSettings(); return false; }); - $('#btnExportSettings').click(function() { exportSettings(); return false; }); $('#chkWhiteListMode').parent().on('gumby.onChange', chkWhiteListModeOnChange); $('#txtZoomFactor').change(percentageOnChange); $('#txtPicturesOpacity').change(percentageOnChange); @@ -741,17 +653,11 @@ $(function () { $('#txtFrameThickness').change(updateRngFrameThickness); $('#rngFontSize').on('input change', updateTxtFontSize); $('#txtFontSize').change(updateRngFontSize); - $('#txtBelowPositionOffset').change(updateTxtBelowPositionOffset); - $('#txtAbovePositionOffset').change(updateTxtAbovePositionOffset); - $('#txtCaptionOpacity').change(updateTxtCaptionOpacity); - $('#txtDetailsOpacity').change(updateTxtDetailsOpacity); $('#txtVideoPositionStep').change(percentageOnChange); $('.actionKey').change(selKeyOnChange); $('#btnAddExcludedSite').click(btnAddExcludedSiteOnClick); $('#btnRemoveExcludedSite').click(btnRemoveExcludedSiteOnClick); $('#txtDownloadFolder').change(downloadFolderOnChange); - $('#chkDownloadReplaceOriginalFilename').parent().on('gumby.onChange', updateDownloadReplaceOriginalFilename); - $('#txtDownloadReplaceOriginalFilename').change(replaceOriginalFilenameOnChange); $('#chkUseSeparateTabOrWindowForUnloadableUrlsEnabled').parent().on('gumby.onChange', updateUseSeparateTabOrWindowForUnloadableUrls); $('#chkHideMouseCursor').parent().on('gumby.onChange', updateDivHideMouseCursor); $('#chkDarkMode').parent().on('gumby.onChange', updateDarkMode); @@ -775,32 +681,6 @@ function enableAllPlugins() { $('input.chkPlugin').each(function() { $(this).trigger('gumby.check'); }) } -//Checks if string is JSON. -//If yes, imports settings and clears textarea. -function importSettings() { - let jsonImport; - try { - jsonImport = JSON.parse($('#txtBoxImportExportSettings')[0].value); - // Checks if a few HZ+ settings are defined to test if it's a valid HZ+ JSON - const jsonTest = [jsonImport.centerImages, jsonImport.fullZoomKey, jsonImport.hideMouseCursor]; - jsonTest.forEach((variable) => { - if (typeof variable === 'undefined') { - throw new Error('Not a valid HZ+ import JSON'); - } - }); - } catch (e) { - displayMsg(ImportFail); - return false; - } - displayMsg(Imported); - restoreOptions({jsonImport}); - $('#txtBoxImportExportSettings').val(''); -} - -function exportSettings() { - saveOptions(true); -} - // highlight item if modified, unhighlight if not modified function checkModification(item) { if (item[0].dataset.val1 == undefined) return; diff --git a/js/popup.js b/js/popup.js index 160abb0ff..782d57d49 100644 --- a/js/popup.js +++ b/js/popup.js @@ -40,15 +40,12 @@ function initActionKeys() { function loadKeys(sel) { $('').appendTo(sel); - if (sel.attr('id') != 'selPrevImgKey' || sel.attr('id') != 'selNextImgKey'){ + if (sel.attr('id') != 'lockImageKey') $('').appendTo(sel); - $('').appendTo(sel); - } if (sel.attr('id') != 'selOpenImageInTabKey') $('').appendTo(sel); $('').appendTo(sel); $('').appendTo(sel); - $('').appendTo(sel); if (navigator.appVersion.indexOf('Macintosh') > -1) { $('').appendTo(sel); } @@ -159,15 +156,14 @@ function restoreOptions(optionsFromFactorySettings) { } function selKeyOnChange(event) { - const noneKey = '0'; // sel key code for 'none' - let currSel = $(event.target); + var currSel = $(event.target); if (currSel[0].dataset.val0 == undefined) return; // event fired before init currSel[0].dataset.val1 = currSel.val(); checkModification(currSel); - if (currSel.val() != noneKey) { + if (currSel.val() != '0') { $('.actionKey').each(function () { if (!$(this).is(currSel) && $(this).val() == currSel.val()) { - $(this).val(noneKey); + $(this).val('0'); $(this)[0].dataset.val1 = $(this).val(); checkModification($(this)); } diff --git a/manifest.json b/manifest.json index 57fb8e482..49b3a3e30 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_extName__", "short_name": "__MSG_extShortName__", - "version": "1.0.222", + "version": "1.0.213", "description": "__MSG_extDescription__", "homepage_url": "https://github.com/extesy/hoverzoom/", "author": "Oleg Anashkin", @@ -101,7 +101,6 @@ "plugins/itaku_a.js", "plugins/kick_a.js", "plugins/lensdump_a.js", - "plugins/media_a.js", "plugins/mediawiki_a.js", "plugins/medium_a.js", "plugins/niconico_a.js", @@ -476,7 +475,6 @@ { "js": ["plugins/twitter.js"], "matches": [ - "*://*.x.com/*", "*://*.twitter.com/*", "*://*.tweetdeck.com/*" ] @@ -485,10 +483,6 @@ "js": ["plugins/twitpic.js"], "matches": ["*://*.twitter.com/*"] }, - { - "js": ["plugins/gifbin.js"], - "matches": ["*://gifbin.com/*"] - }, { "js": ["plugins/ravelry.js"], "matches": ["*://*.ravelry.com/*"] @@ -982,7 +976,6 @@ "js": ["plugins/e621.js"], "matches": [ "*://*.e621.net/*", - "*://*.e6ai.net/*", "*://*.e926.net/*", "*://danbooru.donmai.us/*" ] @@ -1032,10 +1025,6 @@ "js": ["plugins/furaffinity.js"], "matches": ["*://*.furaffinity.net/*"] }, - { - "js": ["plugins/furrynetwork.js"], - "matches": ["*://*.furrynetwork.com/*"] - }, { "js": ["plugins/alicdn.js"], "matches": [ @@ -1278,10 +1267,6 @@ "js": ["plugins/artstation.js"], "matches": ["*://*.artstation.com/*"] }, - { - "js": ["plugins/artfol.js"], - "matches": ["*://*.artfol.co/*"] - }, { "js": ["plugins/artsper.js"], "matches": ["*://*.artsper.com/*"] @@ -1944,10 +1929,6 @@ "js": ["plugins/head-fi.js"], "matches": ["*://*.head-fi.org/*"] }, - { - "js": ["plugins/etejo.js"], - "matches": ["*://*.etejo.com/*"] - }, { "js": ["plugins/wildcritters.js"], "matches": ["*://*.wildcritters.ws/*"] @@ -1992,10 +1973,6 @@ "js": ["plugins/kleinanzeigen.js"], "matches": ["*://*.kleinanzeigen.de/*"] }, - { - "js": ["plugins/tenor.js"], - "matches": ["*://*.tenor.com/*"] - }, { "js": ["plugins/trakttv.js"], "matches": ["*://*.trakt.tv/*"] @@ -2047,118 +2024,6 @@ { "js": ["plugins/bluesky.js"], "matches": ["*://*.bsky.app/*"] - }, - { - "js": ["plugins/onzemondial.js"], - "matches": ["*://*.onzemondial.com/*"] - }, - { - "js": ["plugins/cineserie.js"], - "matches": ["*://*.cineserie.com/*"] - }, - { - "js": ["plugins/opendata92.js"], - "matches": ["*://*.opendata.hauts-de-seine.fr/*"] - }, - { - "js": ["plugins/nature.js"], - "matches": ["*://*.nature.com/*"] - }, - { - "js": ["plugins/galerie9art.js"], - "matches": ["*://*.galerie9art.fr/*"] - }, - { - "js": ["plugins/worldatlas.js"], - "matches": ["*://*.worldatlas.com/*"] - }, - { - "js": ["plugins/kotnauction.js"], - "matches": ["*://*.kotnauction.com/*"] - }, - { - "js": ["plugins/techradar.js"], - "matches": ["*://*.techradar.com/*"] - }, - { - "js": ["plugins/feedly.js"], - "matches": ["*://*.feedly.com/*"] - }, - { - "js": ["plugins/inoreader.js"], - "matches": ["*://*.inoreader.com/*"] - }, - { - "js": ["plugins/unsplash.js"], - "matches": ["*://*.unsplash.com/*"] - }, - { - "js": ["plugins/usarmy.js"], - "matches": ["*://*.defense.gov/*", "*://*.mil/*"] - }, - { - "js": ["plugins/lacroix.js"], - "matches": ["*://*.la-croix.com/*"] - }, - { - "js": ["plugins/routard.js"], - "matches": ["*://*.routard.com/*"] - }, - { - "js": ["plugins/podcasts_apple.js"], - "matches": ["*://podcasts.apple.com/*"] - }, - { - "js": ["plugins/apple.js"], - "matches": ["*://apps.apple.com/*", "*://books.apple.com/*", "*://music.apple.com/*", "*://podcasts.apple.com/*"] - }, - { - "js": ["plugins/monuments_nationaux.js"], - "matches": ["*://*.monuments-nationaux.fr/*"] - }, - { - "js": ["plugins/physorg.js"], - "matches": ["*://*.phys.org/*", "*://*.medicalxpress.com/*", "*://*.sciencex.com/*", "*://*.techxplore.com/*"] - }, - { - "js": ["plugins/spotify.js"], - "matches": ["*://*.spotify.com/*"] - }, - { - "js": ["plugins/songkick.js"], - "matches": ["*://*.songkick.com/*"] - }, - { - "js": ["plugins/nextdoor.js"], - "matches": ["*://*.nextdoor.com/*"] - }, - { - "js": ["plugins/polona.js"], - "matches": ["*://*.polona.pl/*"] - }, - { - "js": ["plugins/raindrop.js"], - "matches": ["*://*.raindrop.io/*"] - }, - { - "js": ["plugins/stackoverflow.js"], - "matches": ["*://*.stackoverflow.com/*"] - }, - { - "js": ["plugins/uinotes.js"], - "matches": ["*://*.uinotes.com/*"] - }, - { - "js": ["plugins/meiye.js"], - "matches": ["*://*.meiye.art/*"] - }, - { - "js": ["plugins/lummi.js"], - "matches": ["*://*.lummi.ai/*"] - }, - { - "js": ["plugins/brave.js"], - "matches": ["*://*.search.brave.com/*"] } ] } diff --git a/plugins/apple.js b/plugins/apple.js deleted file mode 100644 index 4ca603710..000000000 --- a/plugins/apple.js +++ /dev/null @@ -1,94 +0,0 @@ -var hoverZoomPlugins = hoverZoomPlugins || []; -hoverZoomPlugins.push( { - name: 'podcasts_apple', - version: '1.0', - prepareImgLinks: function(callback) { - var name = this.name; - - // This plug-in: - // - zoom music, apps & books covers - // - play podcasts - // - do NOT play music tracks - - // sample: https://is1-ssl.mzstatic.com/image/thumb/Music124/v4/20/d5/9f/20d59f6d-89e4-61fe-d3d4-e5680cd5f8b3/5099922840455.jpg/48x48bb.webp - // -> https://is1-ssl.mzstatic.com/image/thumb/Music124/v4/20/d5/9f/20d59f6d-89e4-61fe-d3d4-e5680cd5f8b3/5099922840455.jpg/9999x0w.png - - $('a[href*="/podcast/"]').on('mouseover', function() { - const link = $(this); - let data = link.data(); - - if (data.hoverZoomMouseOver) return; - data.hoverZoomMouseOver = true; - - const source = link.find('source'); - if (source[0] == undefined) return; - var url = source[0].srcset.split(' ')[0]; - url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); - - data.hoverZoomSrc = [url]; - callback(link, name); - - // Cover is displayed iff the cursor is still over the image - if (link.data().hoverZoomMouseOver) - hoverZoom.displayPicFromElement(link, true); - - }).on('mouseleave', function () { - const link = $(this); - link.data().hoverZoomMouseOver = false; - }); - - $('img, div.songs-list-row__song-index, div.artwork-with-badge, div.artwork-with-badge__artwork, div.artwork-wrapper, div.artwork__radiosity, div.ellipse-lockup, div.product-lockup, div.top-search-lockup, div.track-lockup__artwork-wrapper, div.vertical-video, div.we-lockup__overlay, div.we-book-artwork').on('mouseover', function() { - const link = $(this); - let data = link.data(); - - if (data.hoverZoomMouseOver) return; - data.hoverZoomMouseOver = true; - - const source = link.find('source')[0] || link.siblings('source')[0]; - if (source == undefined) return; - var url = source.srcset.replace(',http', ', http').split(' ')[0].replace(/,$/, ''); - url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); - - data.hoverZoomSrc = [url]; - callback(link, name); - - // Cover is displayed iff the cursor is still over the image - if (link.data().hoverZoomMouseOver) - hoverZoom.displayPicFromElement(link, true); - - }).on('mouseleave', function () { - const link = $(this); - link.data().hoverZoomMouseOver = false; - }); - - // deal with shadowRoot - $('amp-lcd').on('mouseover', function() { - if (this.shadowRoot == undefined) return; - - const link = $(this); - let data = link.data(); - - if (data.hoverZoomMouseOver) return; - data.hoverZoomMouseOver = true; - - const img = $(this.shadowRoot).find('img')[0]; - if (img == undefined) return; - const src = img.src; - if (src == undefined) return; - var url = src.replace(',http', ', http').split(' ')[0].replace(/,$/, ''); - url = url.replace(/(.*)\/.*/, '$1/9999x0w.png'); - - data.hoverZoomSrc = [url]; - callback(link, name); - - // Cover is displayed iff the cursor is still over the image - if (link.data().hoverZoomMouseOver) - hoverZoom.displayPicFromElement(link, true); - - }).on('mouseleave', function () { - const link = $(this); - link.data().hoverZoomMouseOver = false; - }); - - } -}); diff --git a/plugins/artfol.js b/plugins/artfol.js deleted file mode 100644 index dac9a6e9d..000000000 --- a/plugins/artfol.js +++ /dev/null @@ -1,19 +0,0 @@ -var hoverZoomPlugins = hoverZoomPlugins || []; -hoverZoomPlugins.push({ - name:'artfol.co', - version:'0.1', - - prepareImgLinks: function (callback) { - const res = []; - - $('a[href*="a/"][title]:not(.hoverZoomMouseover)').each(function() { - let img = $(this); - let src = img[0].innerHTML.match(/\/medium(.*\.jpg)/)[1]; - - img.data().hoverZoomSrc = ['https://www.artfol-image.me' + src]; - res.push(img); - }); - - callback($(res), this.name); - } -}); diff --git a/plugins/brave.js b/plugins/brave.js deleted file mode 100644 index fb6278edf..000000000 --- a/plugins/brave.js +++ /dev/null @@ -1,82 +0,0 @@ -var hoverZoomPlugins = hoverZoomPlugins || []; -hoverZoomPlugins.push( { - name: 'brave', - version: '1.0', - prepareImgLinks: function(callback) { - const pluginName = this.name; - - // sample: https://imgs.search.brave.com/-X9Un7ROC7nDmrcHTiYUf-WyLXXs36rD7Cy-31tlE2k/rs:fit:500:0:0:0/g:ce/aHR0cHM6Ly9pbWcu/ZnJlZXBpay5jb20v/cHJlbWl1bS1waG90/by9hdXN0cmFsaWFu/LXNoZXBoZXJkLWRv/Z18xMDE1Mzg0LTE2/MDM2NC5qcGc_c2l6/ZT02MjYmZXh0PWpw/Zw - $('img[src]').on('mouseover', function() { - const link = $(this); - if (link.data().hoverZoomMouseOver) return; - link.data().hoverZoomMouseOver = true; - const src = this.src; - - var HZbrave = sessionStorage.getItem('HZbrave'); - const jsObj = hoverZoom.strToJavascriptObj(HZbrave); - - var o = undefined; - try { - o = jsObj?.find(d => d.thumbnail.src == src); - } catch {} - if (o) { - const fullsize = o.thumbnail.original || o.thumbnail.src; - const caption = o.title; - link.data().hoverZoomSrc = [fullsize]; - link.data().hoverZoomCaption = caption; - var res = []; - res.push(link); - callback($(res), pluginName); - // Image is displayed if the cursor is still over the link - if (link.data().hoverZoomMouseOver) - hoverZoom.displayPicFromElement(link); - } else { - chrome.runtime.sendMessage({action:'ajaxRequest', - method:'GET', - url:window.location.href}, - function (response) { - if (response == null) { return; } - - const parser = new DOMParser(); - const doc = parser.parseFromString(response, "text/html"); - - if (doc.scripts == undefined) return; - let scripts = Array.from(doc.scripts); - scripts = scripts.filter(script => /results:\[/.test(script.text)); - if (scripts.length != 1) return; - const data = scripts[0].text; - const index1 = data.indexOf('results:[') + 8; // open [ - const index2 = hoverZoom.matchBracket(data, index1); // close ] - const usefulData = data.substring(index1, index2 + 1); - - // store for reuse - sessionStorage.setItem("HZbrave", usefulData); - - try { - const jsObj = hoverZoom.strToJavascriptObj(usefulData); - var o = undefined; - try { - o = jsObj?.find(d => d.thumbnail.src == src); - } catch {} - if (o == undefined) { return; } - const fullsize = o.thumbnail.original || o.thumbnail.src; - const caption = o.title; - link.data().hoverZoomSrc = [fullsize]; - link.data().hoverZoomCaption = caption; - var res = []; - res.push(link); - callback($(res), pluginName); - // Image is displayed if the cursor is still over the link - if (link.data().hoverZoomMouseOver) - hoverZoom.displayPicFromElement(link); - - } catch {} - }); - } - }).on('mouseleave', function() { - const link = $(this); - link.data().hoverZoomMouseOver = false; - }); - - } -}); diff --git a/plugins/cineserie.js b/plugins/cineserie.js deleted file mode 100644 index 678c261cd..000000000 --- a/plugins/cineserie.js +++ /dev/null @@ -1,42 +0,0 @@ -var hoverZoomPlugins = hoverZoomPlugins || []; -hoverZoomPlugins.push({ - name:'cineserie', - version:'0.1', - prepareImgLinks:function (callback) { - var res = []; - - // sample: https://imgr.cineserie.com/2016/05/143726.jpg?imgeng=/f_jpg/cmpr_0/w_225/h_337/m_cropbox&ver=1 - // -> https://imgr.cineserie.com/2016/05/143726.jpg - - function findFullsizeUrl(link, src) { - let fullsizeUrl = src.replace(/(png|jpe?g)\?.*/, '$1'); - if (fullsizeUrl == src) return; - - if (link.data().hoverZoomSrc == undefined) { link.data().hoverZoomSrc = [] } - if (link.data().hoverZoomSrc.indexOf(fullsizeUrl) == -1) { - link.data().hoverZoomSrc.unshift(fullsizeUrl); - res.push(link); - } - } - - $('img[src*="png?"], img[src*="jpg?"], img[src*="jpeg?"]').each(function() { - findFullsizeUrl($(this), this.src); - }); - - $('[style*="png?"], [style*="jpg?"], [style*="jpeg?"]').each(function() { - // extract url from style - var backgroundImage = this.style.backgroundImage; - if (backgroundImage && /png|jpe?g/.test(backgroundImage)) { - const reUrl = /.*url\s*\(\s*(.*)\s*\).*/i - backgroundImage = backgroundImage.replace(reUrl, '$1'); - // remove leading & trailing quotes - var backgroundImageUrl = backgroundImage.replace(/^['"]/, '').replace(/['"]+$/, ''); - findFullsizeUrl($(this), backgroundImageUrl); - } - }); - - if (res.length) { - callback($(res), this.name); - } - } -}); diff --git a/plugins/cloudflare_a.js b/plugins/cloudflare_a.js index c1235abe1..41a9bd063 100644 --- a/plugins/cloudflare_a.js +++ b/plugins/cloudflare_a.js @@ -1,26 +1,24 @@ var hoverZoomPlugins = hoverZoomPlugins || []; hoverZoomPlugins.push({ name:'cloudflare_a', - version:'0.3', + version:'0.2', prepareImgLinks:function (callback) { var res = []; // sample: https://prod.cdn-medias.jeuneafrique.com/cdn-cgi/image/q=100,f=auto,metadata=none,width=640,height=320/https://prod.cdn-medias.jeuneafrique.com/medias/2023/05/23/jad20230523-ass-tchad-idriss-deby-le-sud-1256x628.jpg // -> https://prod.cdn-medias.jeuneafrique.com/medias/2023/05/23/jad20230523-ass-tchad-idriss-deby-le-sud-1256x628.jpg - // sample: https://cdn.wamiz.fr/cdn-cgi/image/format=auto,quality=80,width=200,height=200,fit=cover/adoption/pet/picture/6624916d91181835733923.jpg - // -> https://cdn.wamiz.fr/adoption/pet/picture/6624916d91181835733923.jpg - - const reFind1 = /^.*\/cdn-cgi\/image\/.*\/(http.*)/; - const reFind2 = /(^.*)\/cdn-cgi\/image\/[^\/]{1,}\/(?!http)(.*)/; - const reReplace1 = '$1'; - const reReplace2 = '$1/$2'; + const reThumb = /^.*\/cdn-cgi\/image\/.*\/(http.*)/; + const reReplace = '$1'; function findFullsizeUrl(link, src) { - let fullsizeUrl = src.replace(reFind1, reReplace1); - if (fullsizeUrl == src) { - fullsizeUrl = src.replace(reFind2, reReplace2); - if (fullsizeUrl == src) return; - } + let fullsizeUrl = src.replace(reThumb, reReplace); + if (fullsizeUrl == src) return; + + // decode ASCII characters, for instance: '%2C' -> ',' + // NB: this operation must be try/catched because url might not be well-formed + try { + fullsizeUrl = decodeURIComponent(fullsizeUrl); + } catch {} if (link.data().hoverZoomSrc == undefined) { link.data().hoverZoomSrc = [] } if (link.data().hoverZoomSrc.indexOf(fullsizeUrl) == -1) { diff --git a/plugins/default.js b/plugins/default.js index 4d7a58fa2..46bc87641 100644 --- a/plugins/default.js +++ b/plugins/default.js @@ -1,14 +1,13 @@ var hoverZoomPlugins = hoverZoomPlugins || []; hoverZoomPlugins.push({ name: 'Default', - version:'0.7', + version:'0.6', prepareImgLinks: function (callback) { const res = []; const reVideos = /\/[^:]+\.(?:3gpp|m4v|mkv|mp4|ogv|webm)(?:[\?#].*)?(?:\/)?$/i const reImages = /\/[^:]+\.(?:avif|bmp|gifv?|ico|jfif|jpe|jpe?g|png|svg|webp|xbm)(?:[\?#].*)?(?:\/)?$/i const rePlaylists = /\/[^:]+\.(?:m3u8)(?:[\?#].*)?(?:\/)?$/i const reAudios = /\/[^:]+\.(?:flac|m4a|mp3|oga|ogg|opus|wav)(?:[\?#].*)?(?:\/)?$/i - $('a[href]').filter(function () { if (typeof(this.href) != 'string') return false; @@ -33,98 +32,6 @@ hoverZoomPlugins.push({ } } }); - - // handle elements - $('video[src]').filter(function () { - $(this).data().hoverZoomSrc = []; - if (!options.zoomVideos) - return false; - if (typeof(this.src) != 'string') - return false; - if (this.src.match(/^blob:/)) - return false; - return true; - }).each(function () { - var _this = $(this), data = _this.data(); - // discard video already being played - if (this.paused || this.controls === false) { - var src = this.src; - if (!src.match(reVideos)) - src += '.video'; - data.hoverZoomSrc = [src]; - res.push(_this); - } - }); - - // handle