Last active
August 28, 2024 15:04
-
-
Save Trafalgar63me/89b86900399b32974e579cd845668d35 to your computer and use it in GitHub Desktop.
compare Images And Highlight
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Azure DevOps - PR Image Comparison | |
// @version 2024-08-20 | |
// @description Display pixel differences when clicking on a modified image in Azure Devops pull-requests view. Uses pixelmatch library. | |
// @author Mwangi Gakiba | |
// @match https://dev.azure.com/* | |
// @icon https://cdn.vsassets.io/content/icons/favicon.ico | |
// @grant none | |
// ==/UserScript== | |
const DEFAULT_MATCH_THRESHOLD = 0.1; | |
const DEFAULT_ADDED_PIXELS_COLOR = [0, 255, 0]; | |
const DEFAULT_REMOVED_PIXELS_COLOR = [255, 0, 0]; | |
const AZURE_IMAGE_SECTION_SELECTOR = '.bolt-card-content'; | |
const AZURE_IMAGE_SELECTOR = '.bolt-card-content img'; | |
(function () { | |
'use strict'; | |
const observableElement = document.body; | |
const observer = new MutationObserver(() => handleDiffableImages(observableElement)); | |
observer.observe(observableElement, { childList: true, subtree: true }); | |
})(); | |
function handleDiffableImages(element) { | |
const images = element.querySelectorAll(AZURE_IMAGE_SELECTOR); | |
images.forEach((img) => { | |
if (!img.onload) { | |
img.onload = () => attachOnClickHandlerToImages(); | |
} | |
}); | |
} | |
function attachOnClickHandlerToImages() { | |
const processedAttributeName = 'data-pixelmatch-processed'; | |
const sections = document.querySelectorAll(AZURE_IMAGE_SECTION_SELECTOR); | |
sections.forEach((section) => { | |
const pngImages = section.querySelectorAll( | |
`.repos-editor-image:not([${processedAttributeName}])` | |
); | |
if (pngImages.length % 2 !== 0) { | |
return; | |
} | |
waitForImagesToLoad(pngImages, () => { | |
pngImages.forEach((img, index) => { | |
let overlay = null; | |
img.style.cursor = 'pointer'; | |
img.setAttribute(processedAttributeName, 'true'); | |
const handleImageClick = function () { | |
if (!overlay) { | |
const nextImg = pngImages[index + (index % 2 === 0 ? 1 : -1)]; | |
overlay = compareImagesAndHighlight(img, nextImg); | |
overlay.style.position = 'absolute'; | |
overlay.style.left = img.offsetLeft + 'px'; | |
overlay.style.top = img.offsetTop + 'px'; | |
overlay.style.pointerEvents = 'none'; | |
img.parentElement.prepend(overlay); | |
} else { | |
overlay.remove(); | |
overlay = null; | |
} | |
}; | |
img.onclick = handleImageClick; | |
}); | |
}); | |
}); | |
} | |
function compareImagesAndHighlight(img1, img2) { | |
const { ctx: ctx1 } = createCanvasAndContext(img1.width, img1.height); | |
const { ctx: ctx2 } = createCanvasAndContext(img2.width, img2.height); | |
const imgData1 = drawImageToCanvas(ctx1, img1); | |
const imgData2 = drawImageToCanvas(ctx2, img2); | |
const { ctx: diffCtx, canvas: diffCanvas } = createCanvasAndContext(img1.width, img1.height); | |
const diffImageData = diffC// ==UserScript== | |
// @name Azure DevOps - PR Image Comparison | |
// @version 2024-08-20 | |
// @description Display pixel differences when clicking on a modified image in Azure Devops pull-requests view. Uses pixelmatch library. | |
// @author Marius Eismantas | |
// @match https://dev.azure.com/* | |
// @icon https://cdn.vsassets.io/content/icons/favicon.ico | |
// @grant none | |
// ==/UserScript== | |
const DEFAULT_MATCH_THRESHOLD = 0.1; | |
const DEFAULT_ADDED_PIXELS_COLOR = [0, 255, 0]; | |
const DEFAULT_REMOVED_PIXELS_COLOR = [255, 0, 0]; | |
const AZURE_IMAGE_SECTION_SELECTOR = '.bolt-card-content'; | |
const AZURE_IMAGE_SELECTOR = '.bolt-card-content img'; | |
(function () { | |
'use strict'; | |
const observableElement = document.body; | |
const observer = new MutationObserver(() => handleDiffableImages(observableElement)); | |
observer.observe(observableElement, { childList: true, subtree: true }); | |
})(); | |
function handleDiffableImages(element) { | |
const images = element.querySelectorAll(AZURE_IMAGE_SELECTOR); | |
images.forEach((img) => { | |
if (!img.onload) { | |
img.onload = () => attachOnClickHandlerToImages(); | |
} | |
}); | |
} | |
function attachOnClickHandlerToImages() { | |
const processedAttributeName = 'data-pixelmatch-processed'; | |
const sections = document.querySelectorAll(AZURE_IMAGE_SECTION_SELECTOR); | |
sections.forEach((section) => { | |
const pngImages = section.querySelectorAll( | |
`.repos-editor-image:not([${processedAttributeName}])` | |
); | |
if (pngImages.length % 2 !== 0) { | |
return; | |
} | |
waitForImagesToLoad(pngImages, () => { | |
pngImages.forEach((img, index) => { | |
let overlay = null; | |
img.style.cursor = 'pointer'; | |
img.setAttribute(processedAttributeName, 'true'); | |
const handleImageClick = function () { | |
if (!overlay) { | |
const nextImg = pngImages[index + (index % 2 === 0 ? 1 : -1)]; | |
overlay = compareImagesAndHighlight(img, nextImg); | |
overlay.style.position = 'absolute'; | |
overlay.style.left = img.offsetLeft + 'px'; | |
overlay.style.top = img.offsetTop + 'px'; | |
overlay.style.pointerEvents = 'none'; | |
img.parentElement.prepend(overlay); | |
} else { | |
overlay.remove(); | |
overlay = null; | |
} | |
}; | |
img.onclick = handleImageClick; | |
}); | |
}); | |
}); | |
} | |
function compareImagesAndHighlight(img1, img2) { | |
const { ctx: ctx1 } = createCanvasAndContext(img1.width, img1.height); | |
const { ctx: ctx2 } = createCanvasAndContext(img2.width, img2.height); | |
const imgData1 = drawImageToCanvas(ctx1, img1); | |
const imgData2 = drawImageToCanvas(ctx2, img2); | |
const { ctx: diffCtx, canvas: diffCanvas } = createCanvasAndContext(img1.width, img1.height); | |
const diffImageData = diffC |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment