kopia lustrzana https://github.com/dgtlmoon/changedetection.io
UI - Ability to highlight text and have it offered as a ignore-text option, really nice easy way to set ignores on changing text (#1746)
rodzic
087d21c61e
commit
52df3b10e7
|
@ -1430,6 +1430,27 @@ def changedetection_app(config=None, datastore_o=None):
|
||||||
# paste in etc
|
# paste in etc
|
||||||
return redirect(url_for('index'))
|
return redirect(url_for('index'))
|
||||||
|
|
||||||
|
@app.route("/highlight_submit_ignore_url", methods=['POST'])
|
||||||
|
def highlight_submit_ignore_url():
|
||||||
|
import re
|
||||||
|
mode = request.form.get('mode')
|
||||||
|
selection = request.form.get('selection')
|
||||||
|
|
||||||
|
uuid = request.args.get('uuid','')
|
||||||
|
if datastore.data["watching"].get(uuid):
|
||||||
|
if mode == 'exact':
|
||||||
|
for l in selection.splitlines():
|
||||||
|
datastore.data["watching"][uuid]['ignore_text'].append(l.strip())
|
||||||
|
elif mode == 'digit-regex':
|
||||||
|
for l in selection.splitlines():
|
||||||
|
# Replace any series of numbers with a regex
|
||||||
|
s = re.escape(l.strip())
|
||||||
|
s = re.sub(r'[0-9]+', r'\\d+', s)
|
||||||
|
datastore.data["watching"][uuid]['ignore_text'].append('/' + s + '/')
|
||||||
|
|
||||||
|
return f"<a href={url_for('preview_page', uuid=uuid)}>Click to preview</a>"
|
||||||
|
|
||||||
|
|
||||||
import changedetectionio.blueprint.browser_steps as browser_steps
|
import changedetectionio.blueprint.browser_steps as browser_steps
|
||||||
app.register_blueprint(browser_steps.construct_blueprint(datastore), url_prefix='/browser-steps')
|
app.register_blueprint(browser_steps.construct_blueprint(datastore), url_prefix='/browser-steps')
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
var csrftoken = $('input[name=csrf_token]').val();
|
||||||
|
$.ajaxSetup({
|
||||||
|
beforeSend: function (xhr, settings) {
|
||||||
|
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
|
||||||
|
xhr.setRequestHeader("X-CSRFToken", csrftoken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Load it when the #screenshot tab is in use, so we dont give a slow experience when waiting for the text diff to load
|
// Load it when the #screenshot tab is in use, so we dont give a slow experience when waiting for the text diff to load
|
||||||
window.addEventListener('hashchange', function (e) {
|
window.addEventListener('hashchange', function (e) {
|
||||||
toggle(location.hash);
|
toggle(location.hash);
|
||||||
|
@ -15,11 +24,71 @@ $(document).ready(function () {
|
||||||
$("#settings").hide();
|
$("#settings").hide();
|
||||||
} else if (hash_name === '#extract') {
|
} else if (hash_name === '#extract') {
|
||||||
$("#settings").hide();
|
$("#settings").hide();
|
||||||
}
|
} else {
|
||||||
|
|
||||||
|
|
||||||
else {
|
|
||||||
$("#settings").show();
|
$("#settings").show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const article = $('.highlightable-filter')[0];
|
||||||
|
|
||||||
|
// We could also add the 'touchend' event for touch devices, but since
|
||||||
|
// most iOS/Android browsers already show a dialog when you select
|
||||||
|
// text (often with a Share option) we'll skip that
|
||||||
|
article.addEventListener('mouseup', dragTextHandler, false);
|
||||||
|
article.addEventListener('mousedown', clean, false);
|
||||||
|
|
||||||
|
function clean(event) {
|
||||||
|
$("#highlightSnippet").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function dragTextHandler(event) {
|
||||||
|
console.log('mouseupped');
|
||||||
|
|
||||||
|
// Check if any text was selected
|
||||||
|
if (window.getSelection().toString().length > 0) {
|
||||||
|
|
||||||
|
// Find out how much (if any) user has scrolled
|
||||||
|
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
|
||||||
|
|
||||||
|
// Get cursor position
|
||||||
|
const posX = event.clientX;
|
||||||
|
const posY = event.clientY + 20 + scrollTop;
|
||||||
|
|
||||||
|
// Append HTML to the body, create the "Tweet Selection" dialog
|
||||||
|
document.body.insertAdjacentHTML('beforeend', '<div id="highlightSnippet" style="position: absolute; top: ' + posY + 'px; left: ' + posX + 'px;"><div class="pure-form-message-inline" style="font-size: 70%">Ignore any change on any line which contains the selected text.</div><br><a data-mode="exact" href="javascript:void(0);" class="pure-button button-secondary button-xsmall">Ignore exact text</a> </div>');
|
||||||
|
|
||||||
|
if (/\d/.test(window.getSelection().toString())) {
|
||||||
|
// Offer regex replacement
|
||||||
|
document.getElementById("highlightSnippet").insertAdjacentHTML('beforeend', '<a data-mode="digit-regex" href="javascript:void(0);" class="pure-button button-secondary button-xsmall">Ignore text including number changes</a>');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#highlightSnippet a').bind('click', function (e) {
|
||||||
|
if(!window.getSelection().toString().trim().length) {
|
||||||
|
alert('Oops no text selected!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: highlight_submit_ignore_url,
|
||||||
|
data: {'mode': $(this).data('mode'), 'selection': window.getSelection().toString()},
|
||||||
|
statusCode: {
|
||||||
|
400: function () {
|
||||||
|
// More than likely the CSRF token was lost when the server restarted
|
||||||
|
alert("There was a problem processing the request, please reload the page.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).done(function (data) {
|
||||||
|
$("#highlightSnippet").append(data)
|
||||||
|
}).fail(function (data) {
|
||||||
|
console.log(data);
|
||||||
|
alert('There was an error communicating with the server.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -218,3 +218,10 @@ td#diff-col div {
|
||||||
text-align: center; }
|
text-align: center; }
|
||||||
.tab-pane-inner#screenshot img {
|
.tab-pane-inner#screenshot img {
|
||||||
max-width: 99%; }
|
max-width: 99%; }
|
||||||
|
|
||||||
|
#highlightSnippet {
|
||||||
|
background: var(--color-background);
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--color-background);
|
||||||
|
box-shadow: 1px 1px 4px var(--color-shadow-jump); }
|
||||||
|
|
|
@ -119,3 +119,11 @@ td#diff-col div {
|
||||||
max-width: 99%;
|
max-width: 99%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#highlightSnippet {
|
||||||
|
background: var(--color-background);
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 5px;
|
||||||
|
background: var(--color-background);
|
||||||
|
box-shadow: 1px 1px 4px var(--color-shadow-jump);
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
{% if last_error_screenshot %}
|
{% if last_error_screenshot %}
|
||||||
const error_screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid, error_screenshot=1) }}";
|
const error_screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid, error_screenshot=1) }}";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
const highlight_submit_ignore_url="{{url_for('highlight_submit_ignore_url', uuid=uuid)}}";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
|
<script src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
|
||||||
|
|
||||||
|
@ -76,7 +79,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="text">
|
<div class="tab-pane-inner" id="text">
|
||||||
<div class="tip">Pro-tip: Use <strong>show current snapshot</strong> tab to visualise what will be ignored.</div>
|
<div class="tip">Pro-tip: Use <strong>show current snapshot</strong> tab to visualise what will be ignored, highlight text to add to ignore filters</div>
|
||||||
|
|
||||||
{% if password_enabled_and_share_is_off %}
|
{% if password_enabled_and_share_is_off %}
|
||||||
<div class="tip">Pro-tip: You can enable <strong>"share access when password is enabled"</strong> from settings</div>
|
<div class="tip">Pro-tip: You can enable <strong>"share access when password is enabled"</strong> from settings</div>
|
||||||
|
@ -91,7 +94,7 @@
|
||||||
<td id="a" style="display: none;">{{previous}}</td>
|
<td id="a" style="display: none;">{{previous}}</td>
|
||||||
<td id="b" style="display: none;">{{newest}}</td>
|
<td id="b" style="display: none;">{{newest}}</td>
|
||||||
<td id="diff-col">
|
<td id="diff-col">
|
||||||
<span id="result"></span>
|
<span id="result" class="highlightable-filter"></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
{% if last_error_screenshot %}
|
{% if last_error_screenshot %}
|
||||||
const error_screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid, error_screenshot=1) }}";
|
const error_screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid, error_screenshot=1) }}";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
const highlight_submit_ignore_url="{{url_for('highlight_submit_ignore_url', uuid=uuid)}}";
|
||||||
</script>
|
</script>
|
||||||
<script src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
|
<script src="{{url_for('static_content', group='js', filename='diff-overview.js')}}" defer></script>
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<form><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"></form>
|
||||||
<div id="diff-ui">
|
<div id="diff-ui">
|
||||||
<div class="tab-pane-inner" id="error-text">
|
<div class="tab-pane-inner" id="error-text">
|
||||||
<div class="snapshot-age error">{{watch.error_text_ctime|format_seconds_ago}} seconds ago</div>
|
<div class="snapshot-age error">{{watch.error_text_ctime|format_seconds_ago}} seconds ago</div>
|
||||||
|
@ -36,11 +37,12 @@
|
||||||
|
|
||||||
<div class="tab-pane-inner" id="text">
|
<div class="tab-pane-inner" id="text">
|
||||||
<div class="snapshot-age">{{watch.snapshot_text_ctime|format_timestamp_timeago}}</div>
|
<div class="snapshot-age">{{watch.snapshot_text_ctime|format_timestamp_timeago}}</div>
|
||||||
<span class="ignored">Grey lines are ignored</span> <span class="triggered">Blue lines are triggers</span>
|
<span class="ignored">Grey lines are ignored</span> <span class="triggered">Blue lines are triggers</span> <span class="tip"><strong>Pro-tip</strong>: Highlight text to add to ignore filters</span>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td id="diff-col">
|
<td id="diff-col" class="highlightable-filter">
|
||||||
{% for row in content %}
|
{% for row in content %}
|
||||||
<div class="{{row.classes}}">{{row.line}}</div>
|
<div class="{{row.classes}}">{{row.line}}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import time
|
||||||
|
from flask import url_for
|
||||||
|
from .util import live_server_setup, wait_for_all_checks
|
||||||
|
from changedetectionio import html_tools
|
||||||
|
from . util import extract_UUID_from_client
|
||||||
|
|
||||||
|
def set_original_ignore_response():
|
||||||
|
test_return_data = """<html>
|
||||||
|
<body>
|
||||||
|
Some initial text<br>
|
||||||
|
<p>Which is across multiple lines</p>
|
||||||
|
<br>
|
||||||
|
So let's see what happens. <br>
|
||||||
|
<p>oh yeah 456</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
with open("test-datastore/endpoint-content.txt", "w") as f:
|
||||||
|
f.write(test_return_data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_highlight_ignore(client, live_server):
|
||||||
|
live_server_setup(live_server)
|
||||||
|
set_original_ignore_response()
|
||||||
|
test_url = url_for('test_endpoint', _external=True)
|
||||||
|
res = client.post(
|
||||||
|
url_for("import_page"),
|
||||||
|
data={"urls": test_url},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
assert b"1 Imported" in res.data
|
||||||
|
|
||||||
|
# Give the thread time to pick it up
|
||||||
|
wait_for_all_checks(client)
|
||||||
|
uuid = extract_UUID_from_client(client)
|
||||||
|
# use the highlighter endpoint
|
||||||
|
res = client.post(
|
||||||
|
url_for("highlight_submit_ignore_url", uuid=uuid),
|
||||||
|
data={"mode": 'digit-regex', 'selection': 'oh yeah 123'},
|
||||||
|
follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
|
res = client.get(url_for("edit_page", uuid=uuid))
|
||||||
|
|
||||||
|
# should be a regex now
|
||||||
|
assert b'/oh\ yeah\ \d+/' in res.data
|
||||||
|
|
||||||
|
# Should return a link
|
||||||
|
assert b'href' in res.data
|
||||||
|
|
||||||
|
# And it should register in the preview page
|
||||||
|
res = client.get(url_for("preview_page", uuid=uuid))
|
||||||
|
assert b'<div class="ignored">oh yeah 456' in res.data
|
Ładowanie…
Reference in New Issue