mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2025-12-20 06:55:59 +00:00
"History" page - Use faster server side "difference" rendering, show ignored/triggered rows (#3442)
Some checks failed
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
Some checks failed
Build and push containers / metadata (push) Has been cancelled
Build and push containers / build-push-containers (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (alpine) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/amd64 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v7 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm/v8 (main) (push) Has been cancelled
ChangeDetection.io Container Build Test / Build linux/arm64 (main) (push) Has been cancelled
ChangeDetection.io App Test / lint-code (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Has been cancelled
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-10 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-11 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-12 (push) Has been cancelled
ChangeDetection.io App Test / test-application-3-13 (push) Has been cancelled
This commit is contained in:
@@ -1,115 +1,152 @@
|
||||
$(document).ready(function () {
|
||||
var a = document.getElementById("a");
|
||||
var b = document.getElementById("b");
|
||||
var result = document.getElementById("result");
|
||||
var inputs;
|
||||
|
||||
// Find all <span> elements inside pre#difference
|
||||
var inputs = $('#difference span').toArray();
|
||||
inputs.current = 0;
|
||||
|
||||
// Setup visual minimap of difference locations (cells are pre-built in Python)
|
||||
var $visualizer = $('#cell-diff-jump-visualiser');
|
||||
var $difference = $('#difference');
|
||||
var $cells = $visualizer.find('> div');
|
||||
var visualizerResolutionCells = $cells.length;
|
||||
var cellHeight;
|
||||
|
||||
if ($difference.length && visualizerResolutionCells > 0) {
|
||||
var docHeight = $difference[0].scrollHeight;
|
||||
cellHeight = docHeight / visualizerResolutionCells;
|
||||
|
||||
// Add click handlers to pre-built cells
|
||||
$cells.each(function(i) {
|
||||
$(this).data('cellIndex', i);
|
||||
$(this).on('click', function() {
|
||||
var cellIndex = $(this).data('cellIndex');
|
||||
var targetPositionInDifference = cellIndex * cellHeight;
|
||||
var viewportHeight = $(window).height();
|
||||
|
||||
// Scroll so target is at viewport center (where eyes expect it)
|
||||
window.scrollTo({
|
||||
top: $difference.offset().top + targetPositionInDifference - (viewportHeight / 2),
|
||||
behavior: "smooth"
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('#jump-next-diff').click(function () {
|
||||
if (!inputs || inputs.length === 0) return;
|
||||
|
||||
var element = inputs[inputs.current];
|
||||
var headerOffset = 80;
|
||||
var elementPosition = element.getBoundingClientRect().top;
|
||||
var offsetPosition = elementPosition - headerOffset + window.scrollY;
|
||||
// Find the next change after current scroll position
|
||||
var currentScrollPos = $(window).scrollTop();
|
||||
var viewportHeight = $(window).height();
|
||||
var currentCenter = currentScrollPos + (viewportHeight / 2);
|
||||
|
||||
// Add small buffer (50px) to jump past changes already near center
|
||||
var searchFromPosition = currentCenter + 50;
|
||||
|
||||
var nextElement = null;
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var elementTop = $(inputs[i]).offset().top;
|
||||
if (elementTop > searchFromPosition) {
|
||||
nextElement = inputs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no element found ahead, wrap to first element
|
||||
if (!nextElement) {
|
||||
nextElement = inputs[0];
|
||||
}
|
||||
|
||||
// Scroll to position the element at viewport center
|
||||
var elementTop = $(nextElement).offset().top;
|
||||
var targetScrollPos = elementTop - (viewportHeight / 2);
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
top: targetScrollPos,
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
inputs.current++;
|
||||
if (inputs.current >= inputs.length) {
|
||||
inputs.current = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// Track current scroll position in visualizer
|
||||
function updateVisualizerPosition() {
|
||||
if (!$difference.length || visualizerResolutionCells === 0) return;
|
||||
|
||||
var scrollTop = $(window).scrollTop();
|
||||
var viewportHeight = $(window).height();
|
||||
var viewportCenter = scrollTop + (viewportHeight / 2);
|
||||
var differenceTop = $difference.offset().top;
|
||||
var differenceHeight = $difference[0].scrollHeight;
|
||||
var positionInDifference = viewportCenter - differenceTop;
|
||||
|
||||
// Handle edge case: if we're at max scroll, show last cell
|
||||
// This prevents shorter documents from never reaching 100%
|
||||
var maxScrollTop = $(document).height() - viewportHeight;
|
||||
var isAtBottom = scrollTop >= maxScrollTop - 10; // 10px tolerance
|
||||
|
||||
// Calculate which cell we're currently viewing
|
||||
var currentCell;
|
||||
if (isAtBottom) {
|
||||
currentCell = visualizerResolutionCells - 1;
|
||||
} else {
|
||||
currentCell = Math.floor(positionInDifference / cellHeight);
|
||||
currentCell = Math.max(0, Math.min(currentCell, visualizerResolutionCells - 1));
|
||||
}
|
||||
|
||||
// Remove previous active marker and add to current cell
|
||||
$visualizer.find('> div').removeClass('current-position');
|
||||
$visualizer.find('> div').eq(currentCell).addClass('current-position');
|
||||
}
|
||||
|
||||
// Recalculate cellHeight on window resize
|
||||
function handleResize() {
|
||||
if ($difference.length) {
|
||||
var docHeight = $difference[0].scrollHeight;
|
||||
cellHeight = docHeight / visualizerResolutionCells;
|
||||
updateVisualizerPosition();
|
||||
}
|
||||
}
|
||||
|
||||
// Debounce scroll and resize events to reduce CPU usage
|
||||
$(window).on('scroll', updateVisualizerPosition.debounce(5));
|
||||
$(window).on('resize', handleResize.debounce(100));
|
||||
|
||||
// Initial scroll to specific line if requested
|
||||
if (typeof initialScrollToLineNumber !== 'undefined' && initialScrollToLineNumber !== null && $difference.length) {
|
||||
// Convert line number to text position and scroll to it
|
||||
var diffText = $difference.text();
|
||||
var lines = diffText.split('\n');
|
||||
|
||||
if (initialScrollToLineNumber > 0 && initialScrollToLineNumber <= lines.length) {
|
||||
// Calculate character position of the target line
|
||||
var charPosition = 0;
|
||||
for (var i = 0; i < initialScrollToLineNumber - 1; i++) {
|
||||
charPosition += lines[i].length + 1; // +1 for newline
|
||||
}
|
||||
|
||||
// Estimate vertical position based on average line height
|
||||
var totalChars = diffText.length;
|
||||
var totalHeight = $difference[0].scrollHeight;
|
||||
var estimatedTop = (charPosition / totalChars) * totalHeight;
|
||||
|
||||
// Scroll to position with line at viewport center
|
||||
var viewportHeight = $(window).height();
|
||||
setTimeout(function() {
|
||||
window.scrollTo({
|
||||
top: $difference.offset().top + estimatedTop - (viewportHeight / 2),
|
||||
behavior: "smooth"
|
||||
});
|
||||
}, 100); // Small delay to ensure page is fully loaded
|
||||
}
|
||||
}
|
||||
|
||||
// Initial position update
|
||||
if ($difference.length && cellHeight) {
|
||||
updateVisualizerPosition();
|
||||
}
|
||||
|
||||
function changed() {
|
||||
// https://github.com/kpdecker/jsdiff/issues/389
|
||||
// I would love to use `{ignoreWhitespace: true}` here but it breaks the formatting
|
||||
options = {
|
||||
ignoreWhitespace: document.getElementById("ignoreWhitespace").checked,
|
||||
};
|
||||
|
||||
var diff = Diff[window.diffType](a.textContent, b.textContent, options);
|
||||
var fragment = document.createDocumentFragment();
|
||||
for (var i = 0; i < diff.length; i++) {
|
||||
if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
|
||||
var swap = diff[i];
|
||||
diff[i] = diff[i + 1];
|
||||
diff[i + 1] = swap;
|
||||
}
|
||||
|
||||
var node;
|
||||
if (diff[i].removed) {
|
||||
node = document.createElement("del");
|
||||
node.classList.add("change");
|
||||
const wrapper = node.appendChild(document.createElement("span"));
|
||||
wrapper.appendChild(document.createTextNode(diff[i].value));
|
||||
} else if (diff[i].added) {
|
||||
node = document.createElement("ins");
|
||||
node.classList.add("change");
|
||||
const wrapper = node.appendChild(document.createElement("span"));
|
||||
wrapper.appendChild(document.createTextNode(diff[i].value));
|
||||
} else {
|
||||
node = document.createTextNode(diff[i].value);
|
||||
}
|
||||
fragment.appendChild(node);
|
||||
}
|
||||
|
||||
result.textContent = "";
|
||||
result.appendChild(fragment);
|
||||
|
||||
// For nice mouse-over hover/title information
|
||||
const removed_current_option = $('#diff-version option:selected')
|
||||
if (removed_current_option) {
|
||||
$('del').each(function () {
|
||||
$(this).prop('title', 'Removed '+removed_current_option[0].label);
|
||||
});
|
||||
}
|
||||
const inserted_current_option = $('#current-version option:selected')
|
||||
if (removed_current_option) {
|
||||
$('ins').each(function () {
|
||||
$(this).prop('title', 'Inserted '+inserted_current_option[0].label);
|
||||
});
|
||||
}
|
||||
// Set the list of possible differences to jump to
|
||||
inputs = document.querySelectorAll('#diff-ui .change')
|
||||
// Set the "current" diff pointer
|
||||
inputs.current = 0;
|
||||
// Goto diff
|
||||
$('#jump-next-diff').click();
|
||||
//$('#jump-next-diff').click();
|
||||
}
|
||||
|
||||
|
||||
onDiffTypeChange(
|
||||
document.querySelector('#settings [name="diff_type"]:checked'),
|
||||
);
|
||||
changed();
|
||||
|
||||
a.onpaste = a.onchange = b.onpaste = b.onchange = changed;
|
||||
|
||||
if ("oninput" in a) {
|
||||
a.oninput = b.oninput = changed;
|
||||
} else {
|
||||
a.onkeyup = b.onkeyup = changed;
|
||||
}
|
||||
|
||||
function onDiffTypeChange(radio) {
|
||||
window.diffType = radio.value;
|
||||
// Not necessary
|
||||
// document.title = "Diff " + radio.value.slice(4);
|
||||
}
|
||||
|
||||
var radio = document.getElementsByName("diff_type");
|
||||
for (var i = 0; i < radio.length; i++) {
|
||||
radio[i].onchange = function (e) {
|
||||
onDiffTypeChange(e.target);
|
||||
changed();
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementById("ignoreWhitespace").onchange = function (e) {
|
||||
changed();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user