From da421fe1102c0a2027a74167d78a118e358a2451 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Wed, 11 Oct 2023 16:25:36 +0200 Subject: [PATCH] UI - Ability to select between any difference date ( from / to ) and minor UI cleanup for differences page (#1855) --- changedetectionio/__init__.py | 36 ++-- changedetectionio/static/js/diff-render.js | 195 +++++++++--------- changedetectionio/static/styles/diff.css | 4 + .../static/styles/scss/diff.scss | 7 + changedetectionio/templates/diff.html | 53 +++-- changedetectionio/tests/test_backend.py | 2 +- 6 files changed, 161 insertions(+), 136 deletions(-) diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index bfa901c0..8de43f8f 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -912,21 +912,29 @@ def changedetection_app(config=None, datastore_o=None): # Read as binary and force decode as UTF-8 # Windows may fail decode in python if we just use 'r' mode (chardet decode exception) + from_version = request.args.get('from_version') + from_version_index = -2 # second newest + if from_version and from_version in dates: + from_version_index = dates.index(from_version) + else: + from_version = dates[from_version_index] + try: - newest_version_file_contents = watch.get_history_snapshot(dates[-1]) + from_version_file_contents = watch.get_history_snapshot(dates[from_version_index]) except Exception as e: - newest_version_file_contents = "Unable to read {}.\n".format(dates[-1]) + from_version_file_contents = "Unable to read to-version at index{}.\n".format(dates[from_version_index]) - previous_version = request.args.get('previous_version') - previous_timestamp = dates[-2] - if previous_version: - previous_timestamp = previous_version + to_version = request.args.get('to_version') + to_version_index = -1 + if to_version and to_version in dates: + to_version_index = dates.index(to_version) + else: + to_version = dates[to_version_index] try: - previous_version_file_contents = watch.get_history_snapshot(previous_timestamp) + to_version_file_contents = watch.get_history_snapshot(dates[to_version_index]) except Exception as e: - previous_version_file_contents = "Unable to read {}.\n".format(previous_timestamp) - + to_version_file_contents = "Unable to read to-version at index{}.\n".format(dates[to_version_index]) screenshot_url = watch.get_screenshot() @@ -942,7 +950,8 @@ def changedetection_app(config=None, datastore_o=None): output = render_template("diff.html", current_diff_url=watch['url'], - current_previous_version=str(previous_version), + from_version=str(from_version), + to_version=str(to_version), extra_stylesheets=extra_stylesheets, extra_title=" - Diff - {}".format(watch['title'] if watch['title'] else watch['url']), extract_form=extract_form, @@ -951,13 +960,14 @@ def changedetection_app(config=None, datastore_o=None): last_error_screenshot=watch.get_error_snapshot(), last_error_text=watch.get_error_text(), left_sticky=True, - newest=newest_version_file_contents, + newest=to_version_file_contents, newest_version_timestamp=dates[-1], password_enabled_and_share_is_off=password_enabled_and_share_is_off, - previous=previous_version_file_contents, + from_version_file_contents=from_version_file_contents, + to_version_file_contents=to_version_file_contents, screenshot=screenshot_url, uuid=uuid, - versions=dates[:-1], # All except current/last + versions=dates, # All except current/last watch_a=watch ) diff --git a/changedetectionio/static/js/diff-render.js b/changedetectionio/static/js/diff-render.js index faff34b4..e96680ec 100644 --- a/changedetectionio/static/js/diff-render.js +++ b/changedetectionio/static/js/diff-render.js @@ -1,110 +1,105 @@ -var a = document.getElementById("a"); -var b = document.getElementById("b"); -var result = document.getElementById("result"); - -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; +$(document).ready(function () { + var a = document.getElementById("a"); + var b = document.getElementById("b"); + var result = document.getElementById("result"); + var inputs = document.getElementsByClassName("change"); + inputs.current = 0; + + 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); + + // Jump at start + inputs.current = 0; + next_diff(); } - 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)); + $('.needs-localtime').each(function () { + for (var option of this.options) { + var dateObject = new Date(option.value * 1000); + option.label = dateObject.toLocaleString(undefined, {dateStyle: "full", timeStyle: "medium"}); + } + }) + 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 { - node = document.createTextNode(diff[i].value); + a.onkeyup = b.onkeyup = changed; } - fragment.appendChild(node); - } - - result.textContent = ""; - result.appendChild(fragment); - - // Jump at start - inputs.current = 0; - next_diff(); -} - -window.onload = function () { - /* Convert what is options from UTC time.time() to local browser time */ - var diffList = document.getElementById("diff-version"); - if (typeof diffList != "undefined" && diffList != null) { - for (var option of diffList.options) { - var dateObject = new Date(option.value * 1000); - option.label = dateObject.toLocaleString(); + + function onDiffTypeChange(radio) { + window.diffType = radio.value; + // Not necessary + // document.title = "Diff " + radio.value.slice(4); } - } - - /* Set current version date as local time in the browser also */ - var current_v = document.getElementById("current-v-date"); - var dateObject = new Date(newest_version_timestamp * 1000); - current_v.innerHTML = dateObject.toLocaleString(); - 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(); -}; + var radio = document.getElementsByName("diff_type"); + for (var i = 0; i < radio.length; i++) { + radio[i].onchange = function (e) { + onDiffTypeChange(e.target); + changed(); + }; + } -var inputs = document.getElementsByClassName("change"); -inputs.current = 0; + document.getElementById("ignoreWhitespace").onchange = function (e) { + changed(); + }; -function next_diff() { - var element = inputs[inputs.current]; - var headerOffset = 80; - var elementPosition = element.getBoundingClientRect().top; - var offsetPosition = elementPosition - headerOffset + window.scrollY; - window.scrollTo({ - top: offsetPosition, - behavior: "smooth", - }); + function next_diff() { + var element = inputs[inputs.current]; + var headerOffset = 80; + var elementPosition = element.getBoundingClientRect().top; + var offsetPosition = elementPosition - headerOffset + window.scrollY; + + window.scrollTo({ + top: offsetPosition, + behavior: "smooth", + }); + + inputs.current++; + if (inputs.current >= inputs.length) { + inputs.current = 0; + } + } + +}); - inputs.current++; - if (inputs.current >= inputs.length) { - inputs.current = 0; - } -} diff --git a/changedetectionio/static/styles/diff.css b/changedetectionio/static/styles/diff.css index 48fc3e73..55d5faa5 100644 --- a/changedetectionio/static/styles/diff.css +++ b/changedetectionio/static/styles/diff.css @@ -187,6 +187,10 @@ ins { padding: 0.5em; } #settings ins { padding: 0.5em; } + #settings option:checked { + font-weight: bold; } + #settings [type=radio], #settings [type=checkbox] { + vertical-align: middle; } .source { position: absolute; diff --git a/changedetectionio/static/styles/scss/diff.scss b/changedetectionio/static/styles/scss/diff.scss index 699f24eb..bad947b2 100644 --- a/changedetectionio/static/styles/scss/diff.scss +++ b/changedetectionio/static/styles/scss/diff.scss @@ -77,6 +77,13 @@ ins { ins { padding: 0.5em; } + + option:checked { + font-weight: bold; + } + [type=radio],[type=checkbox] { + vertical-align: middle; + } } .source { diff --git a/changedetectionio/templates/diff.html b/changedetectionio/templates/diff.html index 4dbb67a1..cfb1f706 100644 --- a/changedetectionio/templates/diff.html +++ b/changedetectionio/templates/diff.html @@ -13,10 +13,31 @@
-

Differences

- + {% if versions|length >= 1 %} + Compare + from + + to + + + {% endif %} +
+
+ Style + JSON - {% if versions|length >= 1 %} - - - - {% endif %} -
-
- Removed text - Inserted Text - + + Ignore Whitespace + + +
@@ -91,8 +100,8 @@ - {{previous}} - {{newest}} + {{from_version_file_contents}} + {{to_version_file_contents}} diff --git a/changedetectionio/tests/test_backend.py b/changedetectionio/tests/test_backend.py index 03c135fc..e2857b31 100644 --- a/changedetectionio/tests/test_backend.py +++ b/changedetectionio/tests/test_backend.py @@ -89,7 +89,7 @@ def test_check_basic_change_detection_functionality(client, live_server): # Following the 'diff' link, it should no longer display as 'unviewed' even after we recheck it a few times res = client.get(url_for("diff_history_page", uuid="first")) - assert b'Compare newest' in res.data + assert b'selected=""' in res.data, "Confirm diff history page loaded" # Check the [preview] pulls the right one res = client.get(