Compare commits
1 Commits
darkmode
...
export-reg
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23d0679d13 |
45
README.md
@@ -13,33 +13,15 @@ _Live your data-life pro-actively, Detect website changes and perform meaningful
|
||||
|
||||
- Chrome browser included.
|
||||
- Super fast, no registration needed setup.
|
||||
- Get started watching and receiving website change notifications straight away.
|
||||
- Start watching and receiving change notifications instantly.
|
||||
|
||||
|
||||
### Target specific parts of the webpage using the Visual Selector tool.
|
||||
Easily see what changed, examine by word, line, or individual character.
|
||||
|
||||
Available when connected to a <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Playwright-content-fetcher">playwright content fetcher</a> (included as part of our subscription service)
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/visualselector-anim.gif" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Self-hosted web page change monitoring context difference " />](https://lemonade.changedetection.io/start?src=github)
|
||||
|
||||
### Easily see what changed, examine by word, line, or individual character.
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/screenshot-diff.png" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Self-hosted web page change monitoring context difference " />](https://lemonade.changedetection.io/start?src=github)
|
||||
<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/screenshot-diff.png" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Self-hosted web page change monitoring context difference " />
|
||||
|
||||
|
||||
### Perform interactive browser steps
|
||||
|
||||
Fill in text boxes, click buttons and more, setup your changedetection scenario.
|
||||
|
||||
Using the **Browser Steps** configuration, add basic steps before performing change detection, such as logging into websites, adding a product to a cart, accept cookie logins, entering dates and refining searches.
|
||||
|
||||
[<img src="docs/browsersteps-anim.gif" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Website change detection with interactive browser steps, login, cookies etc" />](https://lemonade.changedetection.io/start?src=github)
|
||||
|
||||
After **Browser Steps** have been run, then visit the **Visual Selector** tab to refine the content you're interested in.
|
||||
Requires Playwright to be enabled.
|
||||
|
||||
|
||||
### Example use cases
|
||||
#### Example use cases
|
||||
|
||||
- Products and services have a change in pricing
|
||||
- _Out of stock notification_ and _Back In stock notification_
|
||||
@@ -77,8 +59,27 @@ _Need an actual Chrome runner with Javascript support? We support fetching via W
|
||||
|
||||
We [recommend and use Bright Data](https://brightdata.grsm.io/n0r16zf7eivq) global proxy services, Bright Data will match any first deposit up to $100 using our signup link.
|
||||
|
||||
## Screenshots
|
||||
|
||||
Please :star: star :star: this project and help it grow! https://github.com/dgtlmoon/changedetection.io/
|
||||
|
||||
### Target specific parts of the webpage using the Visual Selector tool.
|
||||
|
||||
Available when connected to a <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Playwright-content-fetcher">playwright content fetcher</a> (included as part of our subscription service)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/dgtlmoon/changedetection.io/master/docs/visualselector-anim.gif" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Self-hosted web page change monitoring context difference " />
|
||||
|
||||
### Perform interactive browser steps
|
||||
|
||||
Fill in text boxes, click buttons and more, setup your changedetection scenario.
|
||||
|
||||
Using the **Browser Steps** configuration, add basic steps before performing change detection, such as logging into websites, adding a product to a cart, accept cookie logins, entering dates and refining searches.
|
||||
|
||||
<img src="docs/browsersteps-anim.gif" style="max-width:100%;" alt="Self-hosted web page change monitoring context difference " title="Website change detection with interactive browser steps, login, cookies etc" />
|
||||
|
||||
After **Browser Steps** have been run, then visit the **Visual Selector** tab to refine the content you're interested in.
|
||||
Requires Playwright to be enabled.
|
||||
|
||||
## Installation
|
||||
|
||||
### Docker
|
||||
|
||||
@@ -27,7 +27,6 @@ from flask import (
|
||||
session,
|
||||
url_for,
|
||||
)
|
||||
from flask_compress import Compress as FlaskCompress
|
||||
from flask_login import login_required
|
||||
from flask_restful import abort, Api
|
||||
from flask_wtf import CSRFProtect
|
||||
@@ -52,10 +51,6 @@ app = Flask(__name__,
|
||||
static_url_path="",
|
||||
static_folder="static",
|
||||
template_folder="templates")
|
||||
from flask_compress import Compress
|
||||
|
||||
# Super handy for compressing large BrowserSteps responses and others
|
||||
FlaskCompress(app)
|
||||
|
||||
# Stop browser caching of assets
|
||||
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
|
||||
@@ -202,9 +197,7 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
watch_api.add_resource(api_v1.SystemInfo, '/api/v1/systeminfo',
|
||||
resource_class_kwargs={'datastore': datastore, 'update_q': update_q})
|
||||
|
||||
def getDarkModeSetting():
|
||||
css_dark_mode = request.cookies.get('css_dark_mode')
|
||||
return True if (css_dark_mode == 'true' or css_dark_mode == True) else False
|
||||
|
||||
|
||||
# Setup cors headers to allow all domains
|
||||
# https://flask-cors.readthedocs.io/en/latest/
|
||||
@@ -405,7 +398,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
form = forms.quickWatchForm(request.form)
|
||||
output = render_template("watch-overview.html",
|
||||
dark_mode=getDarkModeSetting(),
|
||||
form=form,
|
||||
watches=sorted_watches,
|
||||
tags=existing_tags,
|
||||
@@ -664,7 +656,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
browser_steps_config=browser_step_ui_config,
|
||||
current_base_url=datastore.data['settings']['application']['base_url'],
|
||||
emailprefix=os.getenv('NOTIFICATION_MAIL_BUTTON_PREFIX', False),
|
||||
dark_mode=getDarkModeSetting(),
|
||||
form=form,
|
||||
has_default_notification_urls=True if len(datastore.data['settings']['application']['notification_urls']) else False,
|
||||
has_empty_checktime=using_default_check_time,
|
||||
@@ -752,7 +743,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
output = render_template("settings.html",
|
||||
form=form,
|
||||
dark_mode=getDarkModeSetting(),
|
||||
current_base_url = datastore.data['settings']['application']['base_url'],
|
||||
hide_remove_pass=os.getenv("SALTED_PASS", False),
|
||||
api_key=datastore.data['settings']['application'].get('api_access_token'),
|
||||
@@ -793,7 +783,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
# Could be some remaining, or we could be on GET
|
||||
output = render_template("import.html",
|
||||
dark_mode=getDarkModeSetting(),
|
||||
import_url_list_remaining="\n".join(remaining_urls),
|
||||
original_distill_json=''
|
||||
)
|
||||
@@ -871,7 +860,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
newest=newest_version_file_contents,
|
||||
previous=previous_version_file_contents,
|
||||
extra_stylesheets=extra_stylesheets,
|
||||
dark_mode=getDarkModeSetting(),
|
||||
versions=dates[:-1], # All except current/last
|
||||
uuid=uuid,
|
||||
newest_version_timestamp=dates[-1],
|
||||
@@ -919,7 +907,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
content=content,
|
||||
history_n=watch.history_n,
|
||||
extra_stylesheets=extra_stylesheets,
|
||||
dark_mode=getDarkModeSetting(),
|
||||
# current_diff_url=watch['url'],
|
||||
watch=watch,
|
||||
uuid=uuid,
|
||||
@@ -966,7 +953,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
content=content,
|
||||
history_n=watch.history_n,
|
||||
extra_stylesheets=extra_stylesheets,
|
||||
dark_mode=getDarkModeSetting(),
|
||||
ignored_line_numbers=ignored_line_numbers,
|
||||
triggered_line_numbers=trigger_line_numbers,
|
||||
current_diff_url=watch['url'],
|
||||
@@ -985,7 +971,6 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
def notification_logs():
|
||||
global notification_debug_log
|
||||
output = render_template("notification-log.html",
|
||||
dark_mode=getDarkModeSetting(),
|
||||
logs=notification_debug_log if len(notification_debug_log) else ["Notification logs are empty - no notifications sent yet."])
|
||||
|
||||
return output
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
# OR
|
||||
# - use multiprocessing to bump this over to its own process and add some transport layer (queue/pipes)
|
||||
|
||||
|
||||
|
||||
|
||||
from distutils.util import strtobool
|
||||
from flask import Blueprint, request, make_response
|
||||
from flask_login import login_required
|
||||
@@ -30,35 +33,38 @@ from changedetectionio.store import ChangeDetectionStore
|
||||
|
||||
browsersteps_live_ui_o = {}
|
||||
browsersteps_playwright_browser_interface = None
|
||||
browsersteps_playwright_browser_interface_browser = None
|
||||
browsersteps_playwright_browser_interface_context = None
|
||||
browsersteps_playwright_browser_interface_end_time = None
|
||||
browsersteps_playwright_browser_interface_start_time = None
|
||||
browsersteps_playwright_browser_interface_browser = None
|
||||
browsersteps_playwright_browser_interface_end_time = None
|
||||
|
||||
|
||||
def cleanup_playwright_session():
|
||||
|
||||
global browsersteps_live_ui_o
|
||||
print("Cleaning up old playwright session because time was up")
|
||||
global browsersteps_playwright_browser_interface
|
||||
global browsersteps_live_ui_o
|
||||
global browsersteps_playwright_browser_interface_browser
|
||||
global browsersteps_playwright_browser_interface_context
|
||||
global browsersteps_playwright_browser_interface_end_time
|
||||
global browsersteps_playwright_browser_interface
|
||||
global browsersteps_playwright_browser_interface_start_time
|
||||
global browsersteps_playwright_browser_interface_end_time
|
||||
|
||||
import psutil
|
||||
|
||||
current_process = psutil.Process()
|
||||
children = current_process.children(recursive=True)
|
||||
for child in children:
|
||||
print (child)
|
||||
print('Child pid is {}'.format(child.pid))
|
||||
|
||||
# .stop() hangs sometimes if its called when there are no children to process
|
||||
# but how do we know this is our child? dunno
|
||||
if children:
|
||||
browsersteps_playwright_browser_interface.stop()
|
||||
|
||||
browsersteps_live_ui_o = {}
|
||||
browsersteps_playwright_browser_interface = None
|
||||
browsersteps_playwright_browser_interface_start_time = None
|
||||
browsersteps_playwright_browser_interface_browser = None
|
||||
browsersteps_playwright_browser_interface_end_time = None
|
||||
browsersteps_playwright_browser_interface_start_time = None
|
||||
|
||||
print("Cleaning up old playwright session because time was up, calling .goodbye()")
|
||||
try:
|
||||
browsersteps_playwright_browser_interface_context.goodbye()
|
||||
except Exception as e:
|
||||
print ("Got exception in shutdown, probably OK")
|
||||
print (str(e))
|
||||
|
||||
browsersteps_playwright_browser_interface_context = None
|
||||
|
||||
print ("Cleaning up old playwright session because time was up - done")
|
||||
|
||||
def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
@@ -93,8 +99,12 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
if browsersteps_playwright_browser_interface_end_time:
|
||||
remaining = browsersteps_playwright_browser_interface_end_time-time.time()
|
||||
if browsersteps_playwright_browser_interface_end_time and remaining <= 0:
|
||||
|
||||
|
||||
cleanup_playwright_session()
|
||||
return make_response('Browser session expired, please reload the Browser Steps interface', 401)
|
||||
|
||||
return make_response('Browser session expired, please reload the Browser Steps interface', 500)
|
||||
|
||||
|
||||
# Actions - step/apply/etc, do the thing and return state
|
||||
if request.method == 'POST':
|
||||
@@ -121,11 +131,21 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
this_session.call_action(action_name=step_operation,
|
||||
selector=step_selector,
|
||||
optional_value=step_optional_value)
|
||||
except playwright._impl._api_types.TimeoutError as e:
|
||||
print("Element wasnt found :-(", step_operation)
|
||||
return make_response("Element was not found on page", 401)
|
||||
|
||||
except playwright._impl._api_types.Error as e:
|
||||
# Browser/playwright level error
|
||||
print("Browser error - got playwright._impl._api_types.Error, try reloading the session/browser")
|
||||
print (str(e))
|
||||
|
||||
except Exception as e:
|
||||
print("Exception when calling step operation", step_operation, str(e))
|
||||
# Try to find something of value to give back to the user
|
||||
return make_response(str(e).splitlines()[0], 401)
|
||||
for l in str(e).splitlines():
|
||||
if 'DOMException' in l:
|
||||
return make_response(l, 401)
|
||||
|
||||
return make_response('Browser session ran out of time :( Please reload this page.', 401)
|
||||
|
||||
# Get visual selector ready/update its data (also use the current filter info from the page?)
|
||||
# When the last 'apply' button was pressed
|
||||
@@ -142,11 +162,10 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
if not browsersteps_playwright_browser_interface:
|
||||
print("Starting connection with playwright")
|
||||
logging.debug("browser_steps.py connecting")
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
browsersteps_playwright_browser_interface = sync_playwright().start()
|
||||
|
||||
global browsersteps_playwright_browser_interface_context
|
||||
from . import nonContext
|
||||
browsersteps_playwright_browser_interface_context = nonContext.c_sync_playwright()
|
||||
browsersteps_playwright_browser_interface = browsersteps_playwright_browser_interface_context.start()
|
||||
|
||||
time.sleep(1)
|
||||
# At 20 minutes, some other variable is closing it
|
||||
@@ -186,45 +205,21 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
cleanup_playwright_session()
|
||||
return make_response('Browser session ran out of time :( Please reload this page.', 401)
|
||||
|
||||
response = None
|
||||
try:
|
||||
state = this_session.get_current_state()
|
||||
except playwright._impl._api_types.Error as e:
|
||||
return make_response("Browser session ran out of time :( Please reload this page."+str(e), 401)
|
||||
|
||||
if request.method == 'POST':
|
||||
# Screenshots and other info only needed on requesting a step (POST)
|
||||
try:
|
||||
state = this_session.get_current_state()
|
||||
except playwright._impl._api_types.Error as e:
|
||||
return make_response("Browser session ran out of time :( Please reload this page."+str(e), 401)
|
||||
p = {'screenshot': "data:image/png;base64,{}".format(
|
||||
base64.b64encode(state[0]).decode('ascii')),
|
||||
'xpath_data': state[1],
|
||||
'session_age_start': this_session.age_start,
|
||||
'browser_time_remaining': round(remaining)
|
||||
}
|
||||
|
||||
# Use send_file() which is way faster than read/write loop on bytes
|
||||
import json
|
||||
from tempfile import mkstemp
|
||||
from flask import send_file
|
||||
tmp_fd, tmp_file = mkstemp(text=True, suffix=".json", prefix="changedetectionio-")
|
||||
|
||||
output = json.dumps({'screenshot': "data:image/jpeg;base64,{}".format(
|
||||
base64.b64encode(state[0]).decode('ascii')),
|
||||
'xpath_data': state[1],
|
||||
'session_age_start': this_session.age_start,
|
||||
'browser_time_remaining': round(remaining)
|
||||
})
|
||||
|
||||
with os.fdopen(tmp_fd, 'w') as f:
|
||||
f.write(output)
|
||||
|
||||
response = make_response(send_file(path_or_file=tmp_file,
|
||||
mimetype='application/json; charset=UTF-8',
|
||||
etag=True))
|
||||
# No longer needed
|
||||
os.unlink(tmp_file)
|
||||
|
||||
elif request.method == 'GET':
|
||||
# Just enough to get the session rolling, it will call for goto-site via POST next
|
||||
response = make_response({
|
||||
'session_age_start': this_session.age_start,
|
||||
'browser_time_remaining': round(remaining)
|
||||
})
|
||||
|
||||
return response
|
||||
# @todo BSON/binary JSON, faster xfer, OR pick it off the disk
|
||||
return p
|
||||
|
||||
return browser_steps_blueprint
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ browser_step_ui_config = {'Choose one': '0 0',
|
||||
'Click element': '1 0',
|
||||
'Click element containing text': '0 1',
|
||||
'Enter text in field': '1 1',
|
||||
'Execute JS': '0 1',
|
||||
# 'Extract text and use as filter': '1 0',
|
||||
'Goto site': '0 0',
|
||||
'Press Enter': '0 0',
|
||||
@@ -90,7 +89,7 @@ class steppable_browser_interface():
|
||||
return
|
||||
elem = self.page.get_by_text(value)
|
||||
if elem.count():
|
||||
elem.first.click(delay=randint(200, 500), timeout=3000)
|
||||
elem.first.click(delay=randint(200, 500))
|
||||
|
||||
def action_enter_text_in_field(self, selector, value):
|
||||
if not len(selector.strip()):
|
||||
@@ -98,9 +97,6 @@ class steppable_browser_interface():
|
||||
|
||||
self.page.fill(selector, value, timeout=10 * 1000)
|
||||
|
||||
def action_execute_js(self, selector, value):
|
||||
self.page.evaluate(value)
|
||||
|
||||
def action_click_element(self, selector, value):
|
||||
print("Clicking element")
|
||||
if not len(selector.strip()):
|
||||
@@ -146,10 +142,10 @@ class steppable_browser_interface():
|
||||
self.page.keyboard.press("PageDown", delay=randint(200, 500))
|
||||
|
||||
def action_check_checkbox(self, selector, value):
|
||||
self.page.locator(selector).check(timeout=1000)
|
||||
self.page.locator(selector).check()
|
||||
|
||||
def action_uncheck_checkbox(self, selector, value):
|
||||
self.page.locator(selector, timeout=1000).uncheck(timeout=1000)
|
||||
self.page.locator(selector).uncheck()
|
||||
|
||||
|
||||
# Responsible for maintaining a live 'context' with browserless
|
||||
@@ -211,7 +207,7 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
# Listen for all console events and handle errors
|
||||
self.page.on("console", lambda msg: print(f"Browser steps console - {msg.type}: {msg.text} {msg.args}"))
|
||||
|
||||
print("Time to browser setup", time.time() - now)
|
||||
print("time to browser setup", time.time() - now)
|
||||
self.page.wait_for_timeout(1 * 1000)
|
||||
|
||||
def mark_as_closed(self):
|
||||
@@ -236,7 +232,7 @@ class browsersteps_live_ui(steppable_browser_interface):
|
||||
self.page.evaluate("var include_filters=''")
|
||||
# Go find the interactive elements
|
||||
# @todo in the future, something smarter that can scan for elements with .click/focus etc event handlers?
|
||||
elements = 'a,button,input,select,textarea,i,th,td,p,li,h1,h2,h3,h4,div,span'
|
||||
elements = 'a,button,input,select,textarea,i,th,td,p,li,h1,h2,h3,h4'
|
||||
xpath_element_js = xpath_element_js.replace('%ELEMENTS%', elements)
|
||||
xpath_data = self.page.evaluate("async () => {" + xpath_element_js + "}")
|
||||
# So the JS will find the smallest one first
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
from playwright.sync_api import PlaywrightContextManager
|
||||
import asyncio
|
||||
|
||||
# So playwright wants to run as a context manager, but we do something horrible and hacky
|
||||
# we are holding the session open for as long as possible, then shutting it down, and opening a new one
|
||||
# So it means we don't get to use PlaywrightContextManager' __enter__ __exit__
|
||||
# To work around this, make goodbye() act the same as the __exit__()
|
||||
#
|
||||
# But actually I think this is because the context is opened correctly with __enter__() but we timeout the connection
|
||||
# then theres some lock condition where we cant destroy it without it hanging
|
||||
|
||||
class c_PlaywrightContextManager(PlaywrightContextManager):
|
||||
|
||||
def goodbye(self) -> None:
|
||||
self.__exit__()
|
||||
|
||||
def c_sync_playwright() -> PlaywrightContextManager:
|
||||
return c_PlaywrightContextManager()
|
||||
24
changedetectionio/blueprint/extract/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
from distutils.util import strtobool
|
||||
from flask import Blueprint, request, make_response
|
||||
from flask_login import login_required
|
||||
import os
|
||||
import logging
|
||||
from changedetectionio.store import ChangeDetectionStore
|
||||
|
||||
|
||||
|
||||
def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
|
||||
browser_steps_blueprint = Blueprint('browser_steps', __name__, template_folder="templates")
|
||||
|
||||
@login_required
|
||||
@browser_steps_blueprint.route("/extract-regex", methods=['POST'])
|
||||
def browsersteps_ui_update():
|
||||
import time
|
||||
|
||||
return {123123123: 'yup'}
|
||||
|
||||
return browser_steps_blueprint
|
||||
|
||||
|
||||
@@ -207,9 +207,9 @@ class ValidateTokensList(object):
|
||||
if not p.strip('{}') in notification.valid_tokens:
|
||||
message = field.gettext('Token \'%s\' is not a valid token.')
|
||||
raise ValidationError(message % (p))
|
||||
|
||||
|
||||
class validateURL(object):
|
||||
|
||||
|
||||
"""
|
||||
Flask wtform validators wont work with basic auth
|
||||
"""
|
||||
@@ -400,15 +400,6 @@ class watchForm(commonSettingsForm):
|
||||
self.body.errors.append('Body must be empty when Request Method is set to GET')
|
||||
result = False
|
||||
|
||||
# Attempt to validate jinja2 templates in the URL
|
||||
from jinja2 import Environment
|
||||
# Jinja2 available in URLs along with https://pypi.org/project/jinja2-time/
|
||||
jinja2_env = Environment(extensions=['jinja2_time.TimeExtension'])
|
||||
try:
|
||||
ready_url = str(jinja2_env.from_string(self.url.data).render())
|
||||
except Exception as e:
|
||||
self.url.errors.append('Invalid template syntax')
|
||||
result = False
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ class model(dict):
|
||||
'base_url' : None,
|
||||
'extract_title_as_title': False,
|
||||
'empty_pages_are_a_change': False,
|
||||
'css_dark_mode': False,
|
||||
'fetch_backend': getenv("DEFAULT_FETCH_BACKEND", "html_requests"),
|
||||
'filter_failure_notification_threshold_attempts': _FILTER_FAILURE_THRESHOLD_ATTEMPTS_DEFAULT,
|
||||
'global_ignore_text': [], # List of text to ignore when calculating the comparison checksum
|
||||
|
||||
@@ -93,23 +93,12 @@ class model(dict):
|
||||
@property
|
||||
def link(self):
|
||||
url = self.get('url', '')
|
||||
ready_url = url
|
||||
if '{%' in url or '{{' in url:
|
||||
from jinja2 import Environment
|
||||
# Jinja2 available in URLs along with https://pypi.org/project/jinja2-time/
|
||||
jinja2_env = Environment(extensions=['jinja2_time.TimeExtension'])
|
||||
try:
|
||||
ready_url = str(jinja2_env.from_string(url).render())
|
||||
except Exception as e:
|
||||
from flask import (
|
||||
flash, Markup, url_for
|
||||
)
|
||||
message = Markup('<a href="{}#general">The URL {} is invalid and cannot be used, click to edit</a>'.format(
|
||||
url_for('edit_page', uuid=self.get('uuid')), self.get('url', '')))
|
||||
flash(message, 'error')
|
||||
return ''
|
||||
|
||||
return ready_url
|
||||
return str(jinja2_env.from_string(url).render())
|
||||
return url
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
// @file Scrape the page looking for elements of concern (%ELEMENTS%)
|
||||
// http://matatk.agrip.org.uk/tests/position-and-width/
|
||||
// https://stackoverflow.com/questions/26813480/when-is-element-getboundingclientrect-guaranteed-to-be-updated-accurate
|
||||
//
|
||||
// Some pages like https://www.londonstockexchange.com/stock/NCCL/ncondezi-energy-limited/analysis
|
||||
// will automatically force a scroll somewhere, so include the position offset
|
||||
// Lets hope the position doesnt change while we iterate the bbox's, but this is better than nothing
|
||||
|
||||
var scroll_y=+document.documentElement.scrollTop || document.body.scrollTop
|
||||
|
||||
// Include the getXpath script directly, easier than fetching
|
||||
function getxpath(e) {
|
||||
var n = e;
|
||||
@@ -81,13 +71,8 @@ var bbox;
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
bbox = elements[i].getBoundingClientRect();
|
||||
|
||||
// Forget really small ones
|
||||
if (bbox['width'] < 10 && bbox['height'] < 10) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't include elements that are offset from canvas
|
||||
if (bbox['top']+scroll_y < 0 || bbox['left'] < 0) {
|
||||
// forget really small ones
|
||||
if (bbox['width'] < 15 && bbox['height'] < 15) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -124,16 +109,14 @@ for (var i = 0; i < elements.length; i++) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// @todo Possible to ONLY list where it's clickable to save JSON xfer size
|
||||
size_pos.push({
|
||||
xpath: xpath_result,
|
||||
width: Math.round(bbox['width']),
|
||||
height: Math.round(bbox['height']),
|
||||
left: Math.floor(bbox['left']),
|
||||
top: Math.floor(bbox['top'])+scroll_y,
|
||||
top: Math.floor(bbox['top']),
|
||||
tagName: (elements[i].tagName) ? elements[i].tagName.toLowerCase() : '',
|
||||
tagtype: (elements[i].tagName == 'INPUT' && elements[i].type) ? elements[i].type.toLowerCase() : '',
|
||||
isClickable: (elements[i].onclick) || window.getComputedStyle(elements[i]).cursor == "pointer"
|
||||
tagtype: (elements[i].tagName == 'INPUT' && elements[i].type) ? elements[i].type.toLowerCase() : ''
|
||||
});
|
||||
|
||||
}
|
||||
@@ -167,7 +150,6 @@ if (include_filters.length) {
|
||||
|
||||
if (q) {
|
||||
bbox = q.getBoundingClientRect();
|
||||
console.log("xpath_element_scraper: Got filter element, scroll from top was "+scroll_y)
|
||||
} else {
|
||||
console.log("xpath_element_scraper: filter element "+f+" was not found");
|
||||
}
|
||||
@@ -175,10 +157,10 @@ if (include_filters.length) {
|
||||
if (bbox && bbox['width'] > 0 && bbox['height'] > 0) {
|
||||
size_pos.push({
|
||||
xpath: f,
|
||||
width: parseInt(bbox['width']),
|
||||
height: parseInt(bbox['height']),
|
||||
left: parseInt(bbox['left']),
|
||||
top: parseInt(bbox['top'])+scroll_y
|
||||
width: Math.round(bbox['width']),
|
||||
height: Math.round(bbox['height']),
|
||||
left: Math.floor(bbox['left']),
|
||||
top: Math.floor(bbox['top'])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="favicons/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="256.000000pt" height="256.000000pt" viewBox="0 0 256.000000 256.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,256.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M0 1280 l0 -1280 1280 0 1280 0 0 1280 0 1280 -1280 0 -1280 0 0
|
||||
-1280z m1555 936 c387 -112 675 -426 741 -810 24 -138 15 -352 -20 -470 -106
|
||||
-353 -360 -606 -713 -712 -75 -22 -113 -27 -253 -31 -144 -5 -176 -2 -252 16
|
||||
-316 75 -564 271 -707 557 -67 136 -92 237 -98 401 -7 164 5 253 47 378 106
|
||||
315 349 556 665 659 114 37 180 45 350 41 125 -2 165 -7 240 -29z"/>
|
||||
<path d="M1091 2165 c-364 -82 -629 -328 -738 -682 -24 -80 -27 -103 -27 -258
|
||||
-1 -146 2 -182 21 -251 74 -271 259 -497 508 -621 477 -238 1061 -35 1294 450
|
||||
61 126 83 220 88 379 7 194 -15 307 -93 461 -126 251 -340 428 -614 507 -99
|
||||
29 -343 37 -439 15z m829 -473 c55 -54 100 -106 100 -116 0 -21 -184 -213
|
||||
-212 -222 -24 -7 -48 12 -48 38 0 11 26 47 58 80 l57 60 -151 -3 c-145 -4
|
||||
-152 -5 -190 -31 -22 -15 -78 -73 -124 -128 l-85 -99 -32 31 -32 31 30 38 c17
|
||||
22 70 79 117 128 66 67 97 92 127 100 22 6 106 11 188 11 81 0 147 3 147 8 0
|
||||
4 -25 31 -55 61 -55 55 -65 77 -43 99 25 25 50 10 148 -86z m-1002 -101 c46
|
||||
-24 141 -121 312 -321 203 -236 290 -330 322 -346 22 -11 60 -14 169 -12 l141
|
||||
3 -51 58 c-28 32 -51 64 -51 71 0 18 21 36 43 36 24 0 217 -193 217 -217 0
|
||||
-19 -185 -210 -212 -219 -24 -7 -48 12 -48 38 0 10 23 43 50 72 l50 53 -52 7
|
||||
c-29 3 -93 6 -142 6 -104 0 -152 12 -200 52 -19 15 -135 144 -258 286 -274
|
||||
316 -305 347 -354 361 -22 6 -94 11 -161 11 -67 0 -128 3 -137 6 -22 9 -21 61
|
||||
2 67 9 3 86 5 170 6 133 1 158 -2 190 -18z m227 -468 c23 -34 17 -43 -103
|
||||
-172 -119 -128 -131 -133 -343 -129 l-154 3 0 35 c0 34 1 35 50 42 28 3 96 7
|
||||
153 7 64 1 115 6 136 15 20 8 71 56 127 120 52 58 99 106 105 106 7 0 20 -12
|
||||
29 -27z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -1,4 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="15" height="16.363636" viewBox="0 0 15 16.363636" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<path d="m 14.318182,11.762045 v 1.1925 H 5.4102273 L 11.849318,7.1140909 C 12.234545,9.1561364 12.54,11.181818 14.318182,11.762045 Z m -6.7984093,4.601591 c 1.0759091,0 2.0256823,-0.955909 2.0256823,-2.045454 H 5.4545455 c 0,1.089545 0.9879545,2.045454 2.0652272,2.045454 z M 15,2.8622727 0.9177273,15.636136 0,14.627045 l 1.8443182,-1.6725 h -1.1625 v -1.1925 C 4.0070455,10.677273 2.1784091,4.5388636 5.3611364,2.6897727 5.8009091,2.4347727 6.0709091,1.9609091 6.0702273,1.4488636 v -0.00205 C 6.0702273,0.64772727 6.7104545,0 7.5,0 8.2895455,0 8.9297727,0.64772727 8.9297727,1.4468182 v 0.00205 C 8.9290909,1.9602319 9.199773,2.4354591 9.638864,2.6897773 10.364318,3.111141 10.827273,3.7568228 11.1525,4.5129591 L 14.085682,1.8531818 Z M 6.8181818,1.3636364 C 6.8181818,1.74 7.1236364,2.0454545 7.5,2.0454545 7.8763636,2.0454545 8.1818182,1.74 8.1818182,1.3636364 8.1818182,0.98795455 7.8763636,0.68181818 7.5,0.68181818 c -0.3763636,0 -0.6818182,0.30613637 -0.6818182,0.68181822 z" id="path2" style="fill:#f8321b;stroke-width:0.681818;fill-opacity:1"/>
|
||||
<svg
|
||||
width="15"
|
||||
height="16.363636"
|
||||
viewBox="0 0 15 16.363636"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="bell-off.svg"
|
||||
inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview5"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="28.416667"
|
||||
inkscape:cx="-0.59824046"
|
||||
inkscape:cy="12"
|
||||
inkscape:window-width="1554"
|
||||
inkscape:window-height="896"
|
||||
inkscape:window-x="2095"
|
||||
inkscape:window-y="107"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<defs
|
||||
id="defs8" />
|
||||
<path
|
||||
d="m 14.318182,11.762045 v 1.1925 H 5.4102273 L 11.849318,7.1140909 C 12.234545,9.1561364 12.54,11.181818 14.318182,11.762045 Z m -6.7984093,4.601591 c 1.0759091,0 2.0256823,-0.955909 2.0256823,-2.045454 H 5.4545455 c 0,1.089545 0.9879545,2.045454 2.0652272,2.045454 z M 15,2.8622727 0.9177273,15.636136 0,14.627045 l 1.8443182,-1.6725 h -1.1625 v -1.1925 C 4.0070455,10.677273 2.1784091,4.5388636 5.3611364,2.6897727 5.8009091,2.4347727 6.0709091,1.9609091 6.0702273,1.4488636 v -0.00205 C 6.0702273,0.64772727 6.7104545,0 7.5,0 8.2895455,0 8.9297727,0.64772727 8.9297727,1.4468182 v 0.00205 C 8.9290909,1.9602319 9.199773,2.4354591 9.638864,2.6897773 10.364318,3.111141 10.827273,3.7568228 11.1525,4.5129591 L 14.085682,1.8531818 Z M 6.8181818,1.3636364 C 6.8181818,1.74 7.1236364,2.0454545 7.5,2.0454545 7.8763636,2.0454545 8.1818182,1.74 8.1818182,1.3636364 8.1818182,0.98795455 7.8763636,0.68181818 7.5,0.68181818 c -0.3763636,0 -0.6818182,0.30613637 -0.6818182,0.68181822 z"
|
||||
id="path2"
|
||||
style="fill:#f8321b;stroke-width:0.681818;fill-opacity:1" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
changedetectionio/static/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 31 KiB |
@@ -1,5 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="18" height="19.92" viewBox="0 0 18 19.92" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<path d="M -3,-2 H 21 V 22 H -3 Z" fill="none" id="path2"/>
|
||||
<path d="m 15,14.08 c -0.76,0 -1.44,0.3 -1.96,0.77 L 5.91,10.7 C 5.96,10.47 6,10.24 6,10 6,9.76 5.96,9.53 5.91,9.3 L 12.96,5.19 C 13.5,5.69 14.21,6 15,6 16.66,6 18,4.66 18,3 18,1.34 16.66,0 15,0 c -1.66,0 -3,1.34 -3,3 0,0.24 0.04,0.47 0.09,0.7 L 5.04,7.81 C 4.5,7.31 3.79,7 3,7 1.34,7 0,8.34 0,10 c 0,1.66 1.34,3 3,3 0.79,0 1.5,-0.31 2.04,-0.81 l 7.12,4.16 c -0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92 0,-1.61 -1.31,-2.92 -2.92,-2.92 z" id="path4" style="fill:#0078e7;fill-opacity:1"/>
|
||||
<svg
|
||||
width="18"
|
||||
height="19.92"
|
||||
viewBox="0 0 18 19.92"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="spread.svg"
|
||||
inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="28.416667"
|
||||
inkscape:cx="9.0087975"
|
||||
inkscape:cy="9.9941348"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1056"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6" />
|
||||
<path
|
||||
d="M -3,-2 H 21 V 22 H -3 Z"
|
||||
fill="none"
|
||||
id="path2" />
|
||||
<path
|
||||
d="m 15,14.08 c -0.76,0 -1.44,0.3 -1.96,0.77 L 5.91,10.7 C 5.96,10.47 6,10.24 6,10 6,9.76 5.96,9.53 5.91,9.3 L 12.96,5.19 C 13.5,5.69 14.21,6 15,6 16.66,6 18,4.66 18,3 18,1.34 16.66,0 15,0 c -1.66,0 -3,1.34 -3,3 0,0.24 0.04,0.47 0.09,0.7 L 5.04,7.81 C 4.5,7.31 3.79,7 3,7 1.34,7 0,8.34 0,10 c 0,1.66 1.34,3 3,3 0.79,0 1.5,-0.31 2.04,-0.81 l 7.12,4.16 c -0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92 0,-1.61 -1.31,-2.92 -2.92,-2.92 z"
|
||||
id="path4"
|
||||
style="fill:#0078e7;fill-opacity:1" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 787 B After Width: | Height: | Size: 1.7 KiB |
@@ -10,10 +10,10 @@ $(document).ready(function () {
|
||||
}
|
||||
})
|
||||
var browsersteps_session_id;
|
||||
var browserless_seconds_remaining = 0;
|
||||
var browserless_seconds_remaining=0;
|
||||
var apply_buttons_disabled = false;
|
||||
var include_text_elements = $("#include_text_elements");
|
||||
var xpath_data = false;
|
||||
var xpath_data;
|
||||
var current_selected_i;
|
||||
var state_clicked = false;
|
||||
var c;
|
||||
@@ -25,42 +25,11 @@ $(document).ready(function () {
|
||||
$(window).resize(function () {
|
||||
set_scale();
|
||||
});
|
||||
// Should always be disabled
|
||||
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled');
|
||||
|
||||
$('#browsersteps-click-start').click(function () {
|
||||
$("#browsersteps-click-start").fadeOut();
|
||||
$("#browsersteps-selector-wrapper .spinner").fadeIn();
|
||||
start();
|
||||
});
|
||||
|
||||
$('a#browsersteps-tab').click(function () {
|
||||
reset();
|
||||
start();
|
||||
});
|
||||
|
||||
window.addEventListener('hashchange', function () {
|
||||
if (window.location.hash == '#browser-steps') {
|
||||
reset();
|
||||
}
|
||||
});
|
||||
|
||||
function reset() {
|
||||
xpath_data = false;
|
||||
$('#browsersteps-img').removeAttr('src');
|
||||
$("#browsersteps-click-start").show();
|
||||
$("#browsersteps-selector-wrapper .spinner").hide();
|
||||
browserless_seconds_remaining = 0;
|
||||
browsersteps_session_id = false;
|
||||
apply_buttons_disabled = false;
|
||||
ctx.clearRect(0, 0, c.width, c.height);
|
||||
set_first_gotosite_disabled();
|
||||
}
|
||||
|
||||
function set_first_gotosite_disabled() {
|
||||
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled');
|
||||
$('#browser_steps >li:first-child').css('opacity', '0.5');
|
||||
}
|
||||
|
||||
// Show seconds remaining until playwright/browserless needs to restart the session
|
||||
// (See comment at the top of changedetectionio/blueprint/browser_steps/__init__.py )
|
||||
setInterval(() => {
|
||||
@@ -71,6 +40,21 @@ $(document).ready(function () {
|
||||
}, "1000")
|
||||
|
||||
|
||||
if (window.location.hash == '#browser-steps') {
|
||||
start();
|
||||
}
|
||||
|
||||
window.addEventListener('hashchange', function () {
|
||||
if (window.location.hash == '#browser-steps') {
|
||||
start();
|
||||
}
|
||||
// For when the page loads
|
||||
if (!window.location.hash || window.location.hash != '#browser-steps') {
|
||||
$("img#browsersteps-img").attr('src', '');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
function set_scale() {
|
||||
|
||||
// some things to check if the scaling doesnt work
|
||||
@@ -103,6 +87,7 @@ $(document).ready(function () {
|
||||
// @todo is click better?
|
||||
$('#browsersteps-selector-canvas').off("mousemove mousedown click");
|
||||
// Undo disable_browsersteps_ui
|
||||
$("#browser_steps select,input").removeAttr('disabled').css('opacity', '1.0');
|
||||
$("#browser-steps-ui").css('opacity', '1.0');
|
||||
|
||||
// init
|
||||
@@ -118,7 +103,7 @@ $(document).ready(function () {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
|
||||
e.preventDefault()
|
||||
console.log(e);
|
||||
console.log("current xpath in index is " + current_selected_i);
|
||||
console.log("current xpath in index is "+current_selected_i);
|
||||
last_click_xy = {'x': parseInt((1 / x_scale) * e.offsetX), 'y': parseInt((1 / y_scale) * e.offsetY)}
|
||||
process_selected(current_selected_i);
|
||||
current_selected_i = false;
|
||||
@@ -133,10 +118,6 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$('#browsersteps-selector-canvas').bind('mousemove', function (e) {
|
||||
if (!xpath_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// checkbox if find elements is enabled
|
||||
ctx.clearRect(0, 0, c.width, c.height);
|
||||
ctx.fillStyle = 'rgba(255,0,0, 0.1)';
|
||||
@@ -172,8 +153,8 @@ $(document).ready(function () {
|
||||
// does it mean sort the xpath list by size (w*h) i think so!
|
||||
} else {
|
||||
|
||||
if (include_text_elements[0].checked === true) {
|
||||
// blue one with background instead?
|
||||
if ( include_text_elements[0].checked === true) {
|
||||
// blue one with background instead?
|
||||
ctx.fillStyle = 'rgba(0,0,255, 0.1)';
|
||||
ctx.strokeStyle = 'rgba(0,0,200, 0.7)';
|
||||
$('#browsersteps-selector-canvas').css('cursor', 'grab');
|
||||
@@ -194,6 +175,7 @@ $(document).ready(function () {
|
||||
// });
|
||||
|
||||
|
||||
|
||||
// callback for clicking on an xpath on the canvas
|
||||
function process_selected(xpath_data_index) {
|
||||
found_something = false;
|
||||
@@ -208,23 +190,25 @@ $(document).ready(function () {
|
||||
console.log(x);
|
||||
if (x && first_available.length) {
|
||||
// @todo will it let you click shit that has a layer ontop? probably not.
|
||||
if (x['tagtype'] === 'text' || x['tagtype'] === 'email' || x['tagName'] === 'textarea' || x['tagtype'] === 'password' || x['tagtype'] === 'search') {
|
||||
if (x['tagtype'] === 'text' || x['tagtype'] === 'email' || x['tagName'] === 'textarea' || x['tagtype'] === 'password' || x['tagtype'] === 'search' ) {
|
||||
$('select', first_available).val('Enter text in field').change();
|
||||
$('input[type=text]', first_available).first().val(x['xpath']);
|
||||
$('input[placeholder="Value"]', first_available).addClass('ok').click().focus();
|
||||
found_something = true;
|
||||
} else {
|
||||
if (x['isClickable'] || x['tagName'].startsWith('h') || x['tagName'] === 'a' || x['tagName'] === 'button' || x['tagtype'] === 'submit' || x['tagtype'] === 'checkbox' || x['tagtype'] === 'radio' || x['tagtype'] === 'li') {
|
||||
// Assume it's just for clicking on
|
||||
// what are we clicking on?
|
||||
if (x['tagName'].startsWith('h')|| x['tagName'] === 'a' || x['tagName'] === 'button' || x['tagtype'] === 'submit'|| x['tagtype'] === 'checkbox'|| x['tagtype'] === 'radio'|| x['tagtype'] === 'li') {
|
||||
$('select', first_available).val('Click element').change();
|
||||
$('input[type=text]', first_available).first().val(x['xpath']);
|
||||
found_something = true;
|
||||
}
|
||||
}
|
||||
|
||||
first_available.xpath_data_index = xpath_data_index;
|
||||
first_available.xpath_data_index=xpath_data_index;
|
||||
|
||||
if (!found_something) {
|
||||
if (include_text_elements[0].checked === true) {
|
||||
if ( include_text_elements[0].checked === true) {
|
||||
// Suggest that we use as filter?
|
||||
// @todo filters should always be in the last steps, nothing non-filter after it
|
||||
found_something = true;
|
||||
@@ -236,6 +220,8 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,15 +234,15 @@ $(document).ready(function () {
|
||||
|
||||
function start() {
|
||||
console.log("Starting browser-steps UI");
|
||||
browsersteps_session_id = Date.now();
|
||||
browsersteps_session_id=Date.now();
|
||||
// @todo This setting of the first one should be done at the datalayer but wtforms doesnt wanna play nice
|
||||
$('#browser_steps >li:first-child').removeClass('empty');
|
||||
set_first_gotosite_disabled();
|
||||
$('#browser-steps-ui .loader .spinner').show();
|
||||
$('#browser_steps >li:first-child select').val('Goto site').attr('disabled', 'disabled');
|
||||
$('#browser-steps-ui .loader').show();
|
||||
$('.clear,.remove', $('#browser_steps >li:first-child')).hide();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: browser_steps_sync_url + "&browsersteps_session_id=" + browsersteps_session_id,
|
||||
url: browser_steps_sync_url+"&browsersteps_session_id="+browsersteps_session_id,
|
||||
statusCode: {
|
||||
400: function () {
|
||||
// More than likely the CSRF token was lost when the server restarted
|
||||
@@ -265,12 +251,10 @@ $(document).ready(function () {
|
||||
}
|
||||
}).done(function (data) {
|
||||
xpath_data = data.xpath_data;
|
||||
$("#loading-status-text").fadeIn();
|
||||
$('#browsersteps-img').attr('src', data.screenshot);
|
||||
// This should trigger 'Goto site'
|
||||
console.log("Got startup response, requesting Goto-Site (first) step fake click");
|
||||
$('#browser_steps >li:first-child .apply').click();
|
||||
browserless_seconds_remaining = data.browser_time_remaining;
|
||||
set_first_gotosite_disabled();
|
||||
}).fail(function (data) {
|
||||
console.log(data);
|
||||
alert('There was an error communicating with the server.');
|
||||
@@ -279,7 +263,7 @@ $(document).ready(function () {
|
||||
}
|
||||
|
||||
function disable_browsersteps_ui() {
|
||||
set_first_gotosite_disabled();
|
||||
$("#browser_steps select,input").attr('disabled', 'disabled').css('opacity', '0.5');
|
||||
$("#browser-steps-ui").css('opacity', '0.3');
|
||||
$('#browsersteps-selector-canvas').off("mousemove mousedown click");
|
||||
}
|
||||
@@ -326,14 +310,11 @@ $(document).ready(function () {
|
||||
|
||||
// Add the extra buttons to the steps
|
||||
$('ul#browser_steps li').each(function (i) {
|
||||
var s = '<div class="control">' + '<a data-step-index=' + i + ' class="pure-button button-secondary button-green button-xsmall apply" >Apply</a> ';
|
||||
if (i > 0) {
|
||||
// The first step never gets these (Goto-site)
|
||||
s += '<a data-step-index=' + i + ' class="pure-button button-secondary button-xsmall clear" >Clear</a> ' +
|
||||
'<a data-step-index=' + i + ' class="pure-button button-secondary button-red button-xsmall remove" >Remove</a>';
|
||||
}
|
||||
s += '</div>';
|
||||
$(this).append(s)
|
||||
$(this).append('<div class="control">' +
|
||||
'<a data-step-index=' + i + ' class="pure-button button-secondary button-green button-xsmall apply" >Apply</a> ' +
|
||||
'<a data-step-index=' + i + ' class="pure-button button-secondary button-xsmall clear" >Clear</a> ' +
|
||||
'<a data-step-index=' + i + ' class="pure-button button-secondary button-red button-xsmall remove" >Remove</a>' +
|
||||
'</div>')
|
||||
}
|
||||
);
|
||||
|
||||
@@ -370,15 +351,15 @@ $(document).ready(function () {
|
||||
|
||||
$('ul#browser_steps li .control .apply').click(function (event) {
|
||||
// sequential requests @todo refactor
|
||||
if (apply_buttons_disabled) {
|
||||
if(apply_buttons_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var current_data = $(event.currentTarget).closest('li');
|
||||
$('#browser-steps-ui .loader .spinner').fadeIn();
|
||||
apply_buttons_disabled = true;
|
||||
$('ul#browser_steps li .control .apply').css('opacity', 0.5);
|
||||
$("#browsersteps-img").css('opacity', 0.65);
|
||||
$('#browser-steps-ui .loader').fadeIn();
|
||||
apply_buttons_disabled=true;
|
||||
$('ul#browser_steps li .control .apply').css('opacity',0.5);
|
||||
$("#browsersteps-img").css('opacity',0.65);
|
||||
|
||||
var is_last_step = 0;
|
||||
var step_n = $(event.currentTarget).data('step-index');
|
||||
@@ -390,17 +371,17 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
if (is_last_step == (step_n + 1)) {
|
||||
if (is_last_step == (step_n+1)) {
|
||||
is_last_step = true;
|
||||
} else {
|
||||
is_last_step = false;
|
||||
}
|
||||
|
||||
console.log("Requesting step via POST " + $("select[id$='operation']", current_data).first().val());
|
||||
|
||||
// POST the currently clicked step form widget back and await response, redraw
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: browser_steps_sync_url + "&browsersteps_session_id=" + browsersteps_session_id,
|
||||
url: browser_steps_sync_url+"&browsersteps_session_id="+browsersteps_session_id,
|
||||
data: {
|
||||
'operation': $("select[id$='operation']", current_data).first().val(),
|
||||
'selector': $("input[id$='selector']", current_data).first().val(),
|
||||
@@ -412,36 +393,26 @@ $(document).ready(function () {
|
||||
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.");
|
||||
$("#loading-status-text").hide();
|
||||
$('#browser-steps-ui .loader .spinner').fadeOut();
|
||||
},
|
||||
401: function (data) {
|
||||
// More than likely the CSRF token was lost when the server restarted
|
||||
alert(data.responseText);
|
||||
$("#loading-status-text").hide();
|
||||
$('#browser-steps-ui .loader .spinner').fadeOut();
|
||||
}
|
||||
}
|
||||
}).done(function (data) {
|
||||
// it should return the new state (selectors available and screenshot)
|
||||
xpath_data = data.xpath_data;
|
||||
$('#browsersteps-img').attr('src', data.screenshot);
|
||||
$('#browser-steps-ui .loader .spinner').fadeOut();
|
||||
apply_buttons_disabled = false;
|
||||
$("#browsersteps-img").css('opacity', 1);
|
||||
$('ul#browser_steps li .control .apply').css('opacity', 1);
|
||||
$('#browser-steps-ui .loader').fadeOut();
|
||||
apply_buttons_disabled=false;
|
||||
$("#browsersteps-img").css('opacity',1);
|
||||
$('ul#browser_steps li .control .apply').css('opacity',1);
|
||||
browserless_seconds_remaining = data.browser_time_remaining;
|
||||
$("#loading-status-text").hide();
|
||||
set_first_gotosite_disabled();
|
||||
}).fail(function (data) {
|
||||
console.log(data);
|
||||
if (data.responseText.includes("Browser session expired")) {
|
||||
disable_browsersteps_ui();
|
||||
}
|
||||
apply_buttons_disabled = false;
|
||||
$("#loading-status-text").hide();
|
||||
$('ul#browser_steps li .control .apply').css('opacity', 1);
|
||||
$("#browsersteps-img").css('opacity', 1);
|
||||
apply_buttons_disabled=false;
|
||||
$('ul#browser_steps li .control .apply').css('opacity',1);
|
||||
$("#browsersteps-img").css('opacity',1);
|
||||
//$('#browsersteps-selector-wrapper .loader').fadeOut(2500);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,110 +1,112 @@
|
||||
var a = document.getElementById("a");
|
||||
var b = document.getElementById("b");
|
||||
var result = document.getElementById("result");
|
||||
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,
|
||||
};
|
||||
// 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 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");
|
||||
node.appendChild(document.createTextNode(diff[i].value));
|
||||
|
||||
} else if (diff[i].added) {
|
||||
node = document.createElement('ins');
|
||||
node.classList.add("change");
|
||||
node.appendChild(document.createTextNode(diff[i].value));
|
||||
} else {
|
||||
node = document.createTextNode(diff[i].value);
|
||||
}
|
||||
fragment.appendChild(node);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
result.textContent = "";
|
||||
result.appendChild(fragment);
|
||||
|
||||
// Jump at start
|
||||
inputs.current = 0;
|
||||
next_diff();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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();
|
||||
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
a.onpaste = a.onchange =
|
||||
b.onpaste = b.onchange = changed;
|
||||
|
||||
if ("oninput" in a) {
|
||||
a.oninput = b.oninput = changed;
|
||||
if ('oninput' in a) {
|
||||
a.oninput = b.oninput = changed;
|
||||
} else {
|
||||
a.onkeyup = b.onkeyup = changed;
|
||||
a.onkeyup = b.onkeyup = changed;
|
||||
}
|
||||
|
||||
function onDiffTypeChange(radio) {
|
||||
window.diffType = radio.value;
|
||||
// Not necessary
|
||||
// document.title = "Diff " + radio.value.slice(4);
|
||||
window.diffType = radio.value;
|
||||
// Not necessary
|
||||
// document.title = "Diff " + radio.value.slice(4);
|
||||
}
|
||||
|
||||
var radio = document.getElementsByName("diff_type");
|
||||
var radio = document.getElementsByName('diff_type');
|
||||
for (var i = 0; i < radio.length; i++) {
|
||||
radio[i].onchange = function (e) {
|
||||
onDiffTypeChange(e.target);
|
||||
changed();
|
||||
};
|
||||
radio[i].onchange = function (e) {
|
||||
onDiffTypeChange(e.target);
|
||||
changed();
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("ignoreWhitespace").onchange = function (e) {
|
||||
changed();
|
||||
};
|
||||
document.getElementById('ignoreWhitespace').onchange = function (e) {
|
||||
changed();
|
||||
}
|
||||
|
||||
var inputs = document.getElementsByClassName("change");
|
||||
|
||||
var inputs = document.getElementsByClassName('change');
|
||||
inputs.current = 0;
|
||||
|
||||
|
||||
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",
|
||||
});
|
||||
var element = inputs[inputs.current];
|
||||
var headerOffset = 80;
|
||||
var elementPosition = element.getBoundingClientRect().top;
|
||||
var offsetPosition = elementPosition - headerOffset + window.scrollY;
|
||||
|
||||
inputs.current++;
|
||||
if (inputs.current >= inputs.length) {
|
||||
inputs.current = 0;
|
||||
}
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: "smooth"
|
||||
});
|
||||
|
||||
inputs.current++;
|
||||
if (inputs.current >= inputs.length) {
|
||||
inputs.current = 0;
|
||||
}
|
||||
}
|
||||
|
||||
40
changedetectionio/static/js/extract.js
Normal file
@@ -0,0 +1,40 @@
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#extract').click(function (e) {
|
||||
download_csv_file();
|
||||
});
|
||||
|
||||
//create CSV file data in an array
|
||||
var csvFileData = [
|
||||
['Alan Walker', 'Singer'],
|
||||
['Cristiano Ronaldo', 'Footballer'],
|
||||
['Saina Nehwal', 'Badminton Player'],
|
||||
['Arijit Singh', 'Singer'],
|
||||
['Terence Lewis', 'Dancer']
|
||||
];
|
||||
|
||||
//create a user-defined function to download CSV file
|
||||
function download_csv_file() {
|
||||
|
||||
//define the heading for each row of the data
|
||||
var csv = 'Name,Profession\n';
|
||||
|
||||
//merge the data with CSV
|
||||
csvFileData.forEach(function (row) {
|
||||
csv += row.join(',');
|
||||
csv += "\n";
|
||||
});
|
||||
|
||||
//display the created CSV data on the web browser
|
||||
document.write(csv);
|
||||
|
||||
|
||||
var hiddenElement = document.createElement('a');
|
||||
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
|
||||
hiddenElement.target = '_blank';
|
||||
//provide the name for the CSV file to be downloaded
|
||||
hiddenElement.download = 'Famous Personalities.csv';
|
||||
hiddenElement.click();
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* Toggles theme between light and dark mode.
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
const button = document.getElementsByClassName("toggle-theme")[0];
|
||||
|
||||
button.onclick = () => {
|
||||
const htmlElement = document.getElementsByTagName("html");
|
||||
const isDarkMode = htmlElement[0].dataset.darkmode === "true";
|
||||
htmlElement[0].dataset.darkmode = !isDarkMode;
|
||||
if (isDarkMode) {
|
||||
button.classList.remove("dark");
|
||||
setCookieValue(false);
|
||||
} else {
|
||||
button.classList.add("dark");
|
||||
setCookieValue(true);
|
||||
}
|
||||
};
|
||||
|
||||
const setCookieValue = (value) => {
|
||||
document.cookie = `css_dark_mode=${value};max-age=31536000`
|
||||
}
|
||||
});
|
||||
@@ -1,130 +1,5 @@
|
||||
/**
|
||||
* CSS custom properties (aka variables).
|
||||
*/
|
||||
:root {
|
||||
--color-white: #fff;
|
||||
--color-grey-50: #111;
|
||||
--color-grey-100: #262626;
|
||||
--color-grey-200: #333;
|
||||
--color-grey-300: #444;
|
||||
--color-grey-325: #555;
|
||||
--color-grey-350: #565d64;
|
||||
--color-grey-400: #666;
|
||||
--color-grey-500: #777;
|
||||
--color-grey-600: #999;
|
||||
--color-grey-700: #cbcbcb;
|
||||
--color-grey-750: #ddd;
|
||||
--color-grey-800: #e0e0e0;
|
||||
--color-grey-850: #eee;
|
||||
--color-grey-900: #f2f2f2;
|
||||
--color-black: #000;
|
||||
--color-background-page: var(--color-grey-100);
|
||||
--color-background-gradient-first: #5ad8f7;
|
||||
--color-background-gradient-second: #2f50af;
|
||||
--color-background-gradient-third: #9150bf;
|
||||
--color-background: var(--color-white);
|
||||
--color-text: var(--color-grey-200);
|
||||
--color-link: #1b98f8;
|
||||
--color-menu-accent: #ed5900;
|
||||
--color-background-code: var(--color-grey-850);
|
||||
--color-error: #a00;
|
||||
--color-error-input: #ffebeb;
|
||||
--color-error-list: #dd0000;
|
||||
--color-table-background: var(--color-background);
|
||||
--color-table-stripe: var(--color-grey-900);
|
||||
--color-text-tab: var(--color-white);
|
||||
--color-background-tab: rgba(255, 255, 255, 0.2);
|
||||
--color-background-tab-hover: rgba(255, 255, 255, 0.5);
|
||||
--color-text-tab-active: #222;
|
||||
--color-api-key: #0078e7;
|
||||
--color-background-button-primary: #0078e7;
|
||||
--color-background-button-green: #42dd53;
|
||||
--color-background-button-red: #dd4242;
|
||||
--color-background-button-success: rgb(28, 184, 65);
|
||||
--color-background-button-error: rgb(202, 60, 60);
|
||||
--color-text-button-error: var(--color-white);
|
||||
--color-background-button-warning: rgb(202, 60, 60);
|
||||
--color-text-button-warning: var(--color-white);
|
||||
--color-background-button-secondary: rgb(66, 184, 221);
|
||||
--color-background-button-cancel: rgb(200, 200, 200);
|
||||
--color-text-button: var(--color-white);
|
||||
--color-background-button-tag: rgb(99, 99, 99);
|
||||
--color-background-snapshot-age: #dfdfdf;
|
||||
--color-error-text-snapshot-age: var(--color-white);
|
||||
--color-error-background-snapshot-age: #ff0000;
|
||||
--color-background-button-tag-active: #9c9c9c;
|
||||
--color-text-messages: var(--color-white);
|
||||
--color-background-messages-message: rgba(255, 255, 255, .2);
|
||||
--color-background-messages-error: rgba(255, 1, 1, .5);
|
||||
--color-background-messages-notice: rgba(255, 255, 255, .5);
|
||||
--color-border-notification: #ccc;
|
||||
--color-background-checkbox-operations: rgba(0, 0, 0, 0.05);
|
||||
--color-warning: #ff3300;
|
||||
--color-border-warning: var(--color-warning);
|
||||
--color-text-legend: var(--color-white);
|
||||
--color-link-new-version: #e07171;
|
||||
--color-last-checked: #bbb;
|
||||
--color-text-footer: #444;
|
||||
--color-border-watch-table-cell: #eee;
|
||||
--color-text-watch-tag-list: #e70069;
|
||||
--color-background-new-watch-form: rgba(0, 0, 0, 0.05);
|
||||
--color-background-new-watch-input: var(--color-white);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
--color-border-input: var(--color-grey-500);
|
||||
--color-shadow-input: var(--color-grey-400);
|
||||
--color-background-input: var(--color-white);
|
||||
--color-text-input: var(--color-text);
|
||||
--color-text-input-description: var(--color-grey-500);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
--color-background-table-thead: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-700);
|
||||
--color-text-menu-heading: var(--color-grey-350);
|
||||
--color-text-menu-link: var(--color-grey-500);
|
||||
--color-background-menu-link-hover: var(--color-grey-850);
|
||||
--color-text-menu-link-hover: var(--color-grey-300);
|
||||
--color-shadow-jump: var(--color-grey-500);
|
||||
--color-icon-github: var(--color-black);
|
||||
--color-icon-github-hover: var(--color-grey-300); }
|
||||
|
||||
html[data-darkmode="true"] {
|
||||
--color-link: #59bdfb;
|
||||
--color-text: var(--color-white);
|
||||
--color-background-gradient-first: #3f90a5;
|
||||
--color-background-gradient-second: #1e316c;
|
||||
--color-background-gradient-third: #4d2c64;
|
||||
--color-background-new-watch-input: var(--color-grey-100);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
--color-background-table-thead: var(--color-grey-200);
|
||||
--color-table-background: var(--color-grey-300);
|
||||
--color-table-stripe: var(--color-grey-325);
|
||||
--color-background: var(--color-grey-300);
|
||||
--color-text-menu-heading: var(--color-grey-850);
|
||||
--color-text-menu-link: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-400);
|
||||
--color-text-tab-active: var(--color-text);
|
||||
--color-border-input: var(--color-grey-400);
|
||||
--color-shadow-input: var(--color-grey-50);
|
||||
--color-background-input: var(--color-grey-350);
|
||||
--color-text-input-description: var(--color-grey-600);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
--color-text-watch-tag-list: #fa3e92;
|
||||
--color-background-code: var(--color-grey-200);
|
||||
--color-background-tab: rgba(0, 0, 0, 0.2);
|
||||
--color-background-tab-hover: rgba(0, 0, 0, 0.5);
|
||||
--color-background-snapshot-age: var(--color-grey-200);
|
||||
--color-shadow-jump: var(--color-grey-200);
|
||||
--color-icon-github: var(--color-white);
|
||||
--color-icon-github-hover: var(--color-grey-700); }
|
||||
html[data-darkmode="true"] .watch-controls img {
|
||||
opacity: 0.4; }
|
||||
html[data-darkmode="true"] .icon-spread {
|
||||
filter: hue-rotate(-10deg) brightness(1.5); }
|
||||
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
||||
html[data-darkmode="true"] .watch-table .current-diff-url::after {
|
||||
filter: invert(0.5) hue-rotate(10deg) brightness(2); }
|
||||
|
||||
#diff-ui {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 2em;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
@@ -170,10 +45,6 @@ ins {
|
||||
margin-left: 1em;
|
||||
display: inline-block;
|
||||
font-weight: normal; }
|
||||
#settings del {
|
||||
padding: 0.5em; }
|
||||
#settings ins {
|
||||
padding: 0.5em; }
|
||||
|
||||
.source {
|
||||
position: absolute;
|
||||
|
||||
96
changedetectionio/static/styles/diff.scss
Normal file
@@ -0,0 +1,96 @@
|
||||
#diff-ui {
|
||||
|
||||
background: #fff;
|
||||
padding: 2em;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
border-radius: 5px;
|
||||
font-size: 11px;
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
td {
|
||||
padding: 3px 4px;
|
||||
border: 1px solid transparent;
|
||||
vertical-align: top;
|
||||
font: 1em monospace;
|
||||
text-align: left;
|
||||
}
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
h1 {
|
||||
display: inline;
|
||||
font-size: 100%;
|
||||
}
|
||||
del {
|
||||
text-decoration: none;
|
||||
color: #b30000;
|
||||
background: #fadad7;
|
||||
}
|
||||
|
||||
ins {
|
||||
background: #eaf2c2;
|
||||
color: #406619;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#result {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#settings {
|
||||
background: rgba(0,0,0,.05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
color: #fff;
|
||||
font-size: 80%;
|
||||
label {
|
||||
margin-left: 1em;
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.source {
|
||||
position: absolute;
|
||||
right: 1%;
|
||||
top: .2em;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
body {
|
||||
height: 99%; /* Hide scroll bar in Firefox */
|
||||
}
|
||||
}
|
||||
|
||||
td#diff-col div {
|
||||
text-align: justify;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.ignored {
|
||||
background-color: #ccc;
|
||||
/* border: #0d91fa 1px solid; */
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.triggered {
|
||||
background-color: #1b98f8;
|
||||
}
|
||||
|
||||
/* ignored and triggered? make it obvious error */
|
||||
.ignored.triggered {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
.tab-pane-inner#screenshot {
|
||||
text-align: center;
|
||||
img {
|
||||
max-width: 99%;
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"watch": "node-sass -w scss -o .",
|
||||
"build": "node-sass scss -o ."
|
||||
"build": "node-sass styles.scss -o .;node-sass diff.scss -o ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
}
|
||||
|
||||
li {
|
||||
&:not(:first-child) {
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
list-style: decimal;
|
||||
padding: 5px;
|
||||
.control {
|
||||
@@ -75,8 +70,6 @@
|
||||
transform: translate(-50%, -50%);
|
||||
margin-left: -40px;
|
||||
z-index: 100;
|
||||
max-width: 350px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* nice tall skinny one */
|
||||
@@ -85,11 +78,4 @@
|
||||
height: 80px;
|
||||
font-size: 3px;
|
||||
}
|
||||
|
||||
#browsersteps-click-start {
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
color: var(--color-grey-400);
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
@import "parts/_variables.scss";
|
||||
|
||||
#diff-ui {
|
||||
|
||||
background: var(--color-background);
|
||||
padding: 2em;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
border-radius: 5px;
|
||||
font-size: 11px;
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 3px 4px;
|
||||
border: 1px solid transparent;
|
||||
vertical-align: top;
|
||||
font: 1em monospace;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
display: inline;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
del {
|
||||
text-decoration: none;
|
||||
color: #b30000;
|
||||
background: #fadad7;
|
||||
}
|
||||
|
||||
ins {
|
||||
background: #eaf2c2;
|
||||
color: #406619;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#result {
|
||||
white-space: pre-wrap;
|
||||
|
||||
.change {
|
||||
span {}
|
||||
}
|
||||
}
|
||||
|
||||
#settings {
|
||||
background: rgba(0, 0, 0, .05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
color: #fff;
|
||||
font-size: 80%;
|
||||
|
||||
label {
|
||||
margin-left: 1em;
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
del {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
ins {
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.source {
|
||||
position: absolute;
|
||||
right: 1%;
|
||||
top: .2em;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
body {
|
||||
height: 99%;
|
||||
/* Hide scroll bar in Firefox */
|
||||
}
|
||||
}
|
||||
|
||||
td#diff-col div {
|
||||
text-align: justify;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.ignored {
|
||||
background-color: #ccc;
|
||||
/* border: #0d91fa 1px solid; */
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.triggered {
|
||||
background-color: #1b98f8;
|
||||
}
|
||||
|
||||
/* ignored and triggered? make it obvious error */
|
||||
.ignored.triggered {
|
||||
background-color: #ff0000;
|
||||
}
|
||||
|
||||
.tab-pane-inner#screenshot {
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
max-width: 99%;
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/**
|
||||
* CSS custom properties (aka variables).
|
||||
*/
|
||||
|
||||
:root {
|
||||
--color-white: #fff;
|
||||
--color-grey-50: #111;
|
||||
--color-grey-100: #262626;
|
||||
--color-grey-200: #333;
|
||||
--color-grey-300: #444;
|
||||
--color-grey-325: #555;
|
||||
--color-grey-350: #565d64;
|
||||
--color-grey-400: #666;
|
||||
--color-grey-500: #777;
|
||||
--color-grey-600: #999;
|
||||
--color-grey-700: #cbcbcb;
|
||||
--color-grey-750: #ddd;
|
||||
--color-grey-800: #e0e0e0;
|
||||
--color-grey-850: #eee;
|
||||
--color-grey-900: #f2f2f2;
|
||||
--color-black: #000;
|
||||
|
||||
--color-background-page: var(--color-grey-100);
|
||||
--color-background-gradient-first: #5ad8f7;
|
||||
--color-background-gradient-second: #2f50af;
|
||||
--color-background-gradient-third: #9150bf;
|
||||
--color-background: var(--color-white);
|
||||
--color-text: var(--color-grey-200);
|
||||
--color-link: #1b98f8;
|
||||
--color-menu-accent: #ed5900;
|
||||
--color-background-code: var(--color-grey-850);
|
||||
--color-error: #a00;
|
||||
--color-error-input: #ffebeb;
|
||||
--color-error-list: #dd0000;
|
||||
--color-table-background: var(--color-background);
|
||||
--color-table-stripe: var(--color-grey-900);
|
||||
--color-text-tab: var(--color-white);
|
||||
--color-background-tab: rgba(255, 255, 255, 0.2);
|
||||
--color-background-tab-hover: rgba(255, 255, 255, 0.5);
|
||||
--color-text-tab-active: #222;
|
||||
--color-api-key: #0078e7;
|
||||
|
||||
--color-background-button-primary: #0078e7;
|
||||
--color-background-button-green: #42dd53;
|
||||
--color-background-button-red: #dd4242;
|
||||
--color-background-button-success: rgb(28, 184, 65);
|
||||
--color-background-button-error: rgb(202, 60, 60);
|
||||
--color-text-button-error: var(--color-white);
|
||||
--color-background-button-warning: rgb(202, 60, 60);
|
||||
--color-text-button-warning: var(--color-white);
|
||||
--color-background-button-secondary: rgb(66, 184, 221);
|
||||
--color-background-button-cancel: rgb(200, 200, 200);
|
||||
--color-text-button: var(--color-white);
|
||||
--color-background-button-tag: rgb(99, 99, 99);
|
||||
--color-background-snapshot-age: #dfdfdf;
|
||||
--color-error-text-snapshot-age: var(--color-white);
|
||||
--color-error-background-snapshot-age: #ff0000;
|
||||
--color-background-button-tag-active: #9c9c9c;
|
||||
|
||||
--color-text-messages: var(--color-white);
|
||||
--color-background-messages-message: rgba(255, 255, 255, .2);
|
||||
--color-background-messages-error: rgba(255, 1, 1, .5);
|
||||
--color-background-messages-notice: rgba(255, 255, 255, .5);
|
||||
--color-border-notification: #ccc;
|
||||
|
||||
--color-background-checkbox-operations: rgba(0, 0, 0, 0.05);
|
||||
--color-warning: #ff3300;
|
||||
--color-border-warning: var(--color-warning);
|
||||
--color-text-legend: var(--color-white);
|
||||
|
||||
--color-link-new-version: #e07171;
|
||||
--color-last-checked: #bbb;
|
||||
--color-text-footer: #444;
|
||||
--color-border-watch-table-cell: #eee;
|
||||
|
||||
--color-text-watch-tag-list: #e70069;
|
||||
--color-background-new-watch-form: rgba(0, 0, 0, 0.05);
|
||||
--color-background-new-watch-input: var(--color-white);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
|
||||
--color-border-input: var(--color-grey-500);
|
||||
--color-shadow-input: var(--color-grey-400);
|
||||
--color-background-input: var(--color-white);
|
||||
--color-text-input: var(--color-text);
|
||||
--color-text-input-description: var(--color-grey-500);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
|
||||
--color-background-table-thead: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-700);
|
||||
|
||||
--color-text-menu-heading: var(--color-grey-350);
|
||||
--color-text-menu-link: var(--color-grey-500);
|
||||
--color-background-menu-link-hover: var(--color-grey-850);
|
||||
--color-text-menu-link-hover: var(--color-grey-300);
|
||||
|
||||
--color-shadow-jump: var(--color-grey-500);
|
||||
--color-icon-github: var(--color-black);
|
||||
--color-icon-github-hover: var(--color-grey-300);
|
||||
}
|
||||
|
||||
html[data-darkmode="true"] {
|
||||
--color-link: #59bdfb;
|
||||
--color-text: var(--color-white);
|
||||
|
||||
--color-background-gradient-first: #3f90a5;
|
||||
--color-background-gradient-second: #1e316c;
|
||||
--color-background-gradient-third: #4d2c64;
|
||||
|
||||
--color-background-new-watch-input: var(--color-grey-100);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
--color-background-table-thead: var(--color-grey-200);
|
||||
--color-table-background: var(--color-grey-300);
|
||||
--color-table-stripe: var(--color-grey-325);
|
||||
--color-background: var(--color-grey-300);
|
||||
--color-text-menu-heading: var(--color-grey-850);
|
||||
--color-text-menu-link: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-400);
|
||||
--color-text-tab-active: var(--color-text);
|
||||
|
||||
--color-border-input: var(--color-grey-400);
|
||||
--color-shadow-input: var(--color-grey-50);
|
||||
--color-background-input: var(--color-grey-350);
|
||||
--color-text-input-description: var(--color-grey-600);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
--color-text-watch-tag-list: #fa3e92;
|
||||
|
||||
--color-background-code: var(--color-grey-200);
|
||||
|
||||
--color-background-tab: rgba(0, 0, 0, 0.2);
|
||||
--color-background-tab-hover: rgba(0, 0, 0, 0.5);
|
||||
|
||||
--color-background-snapshot-age: var(--color-grey-200);
|
||||
--color-shadow-jump: var(--color-grey-200);
|
||||
--color-icon-github: var(--color-white);
|
||||
--color-icon-github-hover: var(--color-grey-700);
|
||||
|
||||
// Anything that can't be manipulated through variables follows.
|
||||
.watch-controls {
|
||||
img {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-spread {
|
||||
filter: hue-rotate(-10deg) brightness(1.5);
|
||||
}
|
||||
|
||||
.watch-table {
|
||||
|
||||
.title-col a[target="_blank"]::after,
|
||||
.current-diff-url::after {
|
||||
filter: invert(.5) hue-rotate(10deg) brightness(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,9 @@
|
||||
/*
|
||||
* -- BASE STYLES --
|
||||
* Most of these are inherited from Base, but I want to change a few.
|
||||
nvm use v14.18.1 && npm install && npm run build
|
||||
* or npm run watch
|
||||
*/
|
||||
/**
|
||||
* CSS custom properties (aka variables).
|
||||
*/
|
||||
:root {
|
||||
--color-white: #fff;
|
||||
--color-grey-50: #111;
|
||||
--color-grey-100: #262626;
|
||||
--color-grey-200: #333;
|
||||
--color-grey-300: #444;
|
||||
--color-grey-325: #555;
|
||||
--color-grey-350: #565d64;
|
||||
--color-grey-400: #666;
|
||||
--color-grey-500: #777;
|
||||
--color-grey-600: #999;
|
||||
--color-grey-700: #cbcbcb;
|
||||
--color-grey-750: #ddd;
|
||||
--color-grey-800: #e0e0e0;
|
||||
--color-grey-850: #eee;
|
||||
--color-grey-900: #f2f2f2;
|
||||
--color-black: #000;
|
||||
--color-background-page: var(--color-grey-100);
|
||||
--color-background-gradient-first: #5ad8f7;
|
||||
--color-background-gradient-second: #2f50af;
|
||||
--color-background-gradient-third: #9150bf;
|
||||
--color-background: var(--color-white);
|
||||
--color-text: var(--color-grey-200);
|
||||
--color-link: #1b98f8;
|
||||
--color-menu-accent: #ed5900;
|
||||
--color-background-code: var(--color-grey-850);
|
||||
--color-error: #a00;
|
||||
--color-error-input: #ffebeb;
|
||||
--color-error-list: #dd0000;
|
||||
--color-table-background: var(--color-background);
|
||||
--color-table-stripe: var(--color-grey-900);
|
||||
--color-text-tab: var(--color-white);
|
||||
--color-background-tab: rgba(255, 255, 255, 0.2);
|
||||
--color-background-tab-hover: rgba(255, 255, 255, 0.5);
|
||||
--color-text-tab-active: #222;
|
||||
--color-api-key: #0078e7;
|
||||
--color-background-button-primary: #0078e7;
|
||||
--color-background-button-green: #42dd53;
|
||||
--color-background-button-red: #dd4242;
|
||||
--color-background-button-success: rgb(28, 184, 65);
|
||||
--color-background-button-error: rgb(202, 60, 60);
|
||||
--color-text-button-error: var(--color-white);
|
||||
--color-background-button-warning: rgb(202, 60, 60);
|
||||
--color-text-button-warning: var(--color-white);
|
||||
--color-background-button-secondary: rgb(66, 184, 221);
|
||||
--color-background-button-cancel: rgb(200, 200, 200);
|
||||
--color-text-button: var(--color-white);
|
||||
--color-background-button-tag: rgb(99, 99, 99);
|
||||
--color-background-snapshot-age: #dfdfdf;
|
||||
--color-error-text-snapshot-age: var(--color-white);
|
||||
--color-error-background-snapshot-age: #ff0000;
|
||||
--color-background-button-tag-active: #9c9c9c;
|
||||
--color-text-messages: var(--color-white);
|
||||
--color-background-messages-message: rgba(255, 255, 255, .2);
|
||||
--color-background-messages-error: rgba(255, 1, 1, .5);
|
||||
--color-background-messages-notice: rgba(255, 255, 255, .5);
|
||||
--color-border-notification: #ccc;
|
||||
--color-background-checkbox-operations: rgba(0, 0, 0, 0.05);
|
||||
--color-warning: #ff3300;
|
||||
--color-border-warning: var(--color-warning);
|
||||
--color-text-legend: var(--color-white);
|
||||
--color-link-new-version: #e07171;
|
||||
--color-last-checked: #bbb;
|
||||
--color-text-footer: #444;
|
||||
--color-border-watch-table-cell: #eee;
|
||||
--color-text-watch-tag-list: #e70069;
|
||||
--color-background-new-watch-form: rgba(0, 0, 0, 0.05);
|
||||
--color-background-new-watch-input: var(--color-white);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
--color-border-input: var(--color-grey-500);
|
||||
--color-shadow-input: var(--color-grey-400);
|
||||
--color-background-input: var(--color-white);
|
||||
--color-text-input: var(--color-text);
|
||||
--color-text-input-description: var(--color-grey-500);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
--color-background-table-thead: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-700);
|
||||
--color-text-menu-heading: var(--color-grey-350);
|
||||
--color-text-menu-link: var(--color-grey-500);
|
||||
--color-background-menu-link-hover: var(--color-grey-850);
|
||||
--color-text-menu-link-hover: var(--color-grey-300);
|
||||
--color-shadow-jump: var(--color-grey-500);
|
||||
--color-icon-github: var(--color-black);
|
||||
--color-icon-github-hover: var(--color-grey-300); }
|
||||
|
||||
html[data-darkmode="true"] {
|
||||
--color-link: #59bdfb;
|
||||
--color-text: var(--color-white);
|
||||
--color-background-gradient-first: #3f90a5;
|
||||
--color-background-gradient-second: #1e316c;
|
||||
--color-background-gradient-third: #4d2c64;
|
||||
--color-background-new-watch-input: var(--color-grey-100);
|
||||
--color-text-new-watch-input: var(--color-text);
|
||||
--color-background-table-thead: var(--color-grey-200);
|
||||
--color-table-background: var(--color-grey-300);
|
||||
--color-table-stripe: var(--color-grey-325);
|
||||
--color-background: var(--color-grey-300);
|
||||
--color-text-menu-heading: var(--color-grey-850);
|
||||
--color-text-menu-link: var(--color-grey-800);
|
||||
--color-border-table-cell: var(--color-grey-400);
|
||||
--color-text-tab-active: var(--color-text);
|
||||
--color-border-input: var(--color-grey-400);
|
||||
--color-shadow-input: var(--color-grey-50);
|
||||
--color-background-input: var(--color-grey-350);
|
||||
--color-text-input-description: var(--color-grey-600);
|
||||
--color-text-input-placeholder: var(--color-grey-600);
|
||||
--color-text-watch-tag-list: #fa3e92;
|
||||
--color-background-code: var(--color-grey-200);
|
||||
--color-background-tab: rgba(0, 0, 0, 0.2);
|
||||
--color-background-tab-hover: rgba(0, 0, 0, 0.5);
|
||||
--color-background-snapshot-age: var(--color-grey-200);
|
||||
--color-shadow-jump: var(--color-grey-200);
|
||||
--color-icon-github: var(--color-white);
|
||||
--color-icon-github-hover: var(--color-grey-700); }
|
||||
html[data-darkmode="true"] .watch-controls img {
|
||||
opacity: 0.4; }
|
||||
html[data-darkmode="true"] .icon-spread {
|
||||
filter: hue-rotate(-10deg) brightness(1.5); }
|
||||
html[data-darkmode="true"] .watch-table .title-col a[target="_blank"]::after,
|
||||
html[data-darkmode="true"] .watch-table .current-diff-url::after {
|
||||
filter: invert(0.5) hue-rotate(10deg) brightness(2); }
|
||||
|
||||
/* spinner */
|
||||
.spinner,
|
||||
.spinner:after {
|
||||
@@ -172,8 +50,6 @@ html[data-darkmode="true"] {
|
||||
#browser_steps li {
|
||||
list-style: decimal;
|
||||
padding: 5px; }
|
||||
#browser_steps li:not(:first-child):hover {
|
||||
opacity: 1.0; }
|
||||
#browser_steps li .control {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px; }
|
||||
@@ -220,17 +96,11 @@ html[data-darkmode="true"] {
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin-left: -40px;
|
||||
z-index: 100;
|
||||
max-width: 350px;
|
||||
text-align: center; }
|
||||
z-index: 100; }
|
||||
#browsersteps-selector-wrapper .spinner, #browsersteps-selector-wrapper .spinner:after {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
font-size: 3px; }
|
||||
#browsersteps-selector-wrapper #browsersteps-click-start {
|
||||
color: var(--color-grey-400); }
|
||||
#browsersteps-selector-wrapper #browsersteps-click-start:hover {
|
||||
cursor: pointer; }
|
||||
|
||||
.arrow {
|
||||
border: solid #1b98f8;
|
||||
@@ -251,70 +121,28 @@ html[data-darkmode="true"] {
|
||||
-webkit-transform: rotate(45deg); }
|
||||
|
||||
body {
|
||||
color: var(--color-text);
|
||||
background: var(--color-background-page); }
|
||||
|
||||
.visually-hidden {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px; }
|
||||
color: #333;
|
||||
background: #262626; }
|
||||
|
||||
.pure-table-even {
|
||||
background: var(--color-background); }
|
||||
background: #fff; }
|
||||
|
||||
/* Some styles from https://css-tricks.com/ */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--color-link); }
|
||||
color: #1b98f8; }
|
||||
|
||||
a.github-link {
|
||||
color: var(--color-icon-github);
|
||||
margin: 0 1rem 0 0.5rem; }
|
||||
a.github-link svg {
|
||||
fill: currentColor; }
|
||||
a.github-link:hover {
|
||||
color: var(--color-icon-github-hover); }
|
||||
|
||||
button.toggle-theme {
|
||||
width: 4rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--color-icon-github); }
|
||||
button.toggle-theme:hover {
|
||||
color: var(--color-icon-github-hover); }
|
||||
button.toggle-theme svg {
|
||||
fill: currentColor; }
|
||||
button.toggle-theme .icon-light {
|
||||
display: block; }
|
||||
button.toggle-theme .icon-dark {
|
||||
display: none; }
|
||||
button.toggle-theme.dark .icon-light {
|
||||
display: none; }
|
||||
button.toggle-theme.dark .icon-dark {
|
||||
display: block; }
|
||||
color: #fff; }
|
||||
|
||||
.pure-menu-horizontal {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid var(--color-menu-accent);
|
||||
border-bottom: 2px solid #ed5900;
|
||||
align-items: center; }
|
||||
|
||||
.pure-menu-heading {
|
||||
color: var(--color-text-menu-heading); }
|
||||
|
||||
.pure-menu-link {
|
||||
color: var(--color-text-menu-link); }
|
||||
.pure-menu-link:hover {
|
||||
background-color: var(--color-background-menu-link-hover);
|
||||
color: var(--color-text-menu-link-hover); }
|
||||
|
||||
section.content {
|
||||
padding-top: 5em;
|
||||
padding-bottom: 1em;
|
||||
@@ -324,8 +152,7 @@ section.content {
|
||||
justify-content: center; }
|
||||
|
||||
code {
|
||||
background: var(--color-background-code);
|
||||
color: var(--color-text); }
|
||||
background: #eee; }
|
||||
|
||||
/* table related */
|
||||
.watch-table {
|
||||
@@ -334,7 +161,7 @@ code {
|
||||
.watch-table tr.unviewed {
|
||||
font-weight: bold; }
|
||||
.watch-table .error {
|
||||
color: var(--color-error); }
|
||||
color: #a00; }
|
||||
.watch-table td {
|
||||
white-space: nowrap; }
|
||||
.watch-table td.title-col {
|
||||
@@ -348,13 +175,12 @@ code {
|
||||
font-weight: bolder; }
|
||||
.watch-table th a.inactive .arrow {
|
||||
display: none; }
|
||||
.watch-table .title-col a[target="_blank"]::after,
|
||||
.watch-table .current-diff-url::after {
|
||||
.watch-table .title-col a[target="_blank"]::after, .watch-table .current-diff-url::after {
|
||||
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
|
||||
margin: 0 3px 0 5px; }
|
||||
|
||||
.watch-tag-list {
|
||||
color: var(--color-text-watch-tag-list);
|
||||
color: #e70069;
|
||||
white-space: nowrap; }
|
||||
|
||||
.box {
|
||||
@@ -377,10 +203,9 @@ code {
|
||||
|
||||
body:after {
|
||||
content: "";
|
||||
background: linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%); }
|
||||
background: linear-gradient(130deg, #5ad8f7, #2f50af 41.07%, #9150bf 84.05%); }
|
||||
|
||||
body:after,
|
||||
body:before {
|
||||
body:after, body:before {
|
||||
display: block;
|
||||
height: 650px;
|
||||
position: absolute;
|
||||
@@ -396,8 +221,7 @@ body::before {
|
||||
content: "";
|
||||
background-size: cover; }
|
||||
|
||||
body:after,
|
||||
body:before {
|
||||
body:after, body:before {
|
||||
-webkit-clip-path: polygon(100% 0, 0 0, 0 77.5%, 1% 77.4%, 2% 77.1%, 3% 76.6%, 4% 75.9%, 5% 75.05%, 6% 74.05%, 7% 72.95%, 8% 71.75%, 9% 70.55%, 10% 69.3%, 11% 68.05%, 12% 66.9%, 13% 65.8%, 14% 64.8%, 15% 64%, 16% 63.35%, 17% 62.85%, 18% 62.6%, 19% 62.5%, 20% 62.65%, 21% 63%, 22% 63.5%, 23% 64.2%, 24% 65.1%, 25% 66.1%, 26% 67.2%, 27% 68.4%, 28% 69.65%, 29% 70.9%, 30% 72.15%, 31% 73.3%, 32% 74.35%, 33% 75.3%, 34% 76.1%, 35% 76.75%, 36% 77.2%, 37% 77.45%, 38% 77.5%, 39% 77.3%, 40% 76.95%, 41% 76.4%, 42% 75.65%, 43% 74.75%, 44% 73.75%, 45% 72.6%, 46% 71.4%, 47% 70.15%, 48% 68.9%, 49% 67.7%, 50% 66.55%, 51% 65.5%, 52% 64.55%, 53% 63.75%, 54% 63.15%, 55% 62.75%, 56% 62.55%, 57% 62.5%, 58% 62.7%, 59% 63.1%, 60% 63.7%, 61% 64.45%, 62% 65.4%, 63% 66.45%, 64% 67.6%, 65% 68.8%, 66% 70.05%, 67% 71.3%, 68% 72.5%, 69% 73.6%, 70% 74.65%, 71% 75.55%, 72% 76.35%, 73% 76.9%, 74% 77.3%, 75% 77.5%, 76% 77.45%, 77% 77.25%, 78% 76.8%, 79% 76.2%, 80% 75.4%, 81% 74.45%, 82% 73.4%, 83% 72.25%, 84% 71.05%, 85% 69.8%, 86% 68.55%, 87% 67.35%, 88% 66.2%, 89% 65.2%, 90% 64.3%, 91% 63.55%, 92% 63%, 93% 62.65%, 94% 62.5%, 95% 62.55%, 96% 62.8%, 97% 63.3%, 98% 63.9%, 99% 64.75%, 100% 65.7%);
|
||||
clip-path: polygon(100% 0, 0 0, 0 77.5%, 1% 77.4%, 2% 77.1%, 3% 76.6%, 4% 75.9%, 5% 75.05%, 6% 74.05%, 7% 72.95%, 8% 71.75%, 9% 70.55%, 10% 69.3%, 11% 68.05%, 12% 66.9%, 13% 65.8%, 14% 64.8%, 15% 64%, 16% 63.35%, 17% 62.85%, 18% 62.6%, 19% 62.5%, 20% 62.65%, 21% 63%, 22% 63.5%, 23% 64.2%, 24% 65.1%, 25% 66.1%, 26% 67.2%, 27% 68.4%, 28% 69.65%, 29% 70.9%, 30% 72.15%, 31% 73.3%, 32% 74.35%, 33% 75.3%, 34% 76.1%, 35% 76.75%, 36% 77.2%, 37% 77.45%, 38% 77.5%, 39% 77.3%, 40% 76.95%, 41% 76.4%, 42% 75.65%, 43% 74.75%, 44% 73.75%, 45% 72.6%, 46% 71.4%, 47% 70.15%, 48% 68.9%, 49% 67.7%, 50% 66.55%, 51% 65.5%, 52% 64.55%, 53% 63.75%, 54% 63.15%, 55% 62.75%, 56% 62.55%, 57% 62.5%, 58% 62.7%, 59% 63.1%, 60% 63.7%, 61% 64.45%, 62% 65.4%, 63% 66.45%, 64% 67.6%, 65% 68.8%, 66% 70.05%, 67% 71.3%, 68% 72.5%, 69% 73.6%, 70% 74.65%, 71% 75.55%, 72% 76.35%, 73% 76.9%, 74% 77.3%, 75% 77.5%, 76% 77.45%, 77% 77.25%, 78% 76.8%, 79% 76.2%, 80% 75.4%, 81% 74.45%, 82% 73.4%, 83% 72.25%, 84% 71.05%, 85% 69.8%, 86% 68.55%, 87% 67.35%, 88% 66.2%, 89% 65.2%, 90% 64.3%, 91% 63.55%, 92% 63%, 93% 62.65%, 94% 62.5%, 95% 62.55%, 96% 62.8%, 97% 63.3%, 98% 63.9%, 99% 64.75%, 100% 65.7%); }
|
||||
|
||||
@@ -410,96 +234,83 @@ body:before {
|
||||
max-width: 400px;
|
||||
display: block; }
|
||||
|
||||
.pure-button-primary,
|
||||
a.pure-button-primary,
|
||||
.pure-button-selected,
|
||||
a.pure-button-selected {
|
||||
background-color: var(--color-background-button-primary); }
|
||||
|
||||
.button-secondary {
|
||||
color: var(--color-text-button);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); }
|
||||
|
||||
.button-success {
|
||||
background: var(--color-background-button-success); }
|
||||
background: #1cb841;
|
||||
/* this is a green */ }
|
||||
|
||||
.button-tag {
|
||||
background: var(--color-background-button-tag);
|
||||
color: var(--color-text-button);
|
||||
background: #636363;
|
||||
color: #fff;
|
||||
font-size: 65%;
|
||||
border-bottom-left-radius: initial;
|
||||
border-bottom-right-radius: initial; }
|
||||
.button-tag.active {
|
||||
background: var(--color-background-button-tag-active);
|
||||
background: #9c9c9c;
|
||||
font-weight: bold; }
|
||||
|
||||
.button-error {
|
||||
background: var(--color-background-button-error);
|
||||
color: var(--color-text-button-error); }
|
||||
background: #ca3c3c;
|
||||
/* this is a maroon */ }
|
||||
|
||||
.button-warning {
|
||||
background: var(--color-background-button-warning);
|
||||
color: var(--color-text-button-warning); }
|
||||
background: #df7514;
|
||||
/* this is an orange */ }
|
||||
|
||||
.button-secondary {
|
||||
background: var(--color-background-button-secondary); }
|
||||
background: #42b8dd;
|
||||
/* this is a light blue */ }
|
||||
|
||||
.button-cancel {
|
||||
background: var(--color-background-button-cancel); }
|
||||
|
||||
#save_button {
|
||||
margin-right: 1rem; }
|
||||
background: #c8c8c8;
|
||||
/* this is a green */ }
|
||||
|
||||
.messages li {
|
||||
list-style: none;
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
color: var(--color-text-messages);
|
||||
color: #fff;
|
||||
font-weight: bold; }
|
||||
.messages li.message {
|
||||
background: var(--color-background-messages-message); }
|
||||
background: rgba(255, 255, 255, 0.2); }
|
||||
.messages li.error {
|
||||
background: var(--color-background-messages-error); }
|
||||
background: rgba(255, 1, 1, 0.5); }
|
||||
.messages li.notice {
|
||||
background: var(--color-background-messages-notice); }
|
||||
background: rgba(255, 255, 255, 0.5); }
|
||||
|
||||
.messages.with-share-link > *:hover {
|
||||
cursor: pointer; }
|
||||
|
||||
.notifications-wrapper {
|
||||
padding: 0.5rem 0 1rem 0; }
|
||||
|
||||
#notification-customisation {
|
||||
border: 1px solid var(--color-border-notification);
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px; }
|
||||
|
||||
#notification-error-log {
|
||||
border: 1px solid var(--color-border-notification);
|
||||
border: 1px solid #ccc;
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
overflow-wrap: break-word; }
|
||||
|
||||
#token-table.pure-table td,
|
||||
#token-table.pure-table th {
|
||||
#token-table.pure-table td, #token-table.pure-table th {
|
||||
font-size: 80%; }
|
||||
|
||||
#new-watch-form {
|
||||
background: var(--color-background-new-watch-form);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em; }
|
||||
#new-watch-form input {
|
||||
display: inline-block;
|
||||
margin-bottom: 5px; }
|
||||
#new-watch-form input:not(.pure-button) {
|
||||
background-color: var(--color-background-new-watch-input);
|
||||
color: var(--color-text-new-watch-input); }
|
||||
#new-watch-form .label {
|
||||
display: none; }
|
||||
#new-watch-form legend {
|
||||
color: var(--color-text-legend);
|
||||
color: #fff;
|
||||
font-weight: bold; }
|
||||
#new-watch-form #watch-add-wrapper-zone > div {
|
||||
display: inline-block; }
|
||||
@@ -514,14 +325,14 @@ a.pure-button-selected {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 120px;
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
box-shadow: 1px 1px 4px var(--color-shadow-jump); }
|
||||
box-shadow: 5px 0 5px -2px #888; }
|
||||
#diff-jump a {
|
||||
color: var(--color-link);
|
||||
cursor: pointer;
|
||||
color: #1b98f8;
|
||||
cursor: grabbing;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
@@ -530,8 +341,8 @@ a.pure-button-selected {
|
||||
|
||||
footer {
|
||||
padding: 10px;
|
||||
background: var(--color-background);
|
||||
color: var(--color-text-footer);
|
||||
background: #fff;
|
||||
color: #444;
|
||||
text-align: center; }
|
||||
|
||||
#feed-icon {
|
||||
@@ -550,7 +361,7 @@ footer {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
font-size: 65%;
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 10px; }
|
||||
.sticky-tab#left-sticky {
|
||||
left: 0px; }
|
||||
@@ -562,10 +373,9 @@ footer {
|
||||
font-weight: bold; }
|
||||
|
||||
#new-version-text a {
|
||||
color: var(--color-link-new-version); }
|
||||
color: #e07171; }
|
||||
|
||||
.watch-controls {
|
||||
color: #f8321b;
|
||||
/* default */ }
|
||||
.watch-controls .state-on img {
|
||||
opacity: 0.8; }
|
||||
@@ -580,7 +390,7 @@ footer {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
overflow-wrap: normal;
|
||||
overflow-x: auto; }
|
||||
overflow-x: scroll; }
|
||||
|
||||
.pure-form {
|
||||
/* The input fields with errors */
|
||||
@@ -590,39 +400,27 @@ footer {
|
||||
.pure-form fieldset ul {
|
||||
padding-bottom: 0px;
|
||||
margin-bottom: 0px; }
|
||||
.pure-form .pure-control-group,
|
||||
.pure-form .pure-group,
|
||||
.pure-form .pure-controls {
|
||||
.pure-form .pure-control-group, .pure-form .pure-group, .pure-form .pure-controls {
|
||||
padding-bottom: 1em; }
|
||||
.pure-form .pure-control-group div,
|
||||
.pure-form .pure-group div,
|
||||
.pure-form .pure-controls div {
|
||||
.pure-form .pure-control-group div, .pure-form .pure-group div, .pure-form .pure-controls div {
|
||||
margin: 0px; }
|
||||
.pure-form .pure-control-group .checkbox > *,
|
||||
.pure-form .pure-group .checkbox > *,
|
||||
.pure-form .pure-controls .checkbox > * {
|
||||
.pure-form .pure-control-group .checkbox > *, .pure-form .pure-group .checkbox > *, .pure-form .pure-controls .checkbox > * {
|
||||
display: inline;
|
||||
vertical-align: middle; }
|
||||
.pure-form .pure-control-group .checkbox > label,
|
||||
.pure-form .pure-group .checkbox > label,
|
||||
.pure-form .pure-controls .checkbox > label {
|
||||
.pure-form .pure-control-group .checkbox > label, .pure-form .pure-group .checkbox > label, .pure-form .pure-controls .checkbox > label {
|
||||
padding-left: 5px; }
|
||||
.pure-form .pure-control-group legend,
|
||||
.pure-form .pure-group legend,
|
||||
.pure-form .pure-controls legend {
|
||||
color: var(--color-text-legend); }
|
||||
.pure-form .error input {
|
||||
background-color: var(--color-error-input); }
|
||||
background-color: #ffebeb; }
|
||||
.pure-form ul.errors {
|
||||
padding: .5em .6em;
|
||||
border: 1px solid var(--color-error-list);
|
||||
border: 1px solid #dd0000;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
.pure-form ul.errors li {
|
||||
margin-left: 1em;
|
||||
color: var(--color-error-list); }
|
||||
color: #dd0000; }
|
||||
.pure-form label {
|
||||
font-weight: bold; }
|
||||
.pure-form textarea {
|
||||
@@ -664,19 +462,15 @@ footer {
|
||||
/* Force table to not be like tables anymore */
|
||||
/* Force table to not be like tables anymore */
|
||||
/* Hide table headers (but not display: none;, for accessibility) */ }
|
||||
.watch-table thead,
|
||||
.watch-table tbody,
|
||||
.watch-table th,
|
||||
.watch-table td,
|
||||
.watch-table tr {
|
||||
.watch-table thead, .watch-table tbody, .watch-table th, .watch-table td, .watch-table tr {
|
||||
display: block; }
|
||||
.watch-table .last-checked > span {
|
||||
vertical-align: middle; }
|
||||
.watch-table .last-checked::before {
|
||||
color: var(--color-last-checked);
|
||||
color: #555;
|
||||
content: "Last Checked "; }
|
||||
.watch-table .last-changed::before {
|
||||
color: var(--color-last-checked);
|
||||
color: #555;
|
||||
content: "Last Changed "; }
|
||||
.watch-table td.inline {
|
||||
display: inline-block; }
|
||||
@@ -684,13 +478,12 @@ footer {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px; }
|
||||
.watch-table .pure-table td,
|
||||
.watch-table .pure-table th {
|
||||
.watch-table .pure-table td, .watch-table .pure-table th {
|
||||
border: none; }
|
||||
.watch-table td {
|
||||
/* Behave like a "row" */
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--color-border-watch-table-cell);
|
||||
border-bottom: 1px solid #eee;
|
||||
vertical-align: middle; }
|
||||
.watch-table td:before {
|
||||
/* Top/left values mimic padding */
|
||||
@@ -700,66 +493,12 @@ footer {
|
||||
padding-right: 10px;
|
||||
white-space: nowrap; }
|
||||
.watch-table.pure-table-striped tr {
|
||||
background-color: var(--color-table-background); }
|
||||
background-color: #fff; }
|
||||
.watch-table.pure-table-striped tr:nth-child(2n-1) {
|
||||
background-color: var(--color-table-stripe); }
|
||||
background-color: #eee; }
|
||||
.watch-table.pure-table-striped tr:nth-child(2n-1) td {
|
||||
background-color: inherit; } }
|
||||
|
||||
.pure-table {
|
||||
border-color: var(--color-border-table-cell); }
|
||||
.pure-table thead {
|
||||
background-color: var(--color-background-table-thead);
|
||||
color: var(--color-text); }
|
||||
.pure-table td,
|
||||
.pure-table th {
|
||||
border-left-color: var(--color-border-table-cell); }
|
||||
|
||||
.pure-table-striped tr:nth-child(2n-1) td {
|
||||
background-color: var(--color-table-stripe); }
|
||||
|
||||
.pure-form input[type=color],
|
||||
.pure-form input[type=date],
|
||||
.pure-form input[type=datetime-local],
|
||||
.pure-form input[type=datetime],
|
||||
.pure-form input[type=email],
|
||||
.pure-form input[type=month],
|
||||
.pure-form input[type=number],
|
||||
.pure-form input[type=password],
|
||||
.pure-form input[type=search],
|
||||
.pure-form input[type=tel],
|
||||
.pure-form input[type=text],
|
||||
.pure-form input[type=time],
|
||||
.pure-form input[type=url],
|
||||
.pure-form input[type=week],
|
||||
.pure-form select,
|
||||
.pure-form textarea {
|
||||
border: var(--color-border-input);
|
||||
box-shadow: inset 0 1px 3px var(--color-shadow-input);
|
||||
background-color: var(--color-background-input);
|
||||
color: var(--color-text-input); }
|
||||
.pure-form input[type=color]:active,
|
||||
.pure-form input[type=date]:active,
|
||||
.pure-form input[type=datetime-local]:active,
|
||||
.pure-form input[type=datetime]:active,
|
||||
.pure-form input[type=email]:active,
|
||||
.pure-form input[type=month]:active,
|
||||
.pure-form input[type=number]:active,
|
||||
.pure-form input[type=password]:active,
|
||||
.pure-form input[type=search]:active,
|
||||
.pure-form input[type=tel]:active,
|
||||
.pure-form input[type=text]:active,
|
||||
.pure-form input[type=time]:active,
|
||||
.pure-form input[type=url]:active,
|
||||
.pure-form input[type=week]:active,
|
||||
.pure-form select:active,
|
||||
.pure-form textarea:active {
|
||||
background-color: var(--color-background-input); }
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--color-text-input-placeholder); }
|
||||
|
||||
/** Desktop vs mobile input field strategy
|
||||
- We dont use 'size' with <input> because `size` is too unreliable to override, and will often push-out
|
||||
- Rely always on width in CSS
|
||||
@@ -776,29 +515,25 @@ textarea::placeholder {
|
||||
.tabs ul li {
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
color: var(--color-text-tab);
|
||||
color: #fff;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
background-color: var(--color-background-tab); }
|
||||
.tabs ul li:not(.active):hover {
|
||||
background-color: var(--color-background-tab-hover); }
|
||||
.tabs ul li.active,
|
||||
.tabs ul li :target {
|
||||
background-color: var(--color-background); }
|
||||
.tabs ul li.active a,
|
||||
.tabs ul li :target a {
|
||||
color: var(--color-text-tab-active);
|
||||
background-color: rgba(255, 255, 255, 0.2); }
|
||||
.tabs ul li.active, .tabs ul li :target {
|
||||
background-color: #fff; }
|
||||
.tabs ul li.active a, .tabs ul li :target a {
|
||||
color: #222;
|
||||
font-weight: bold; }
|
||||
.tabs ul li a {
|
||||
display: block;
|
||||
padding: 0.8em;
|
||||
color: var(--color-text-tab); }
|
||||
color: #fff; }
|
||||
|
||||
.pure-form-stacked > div:first-child {
|
||||
display: block; }
|
||||
|
||||
.login-form .inner {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 5px; }
|
||||
|
||||
@@ -828,14 +563,13 @@ body.full-width .edit-form {
|
||||
.edit-form .box-wrap {
|
||||
position: relative; }
|
||||
.edit-form .inner {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 20px; }
|
||||
.edit-form #actions {
|
||||
display: block;
|
||||
background: var(--color-background); }
|
||||
background: #fff; }
|
||||
.edit-form .pure-form-message-inline {
|
||||
padding-left: 0;
|
||||
color: var(--color-text-input-description); }
|
||||
padding-left: 0; }
|
||||
|
||||
ul {
|
||||
padding-left: 1em;
|
||||
@@ -872,13 +606,13 @@ ul {
|
||||
cursor: pointer; }
|
||||
|
||||
#api-key-copy {
|
||||
color: var(--color-api-key); }
|
||||
color: #0078e7; }
|
||||
|
||||
.button-green {
|
||||
background-color: var(--color-background-button-green); }
|
||||
background-color: #42dd53; }
|
||||
|
||||
.button-red {
|
||||
background-color: var(--color-background-button-red); }
|
||||
background-color: #dd4242; }
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
@@ -891,21 +625,20 @@ ul {
|
||||
/* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
/* Non-prefixed version, currently
|
||||
supported by Chrome, Edge, Opera and Firefox */ }
|
||||
supported by Chrome, Edge, Opera and Firefox */ }
|
||||
|
||||
.snapshot-age {
|
||||
padding: 4px;
|
||||
margin: 0.5rem 0;
|
||||
background-color: var(--color-background-snapshot-age);
|
||||
background-color: #dfdfdf;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px; }
|
||||
.snapshot-age.error {
|
||||
background-color: var(--color-error-background-snapshot-age);
|
||||
color: var(--color-error-text-snapshot-age); }
|
||||
background-color: #ff0000;
|
||||
color: #fff; }
|
||||
|
||||
#checkbox-operations {
|
||||
background: var(--color-background-checkbox-operations);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
@@ -915,10 +648,10 @@ ul {
|
||||
vertical-align: middle; }
|
||||
|
||||
.inline-warning {
|
||||
border: 1px solid var(--color-border-warning);
|
||||
border: 1px solid #ff3300;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
color: var(--color-warning); }
|
||||
color: #ff3300; }
|
||||
.inline-warning > span {
|
||||
display: inline-block;
|
||||
vertical-align: middle; }
|
||||
|
||||
@@ -1,107 +1,42 @@
|
||||
/*
|
||||
* -- BASE STYLES --
|
||||
* Most of these are inherited from Base, but I want to change a few.
|
||||
nvm use v14.18.1 && npm install && npm run build
|
||||
* or npm run watch
|
||||
*/
|
||||
|
||||
@import "parts/_variables";
|
||||
@import "parts/_spinners";
|
||||
@import "parts/_browser-steps";
|
||||
@import "parts/_arrows";
|
||||
@import "parts/spinners";
|
||||
@import "parts/browser-steps";
|
||||
@import "parts/_arrows.scss";
|
||||
|
||||
body {
|
||||
color: var(--color-text);
|
||||
background: var(--color-background-page);
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
color: #333;
|
||||
background: #262626;
|
||||
}
|
||||
|
||||
.pure-table-even {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
/* Some styles from https://css-tricks.com/ */
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--color-link);
|
||||
color: #1b98f8;
|
||||
}
|
||||
|
||||
a.github-link {
|
||||
color: var(--color-icon-github);
|
||||
margin: 0 1rem 0 0.5rem;
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-icon-github-hover);
|
||||
}
|
||||
}
|
||||
|
||||
button.toggle-theme {
|
||||
width: 4rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
color: var(--color-icon-github);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-icon-github-hover);
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.icon-light {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
.icon-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.icon-dark {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pure-menu-horizontal {
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid var(--color-menu-accent);
|
||||
border-bottom: 2px solid #ed5900;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pure-menu-heading {
|
||||
color: var(--color-text-menu-heading);
|
||||
}
|
||||
|
||||
.pure-menu-link {
|
||||
color: var(--color-text-menu-link);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-background-menu-link-hover);
|
||||
color: var(--color-text-menu-link-hover);
|
||||
}
|
||||
}
|
||||
|
||||
section.content {
|
||||
padding-top: 5em;
|
||||
padding-bottom: 1em;
|
||||
@@ -112,8 +47,7 @@ section.content {
|
||||
}
|
||||
|
||||
code {
|
||||
background: var(--color-background-code);
|
||||
color: var(--color-text);
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
/* table related */
|
||||
@@ -126,7 +60,7 @@ code {
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--color-error);
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -140,14 +74,11 @@ code {
|
||||
|
||||
th {
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
font-weight: normal;
|
||||
|
||||
&.active {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
.arrow {
|
||||
display: none;
|
||||
@@ -156,15 +87,14 @@ code {
|
||||
}
|
||||
}
|
||||
|
||||
.title-col a[target="_blank"]::after,
|
||||
.current-diff-url::after {
|
||||
.title-col a[target="_blank"]::after, .current-diff-url::after {
|
||||
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQElEQVR42qXKwQkAIAxDUUdxtO6/RBQkQZvSi8I/pL4BoGw/XPkh4XigPmsUgh0626AjRsgxHTkUThsG2T/sIlzdTsp52kSS1wAAAABJRU5ErkJggg==);
|
||||
margin: 0 3px 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.watch-tag-list {
|
||||
color: var(--color-text-watch-tag-list);
|
||||
color: #e70069;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -196,11 +126,10 @@ code {
|
||||
|
||||
body:after {
|
||||
content: "";
|
||||
background: linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%);
|
||||
background: linear-gradient(130deg, #5ad8f7, #2f50af 41.07%, #9150bf 84.05%);
|
||||
}
|
||||
|
||||
body:after,
|
||||
body:before {
|
||||
body:after, body:before {
|
||||
display: block;
|
||||
height: 650px;
|
||||
position: absolute;
|
||||
@@ -220,8 +149,7 @@ body::before {
|
||||
background-size: cover
|
||||
}
|
||||
|
||||
body:after,
|
||||
body:before {
|
||||
body:after, body:before {
|
||||
-webkit-clip-path: polygon(100% 0, 0 0, 0 77.5%, 1% 77.4%, 2% 77.1%, 3% 76.6%, 4% 75.9%, 5% 75.05%, 6% 74.05%, 7% 72.95%, 8% 71.75%, 9% 70.55%, 10% 69.3%, 11% 68.05%, 12% 66.9%, 13% 65.8%, 14% 64.8%, 15% 64%, 16% 63.35%, 17% 62.85%, 18% 62.6%, 19% 62.5%, 20% 62.65%, 21% 63%, 22% 63.5%, 23% 64.2%, 24% 65.1%, 25% 66.1%, 26% 67.2%, 27% 68.4%, 28% 69.65%, 29% 70.9%, 30% 72.15%, 31% 73.3%, 32% 74.35%, 33% 75.3%, 34% 76.1%, 35% 76.75%, 36% 77.2%, 37% 77.45%, 38% 77.5%, 39% 77.3%, 40% 76.95%, 41% 76.4%, 42% 75.65%, 43% 74.75%, 44% 73.75%, 45% 72.6%, 46% 71.4%, 47% 70.15%, 48% 68.9%, 49% 67.7%, 50% 66.55%, 51% 65.5%, 52% 64.55%, 53% 63.75%, 54% 63.15%, 55% 62.75%, 56% 62.55%, 57% 62.5%, 58% 62.7%, 59% 63.1%, 60% 63.7%, 61% 64.45%, 62% 65.4%, 63% 66.45%, 64% 67.6%, 65% 68.8%, 66% 70.05%, 67% 71.3%, 68% 72.5%, 69% 73.6%, 70% 74.65%, 71% 75.55%, 72% 76.35%, 73% 76.9%, 74% 77.3%, 75% 77.5%, 76% 77.45%, 77% 77.25%, 78% 76.8%, 79% 76.2%, 80% 75.4%, 81% 74.45%, 82% 73.4%, 83% 72.25%, 84% 71.05%, 85% 69.8%, 86% 68.55%, 87% 67.35%, 88% 66.2%, 89% 65.2%, 90% 64.3%, 91% 63.55%, 92% 63%, 93% 62.65%, 94% 62.5%, 95% 62.55%, 96% 62.8%, 97% 63.3%, 98% 63.9%, 99% 64.75%, 100% 65.7%);
|
||||
clip-path: polygon(100% 0, 0 0, 0 77.5%, 1% 77.4%, 2% 77.1%, 3% 76.6%, 4% 75.9%, 5% 75.05%, 6% 74.05%, 7% 72.95%, 8% 71.75%, 9% 70.55%, 10% 69.3%, 11% 68.05%, 12% 66.9%, 13% 65.8%, 14% 64.8%, 15% 64%, 16% 63.35%, 17% 62.85%, 18% 62.6%, 19% 62.5%, 20% 62.65%, 21% 63%, 22% 63.5%, 23% 64.2%, 24% 65.1%, 25% 66.1%, 26% 67.2%, 27% 68.4%, 28% 69.65%, 29% 70.9%, 30% 72.15%, 31% 73.3%, 32% 74.35%, 33% 75.3%, 34% 76.1%, 35% 76.75%, 36% 77.2%, 37% 77.45%, 38% 77.5%, 39% 77.3%, 40% 76.95%, 41% 76.4%, 42% 75.65%, 43% 74.75%, 44% 73.75%, 45% 72.6%, 46% 71.4%, 47% 70.15%, 48% 68.9%, 49% 67.7%, 50% 66.55%, 51% 65.5%, 52% 64.55%, 53% 63.75%, 54% 63.15%, 55% 62.75%, 56% 62.55%, 57% 62.5%, 58% 62.7%, 59% 63.1%, 60% 63.7%, 61% 64.45%, 62% 65.4%, 63% 66.45%, 64% 67.6%, 65% 68.8%, 66% 70.05%, 67% 71.3%, 68% 72.5%, 69% 73.6%, 70% 74.65%, 71% 75.55%, 72% 76.35%, 73% 76.9%, 74% 77.3%, 75% 77.5%, 76% 77.45%, 77% 77.25%, 78% 76.8%, 79% 76.2%, 80% 75.4%, 81% 74.45%, 82% 73.4%, 83% 72.25%, 84% 71.05%, 85% 69.8%, 86% 68.55%, 87% 67.35%, 88% 66.2%, 89% 65.2%, 90% 64.3%, 91% 63.55%, 92% 63%, 93% 62.65%, 94% 62.5%, 95% 62.55%, 96% 62.8%, 97% 63.3%, 98% 63.9%, 99% 64.75%, 100% 65.7%)
|
||||
}
|
||||
@@ -237,57 +165,51 @@ body:before {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pure-button-primary,
|
||||
a.pure-button-primary,
|
||||
.pure-button-selected,
|
||||
a.pure-button-selected {
|
||||
background-color: var(--color-background-button-primary);
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
color: var(--color-text-button);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.button-success {
|
||||
background: var(--color-background-button-success);
|
||||
background: rgb(28, 184, 65);
|
||||
/* this is a green */
|
||||
}
|
||||
|
||||
.button-tag {
|
||||
background: var(--color-background-button-tag);
|
||||
color: var(--color-text-button);
|
||||
background: rgb(99, 99, 99);
|
||||
color: #fff;
|
||||
font-size: 65%;
|
||||
border-bottom-left-radius: initial;
|
||||
border-bottom-right-radius: initial;
|
||||
|
||||
&.active {
|
||||
background: var(--color-background-button-tag-active);
|
||||
background: #9c9c9c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.button-error {
|
||||
background: var(--color-background-button-error);
|
||||
color: var(--color-text-button-error);
|
||||
background: rgb(202, 60, 60);
|
||||
/* this is a maroon */
|
||||
}
|
||||
|
||||
.button-warning {
|
||||
background: var(--color-background-button-warning);
|
||||
color: var(--color-text-button-warning);
|
||||
background: rgb(223, 117, 20);
|
||||
/* this is an orange */
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background: var(--color-background-button-secondary);
|
||||
background: rgb(66, 184, 221);
|
||||
/* this is a light blue */
|
||||
}
|
||||
|
||||
|
||||
.button-cancel {
|
||||
background: var(--color-background-button-cancel);
|
||||
}
|
||||
|
||||
#save_button {
|
||||
margin-right: 1rem;
|
||||
background: rgb(200, 200, 200);
|
||||
/* this is a green */
|
||||
}
|
||||
|
||||
.messages {
|
||||
@@ -295,56 +217,50 @@ a.pure-button-selected {
|
||||
list-style: none;
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
color: var(--color-text-messages);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
|
||||
&.message {
|
||||
background: var(--color-background-messages-message);
|
||||
background: rgba(255, 255, 255, .2);
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: var(--color-background-messages-error);
|
||||
background: rgba(255, 1, 1, .5);
|
||||
}
|
||||
|
||||
&.notice {
|
||||
background: var(--color-background-messages-notice);
|
||||
background: rgba(255, 255, 255, .5);
|
||||
}
|
||||
}
|
||||
|
||||
&.with-share-link {
|
||||
>*:hover {
|
||||
> *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notifications-wrapper {
|
||||
padding: 0.5rem 0 1rem 0;
|
||||
}
|
||||
|
||||
#notification-customisation {
|
||||
border: 1px solid var(--color-border-notification);
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#notification-error-log {
|
||||
border: 1px solid var(--color-border-notification);
|
||||
border: 1px solid #ccc;
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
#token-table {
|
||||
|
||||
&.pure-table td,
|
||||
&.pure-table th {
|
||||
&.pure-table td, &.pure-table th {
|
||||
font-size: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
#new-watch-form {
|
||||
background: var(--color-background-new-watch-form);
|
||||
background: rgba(0, 0, 0, .05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
@@ -354,25 +270,19 @@ a.pure-button-selected {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
input:not(.pure-button) {
|
||||
background-color: var(--color-background-new-watch-input);
|
||||
color: var(--color-text-new-watch-input);
|
||||
}
|
||||
|
||||
.label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
legend {
|
||||
color: var(--color-text-legend);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#watch-add-wrapper-zone {
|
||||
>div {
|
||||
> div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 760px) {
|
||||
#url {
|
||||
width: 100%;
|
||||
@@ -390,15 +300,15 @@ a.pure-button-selected {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 120px;
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
box-shadow: 1px 1px 4px var(--color-shadow-jump);
|
||||
box-shadow: 5px 0 5px -2px #888;
|
||||
|
||||
a {
|
||||
color: var(--color-link);
|
||||
cursor: pointer;
|
||||
color: #1b98f8;
|
||||
cursor: grabbing;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
@@ -409,8 +319,8 @@ a.pure-button-selected {
|
||||
|
||||
footer {
|
||||
padding: 10px;
|
||||
background: var(--color-background);
|
||||
color: var(--color-text-footer);
|
||||
background: #fff;
|
||||
color: #444;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -433,7 +343,7 @@ footer {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
font-size: 65%;
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
|
||||
&#left-sticky {
|
||||
@@ -452,12 +362,10 @@ footer {
|
||||
}
|
||||
|
||||
#new-version-text a {
|
||||
color: var(--color-link-new-version);
|
||||
color: #e07171;
|
||||
}
|
||||
|
||||
.watch-controls {
|
||||
color: #f8321b;
|
||||
|
||||
.state-on {
|
||||
img {
|
||||
opacity: 0.8;
|
||||
@@ -475,6 +383,7 @@ footer {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.monospaced-textarea {
|
||||
@@ -483,8 +392,7 @@ footer {
|
||||
font-family: monospace;
|
||||
white-space: pre;
|
||||
overflow-wrap: normal;
|
||||
// No scrollbars until needed.
|
||||
overflow-x: auto;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,9 +407,7 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
.pure-control-group,
|
||||
.pure-group,
|
||||
.pure-controls {
|
||||
.pure-control-group, .pure-group, .pure-controls {
|
||||
padding-bottom: 1em;
|
||||
|
||||
div {
|
||||
@@ -509,32 +415,28 @@ footer {
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
>* {
|
||||
> * {
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
>label {
|
||||
> label {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
legend {
|
||||
color: var(--color-text-legend);
|
||||
}
|
||||
}
|
||||
|
||||
/* The input fields with errors */
|
||||
.error {
|
||||
input {
|
||||
background-color: var(--color-error-input);
|
||||
background-color: #ffebeb;
|
||||
}
|
||||
}
|
||||
|
||||
/* The list of errors */
|
||||
ul.errors {
|
||||
padding: .5em .6em;
|
||||
border: 1px solid var(--color-error-list);
|
||||
border: 1px solid #dd0000;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
-webkit-box-sizing: border-box;
|
||||
@@ -542,7 +444,7 @@ footer {
|
||||
|
||||
li {
|
||||
margin-left: 1em;
|
||||
color: var(--color-error-list);
|
||||
color: #dd0000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +462,7 @@ footer {
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
>* {
|
||||
> * {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@@ -569,25 +471,21 @@ footer {
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
@media only screen and (max-width: 760px), (min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
.box {
|
||||
max-width: 95%
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#nav-menu {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 800px) {
|
||||
@media only screen and (max-width: 760px), (min-device-width: 768px) and (max-device-width: 800px) {
|
||||
|
||||
div.sticky-tab#hosted-sticky {
|
||||
top: 60px;
|
||||
@@ -616,29 +514,24 @@ footer {
|
||||
and also iPads specifically.
|
||||
*/
|
||||
.watch-table {
|
||||
|
||||
/* Force table to not be like tables anymore */
|
||||
thead,
|
||||
tbody,
|
||||
th,
|
||||
td,
|
||||
tr {
|
||||
thead, tbody, th, td, tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.last-checked {
|
||||
>span {
|
||||
> span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.last-checked::before {
|
||||
color: var(--color-last-checked);
|
||||
color: #555;
|
||||
content: "Last Checked ";
|
||||
}
|
||||
|
||||
.last-changed::before {
|
||||
color: var(--color-last-checked);
|
||||
color: #555;
|
||||
content: "Last Changed ";
|
||||
}
|
||||
|
||||
@@ -654,17 +547,15 @@ footer {
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
.pure-table td,
|
||||
.pure-table th {
|
||||
.pure-table td, .pure-table th {
|
||||
border: none;
|
||||
}
|
||||
|
||||
td {
|
||||
/* Behave like a "row" */
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--color-border-watch-table-cell);
|
||||
border-bottom: 1px solid #eee;
|
||||
vertical-align: middle;
|
||||
|
||||
&:before {
|
||||
/* Top/left values mimic padding */
|
||||
top: 6px;
|
||||
@@ -677,11 +568,11 @@ footer {
|
||||
|
||||
&.pure-table-striped {
|
||||
tr {
|
||||
background-color: var(--color-table-background);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
tr:nth-child(2n-1) {
|
||||
background-color: var(--color-table-stripe);
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
tr:nth-child(2n-1) td {
|
||||
@@ -692,66 +583,12 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
.pure-table {
|
||||
border-color: var(--color-border-table-cell);
|
||||
|
||||
thead {
|
||||
background-color: var(--color-background-table-thead);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border-left-color: var(--color-border-table-cell);
|
||||
}
|
||||
}
|
||||
|
||||
.pure-table-striped {
|
||||
tr:nth-child(2n-1) {
|
||||
td {
|
||||
background-color: var(--color-table-stripe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pure-form input[type=color],
|
||||
.pure-form input[type=date],
|
||||
.pure-form input[type=datetime-local],
|
||||
.pure-form input[type=datetime],
|
||||
.pure-form input[type=email],
|
||||
.pure-form input[type=month],
|
||||
.pure-form input[type=number],
|
||||
.pure-form input[type=password],
|
||||
.pure-form input[type=search],
|
||||
.pure-form input[type=tel],
|
||||
.pure-form input[type=text],
|
||||
.pure-form input[type=time],
|
||||
.pure-form input[type=url],
|
||||
.pure-form input[type=week],
|
||||
.pure-form select,
|
||||
.pure-form textarea {
|
||||
border: var(--color-border-input);
|
||||
box-shadow: inset 0 1px 3px var(--color-shadow-input);
|
||||
background-color: var(--color-background-input);
|
||||
color: var(--color-text-input);
|
||||
|
||||
&:active {
|
||||
background-color: var(--color-background-input);
|
||||
}
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--color-text-input-placeholder);
|
||||
}
|
||||
|
||||
|
||||
/** Desktop vs mobile input field strategy
|
||||
- We dont use 'size' with <input> because `size` is too unreliable to override, and will often push-out
|
||||
- Rely always on width in CSS
|
||||
*/
|
||||
@media only screen and (min-width: 761px) {
|
||||
|
||||
/* m-d is medium-desktop */
|
||||
.m-d {
|
||||
min-width: 80%;
|
||||
@@ -768,23 +605,16 @@ textarea::placeholder {
|
||||
li {
|
||||
margin-right: 3px;
|
||||
display: inline-block;
|
||||
color: var(--color-text-tab);
|
||||
color: #fff;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
background-color: var(--color-background-tab);
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
&:not(.active) {
|
||||
&:hover {
|
||||
background-color: var(--color-background-tab-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.active,
|
||||
:target {
|
||||
background-color: var(--color-background);
|
||||
&.active, :target {
|
||||
background-color: #fff;
|
||||
|
||||
a {
|
||||
color: var(--color-text-tab-active);
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
@@ -792,24 +622,22 @@ textarea::placeholder {
|
||||
a {
|
||||
display: block;
|
||||
padding: 0.8em;
|
||||
color: var(--color-text-tab);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form-edge-padding: 20px;
|
||||
|
||||
.pure-form-stacked {
|
||||
>div:first-child {
|
||||
> div:first-child {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
.inner {
|
||||
background: var(--color-background);
|
||||
;
|
||||
background: #fff;;
|
||||
padding: $form-edge-padding;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -839,13 +667,11 @@ $form-edge-padding: 20px;
|
||||
#selector-header {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
body.full-width {
|
||||
.edit-form {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
min-width: 70%;
|
||||
/* so it cant overflow */
|
||||
@@ -856,18 +682,17 @@ body.full-width {
|
||||
}
|
||||
|
||||
.inner {
|
||||
background: var(--color-background);
|
||||
background: #fff;;
|
||||
padding: $form-edge-padding;
|
||||
}
|
||||
|
||||
#actions {
|
||||
display: block;
|
||||
background: var(--color-background);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.pure-form-message-inline {
|
||||
padding-left: 0;
|
||||
color: var(--color-text-input-description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -891,15 +716,14 @@ ul {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
|
||||
//width: 100%;
|
||||
>img {
|
||||
> img {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
>canvas {
|
||||
> canvas {
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
max-width: 100%;
|
||||
@@ -927,61 +751,54 @@ ul {
|
||||
}
|
||||
|
||||
#api-key-copy {
|
||||
color: var(--color-api-key);
|
||||
color: #0078e7;
|
||||
}
|
||||
|
||||
.button-green {
|
||||
background-color: var(--color-background-button-green);
|
||||
background-color: #42dd53;
|
||||
}
|
||||
|
||||
.button-red {
|
||||
background-color: var(--color-background-button-red);
|
||||
background-color: #dd4242;
|
||||
}
|
||||
|
||||
.noselect {
|
||||
-webkit-touch-callout: none;
|
||||
/* iOS Safari */
|
||||
-webkit-user-select: none;
|
||||
/* Safari */
|
||||
-moz-user-select: none;
|
||||
/* Old versions of Firefox */
|
||||
-ms-user-select: none;
|
||||
/* Internet Explorer/Edge */
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-moz-user-select: none; /* Old versions of Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none;
|
||||
/* Non-prefixed version, currently
|
||||
supported by Chrome, Edge, Opera and Firefox */
|
||||
supported by Chrome, Edge, Opera and Firefox */
|
||||
}
|
||||
|
||||
.snapshot-age {
|
||||
padding: 4px;
|
||||
margin: 0.5rem 0;
|
||||
background-color: var(--color-background-snapshot-age);
|
||||
background-color: #dfdfdf;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&.error {
|
||||
background-color: var(--color-error-background-snapshot-age);
|
||||
color: var(--color-error-text-snapshot-age);
|
||||
background-color: #ff0000;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
#checkbox-operations {
|
||||
background: var(--color-background-checkbox-operations);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
padding: 1em;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.checkbox-uuid {
|
||||
>* {
|
||||
> * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.inline-warning {
|
||||
>span {
|
||||
> span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -992,8 +809,8 @@ ul {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
border: 1px solid var(--color-border-warning);
|
||||
border: 1px solid #ff3300;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
color: var(--color-warning);
|
||||
color: #ff3300;
|
||||
}
|
||||
@@ -289,7 +289,6 @@ class ChangeDetectionStore:
|
||||
# List of permissible attributes we accept from the wild internet
|
||||
for k in [
|
||||
'body',
|
||||
'browser_steps',
|
||||
'css_filter',
|
||||
'extract_text',
|
||||
'extract_title_as_title',
|
||||
|
||||
@@ -18,13 +18,12 @@
|
||||
<li><code>tgram://</code> only supports very limited HTML and can fail when extra tags are sent, <a href="https://core.telegram.org/bots/api#html-style">read more here</a> (or use plaintext/markdown format)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="notifications-wrapper">
|
||||
<a id="send-test-notification" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Send test notification</a>
|
||||
{% if emailprefix %}
|
||||
<a id="add-email-helper" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Add email</a>
|
||||
{% endif %}
|
||||
<a href="{{url_for('notification_logs')}}" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Notification debug logs</a>
|
||||
</div>
|
||||
<br/>
|
||||
<a id="send-test-notification" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Send test notification</a>
|
||||
{% if emailprefix %}
|
||||
<a id="add-email-helper" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Add email</a>
|
||||
{% endif %}
|
||||
<a href="{{url_for('notification_logs')}}" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Notification debug logs</a>
|
||||
</div>
|
||||
<div id="notification-customisation" class="pure-control-group">
|
||||
<div class="pure-control-group">
|
||||
|
||||
@@ -1,152 +1,110 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-darkmode="{{ dark_mode|lower }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<meta name="description" content="Self hosted website change detection."/>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Self hosted website change detection.">
|
||||
<title>Change Detection{{extra_title}}</title>
|
||||
<link rel="alternate" type="application/rss+xml" title="Changedetection.io » Feed{% if active_tag %}- {{active_tag}}{% endif %}" href="{{ url_for('rss', tag=active_tag , token=app_rss_token)}}"/>
|
||||
<link rel="stylesheet" href="{{url_for('static_content', group='styles', filename='pure-min.css')}}"/>
|
||||
<link rel="stylesheet" href="{{url_for('static_content', group='styles', filename='styles.css')}}"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="Changedetection.io » Feed{% if active_tag %}- {{active_tag}}{% endif %}" href="{{ url_for('rss', tag=active_tag , token=app_rss_token)}}" />
|
||||
<link rel="stylesheet" href="{{url_for('static_content', group='styles', filename='pure-min.css')}}">
|
||||
<link rel="stylesheet" href="{{url_for('static_content', group='styles', filename='styles.css')}}">
|
||||
{% if extra_stylesheets %}
|
||||
{% for m in extra_stylesheets %}
|
||||
<link rel="stylesheet" href="{{ m }}?ver=1000"/>
|
||||
{% endfor %}
|
||||
{% for m in extra_stylesheets %}
|
||||
<link rel="stylesheet" href="{{ m }}?ver=1000">
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{url_for('static_content', group='favicons', filename='apple-touch-icon.png')}}"/>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{url_for('static_content', group='favicons', filename='favicon-32x32.png')}}"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{url_for('static_content', group='favicons', filename='favicon-16x16.png')}}"/>
|
||||
<link rel="manifest" href="{{url_for('static_content', group='favicons', filename='site.webmanifest')}}"/>
|
||||
<link rel="mask-icon" href="{{url_for('static_content', group='favicons', filename='safari-pinned-tab.svg')}}" color="#5bbad5"/>
|
||||
<link rel="shortcut icon" href="{{url_for('static_content', group='favicons', filename='favicon.ico')}}"/>
|
||||
<meta name="msapplication-TileColor" content="#da532c"/>
|
||||
<meta name="msapplication-config" content="favicons/browserconfig.xml"/>
|
||||
<meta name="theme-color" content="#ffffff"/>
|
||||
|
||||
<style>
|
||||
body::before {
|
||||
background-image: url({{url_for('static_content', group='images', filename='gradient-border.png') }});
|
||||
}
|
||||
body::before {
|
||||
background-image: url({{url_for('static_content', group='images', filename='gradient-border.png')}});
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='jquery-3.6.0.min.js')}}"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed" id="nav-menu">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="header">
|
||||
|
||||
<div class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed" id="nav-menu">
|
||||
{% if has_password and not current_user.is_authenticated %}
|
||||
<a class="pure-menu-heading" href="https://github.com/dgtlmoon/changedetection.io" rel="noopener">
|
||||
<strong>Change</strong>Detection.io</a>
|
||||
<a class="pure-menu-heading" href="https://github.com/dgtlmoon/changedetection.io" rel="noopener"><strong>Change</strong>Detection.io</a>
|
||||
{% else %}
|
||||
<a class="pure-menu-heading" href="{{url_for('index')}}">
|
||||
<strong>Change</strong>Detection.io</a>
|
||||
<a class="pure-menu-heading" href="{{url_for('index')}}"><strong>Change</strong>Detection.io</a>
|
||||
{% endif %}
|
||||
{% if current_diff_url %}
|
||||
<a class="current-diff-url" href="{{ current_diff_url }}">
|
||||
<span style="max-width: 30%; overflow: hidden">{{ current_diff_url }}</span></a>
|
||||
<a class=current-diff-url href="{{ current_diff_url }}"><span style="max-width: 30%; overflow: hidden;">{{ current_diff_url }}</span></a>
|
||||
{% else %}
|
||||
{% if new_version_available and not(has_password and not current_user.is_authenticated) %}
|
||||
<span id="new-version-text" class="pure-menu-heading">
|
||||
<a href="https://github.com/dgtlmoon/changedetection.io">A new version is available</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if new_version_available and not (has_password and not current_user.is_authenticated) %}
|
||||
<span id="new-version-text" class="pure-menu-heading"><a href="https://github.com/dgtlmoon/changedetection.io">A new version is available</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<ul class="pure-menu-list" id="top-right-menu">
|
||||
{% if current_user.is_authenticated or not has_password %}
|
||||
{% if not
|
||||
current_diff_url %}
|
||||
<li class="pure-menu-item">
|
||||
<ul class="pure-menu-list" id="top-right-menu">
|
||||
{% if current_user.is_authenticated or not has_password %}
|
||||
{% if not current_diff_url %}
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{ url_for('settings_page')}}" class="pure-menu-link">SETTINGS</a>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{ url_for('import_page')}}" class="pure-menu-link">IMPORT</a>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{ url_for('get_backup')}}" class="pure-menu-link">BACKUP</a>
|
||||
</li>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="pure-menu-item">
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{ url_for('edit_page', uuid=uuid, next='diff') }}" class="pure-menu-link">EDIT</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="https://github.com/dgtlmoon/changedetection.io">Website Change Detection and Notification.</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{url_for('logout')}}" class="pure-menu-link">LOG OUT</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="pure-menu-item">
|
||||
{% if dark_mode %}
|
||||
{% set darkClass = 'dark' %}
|
||||
{% endif %}
|
||||
<button class="toggle-theme {{darkClass}}" type="button">
|
||||
<span class="visually-hidden">Toggle light/dark mode</span>
|
||||
<span class="icon-light">
|
||||
{% include "svgs/light-mode-toggle-icon.svg" %}
|
||||
</span>
|
||||
<span class="icon-dark">
|
||||
{% include "svgs/dark-mode-toggle-icon.svg" %}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a class="github-link" href="https://github.com/dgtlmoon/changedetection.io">
|
||||
{% include "svgs/github.svg" %}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="pure-menu-item">
|
||||
<a class="pure-menu-link" href="https://github.com/dgtlmoon/changedetection.io">Website Change Detection and Notification.</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="pure-menu-item"><a href="{{url_for('logout')}}" class="pure-menu-link">LOG OUT</a></li>
|
||||
{% endif %}
|
||||
<li class="pure-menu-item"><a class="github-link" href="https://github.com/dgtlmoon/changedetection.io">
|
||||
<svg class="octicon octicon-mark-github v-align-middle" height="32" viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="32" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
</svg>
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if hosted_sticky %}
|
||||
<div class="sticky-tab" id="hosted-sticky">
|
||||
<a href="https://lemonade.changedetection.io/start?ref={{guid}}">Let us host your instance!</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if left_sticky %}
|
||||
<div class="sticky-tab" id="left-sticky">
|
||||
<a href="{{url_for('preview_page', uuid=uuid)}}">Show current snapshot</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if right_sticky %}
|
||||
<div class="sticky-tab" id="right-sticky">{{ right_sticky }}</div>
|
||||
{% endif %}
|
||||
<section class="content">
|
||||
<header>
|
||||
</div>
|
||||
{% if hosted_sticky %}<div class="sticky-tab" id="hosted-sticky"><a href="https://lemonade.changedetection.io/start?ref={{guid}}">Let us host your instance!</a></div>{% endif %}
|
||||
{% if left_sticky %}<div class="sticky-tab" id="left-sticky"><a href="{{url_for('preview_page', uuid=uuid)}}">Show current snapshot</a></div> {% endif %}
|
||||
{% if right_sticky %}<div class="sticky-tab" id="right-sticky">{{ right_sticky }}</div> {% endif %}
|
||||
<section class="content">
|
||||
<header>
|
||||
{% block header %}{% endblock %}
|
||||
</header>
|
||||
</header>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories = true) %}
|
||||
{% if
|
||||
messages %}
|
||||
<ul class="messages">
|
||||
{% for category, message in messages %}
|
||||
<li class="{{ category }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<ul class=messages>
|
||||
{% for category, message in messages %}
|
||||
<li class="{{ category }}">{{ message }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% if session['share-link'] %}
|
||||
{% endwith %}
|
||||
|
||||
{% if session['share-link'] %}
|
||||
<ul class="messages with-share-link">
|
||||
<li class="message">
|
||||
Share this link:
|
||||
<span id="share-link">{{ session['share-link'] }}</span>
|
||||
<img style="height: 1em; display: inline-block" src="{{url_for('static_content', group='images', filename='copy.svg')}}"/>
|
||||
</li>
|
||||
<li class="message">Share this link: <span id="share-link">{{ session['share-link'] }}</span> <img style="height: 1em;display:inline-block;" src="{{url_for('static_content', group='images', filename='copy.svg')}}" /></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{url_for('static_content', group='js', filename='toggle-theme.js')}}"
|
||||
defer></script>
|
||||
</body>
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% endblock %}
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,49 +1,32 @@
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="edit-form">
|
||||
<div class="box-wrap inner">
|
||||
<form
|
||||
class="pure-form pure-form-stacked"
|
||||
action="{{url_for('clear_all_history')}}"
|
||||
method="POST"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
This will remove version history (snapshots) for ALL watches, but keep
|
||||
your list of URLs! <br />
|
||||
You may like to use the <strong>BACKUP</strong> link first.<br />
|
||||
</div>
|
||||
<br />
|
||||
<div class="pure-control-group">
|
||||
<label for="confirmtext">Confirmation text</label>
|
||||
<input
|
||||
type="text"
|
||||
id="confirmtext"
|
||||
required=""
|
||||
name="confirmtext"
|
||||
value=""
|
||||
size="10"
|
||||
/>
|
||||
<span class="pure-form-message-inline"
|
||||
>Type in the word <strong>clear</strong> to confirm that you
|
||||
understand.</span
|
||||
>
|
||||
</div>
|
||||
<br />
|
||||
<div class="pure-control-group">
|
||||
<button type="submit" class="pure-button pure-button-primary">
|
||||
Clear History!
|
||||
</button>
|
||||
</div>
|
||||
<br />
|
||||
<div class="pure-control-group">
|
||||
<a href="{{url_for('index')}}" class="pure-button button-cancel"
|
||||
>Cancel</a
|
||||
>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="box-wrap inner">
|
||||
<form class="pure-form pure-form-stacked" action="{{url_for('clear_all_history')}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
This will remove version history (snapshots) for ALL watches, but keep your list of URLs! <br/>
|
||||
You may like to use the <strong>BACKUP</strong> link first.<br/>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="pure-control-group">
|
||||
<label for="confirmtext">Confirmation text</label>
|
||||
<input type="text" id="confirmtext" required="" name="confirmtext" value="" size="10"/>
|
||||
<span class="pure-form-message-inline">Type in the word <strong>clear</strong> to confirm that you understand.</span>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="pure-control-group">
|
||||
<button type="submit" class="pure-button pure-button-primary">Clear History!</button>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="pure-control-group">
|
||||
<a href="{{url_for('index')}}" class="pure-button button-small button-cancel">Cancel</a>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,290 +1,186 @@
|
||||
{% extends 'base.html' %} {% block content %} {% from '_helpers.jinja' import
|
||||
render_field, render_checkbox_field, render_button %} {% from
|
||||
'_common_fields.jinja' import render_common_settings_form %}
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% from '_helpers.jinja' import render_field, render_checkbox_field, render_button %}
|
||||
{% from '_common_fields.jinja' import render_common_settings_form %}
|
||||
<script>
|
||||
const notification_base_url="{{url_for('ajax_callback_send_notification_test')}}";
|
||||
{% if emailprefix %}
|
||||
const email_notification_prefix=JSON.parse('{{emailprefix|tojson}}');
|
||||
{% endif %}
|
||||
const notification_base_url="{{url_for('ajax_callback_send_notification_test')}}";
|
||||
{% if emailprefix %}
|
||||
const email_notification_prefix=JSON.parse('{{emailprefix|tojson}}');
|
||||
{% endif %}
|
||||
</script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{url_for('static_content', group='js', filename='tabs.js')}}"
|
||||
defer
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{url_for('static_content', group='js', filename='notifications.js')}}"
|
||||
defer
|
||||
></script>
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script>
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='notifications.js')}}" defer></script>
|
||||
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="{{url_for('static_content', group='js', filename='global-settings.js')}}"
|
||||
defer
|
||||
></script>
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='global-settings.js')}}" defer></script>
|
||||
<div class="edit-form">
|
||||
<div class="tabs collapsable">
|
||||
<ul>
|
||||
<li class="tab" id=""><a href="#general">General</a></li>
|
||||
<li class="tab"><a href="#notifications">Notifications</a></li>
|
||||
<li class="tab"><a href="#fetching">Fetching</a></li>
|
||||
<li class="tab"><a href="#filters">Global Filters</a></li>
|
||||
<li class="tab"><a href="#api">API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box-wrap inner">
|
||||
<form
|
||||
class="pure-form pure-form-stacked settings"
|
||||
action="{{url_for('settings_page')}}"
|
||||
method="POST"
|
||||
>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="tab-pane-inner" id="general">
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.requests.form.time_between_check,
|
||||
class="time-check-widget") }}
|
||||
<span class="pure-form-message-inline"
|
||||
>Default time for all watches, when the watch does not have a
|
||||
specific time setting.</span
|
||||
>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.requests.form.jitter_seconds,
|
||||
class="jitter_seconds") }}
|
||||
<span class="pure-form-message-inline"
|
||||
>Example - 3 seconds random jitter could trigger up to 3 seconds
|
||||
earlier or up to 3 seconds later</span
|
||||
>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{
|
||||
render_field(form.application.form.filter_failure_notification_threshold_attempts,
|
||||
class="filter_failure_notification_threshold_attempts") }}
|
||||
<span class="pure-form-message-inline"
|
||||
>After this many consecutive times that the CSS/xPath filter is
|
||||
missing, send a notification
|
||||
<br />
|
||||
Set to <strong>0</strong> to disable
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{% if not hide_remove_pass %} {% if current_user.is_authenticated %}
|
||||
{{ render_button(form.application.form.removepassword_button) }} {%
|
||||
else %} {{ render_field(form.application.form.password) }}
|
||||
<span class="pure-form-message-inline"
|
||||
>Password protection for your changedetection.io
|
||||
application.</span
|
||||
>
|
||||
{% endif %} {% else %}
|
||||
<span class="pure-form-message-inline">Password is locked.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tabs collapsable">
|
||||
<ul>
|
||||
<li class="tab" id=""><a href="#general">General</a></li>
|
||||
<li class="tab"><a href="#notifications">Notifications</a></li>
|
||||
<li class="tab"><a href="#fetching">Fetching</a></li>
|
||||
<li class="tab"><a href="#filters">Global Filters</a></li>
|
||||
<li class="tab"><a href="#api">API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box-wrap inner">
|
||||
<form class="pure-form pure-form-stacked settings" action="{{url_for('settings_page')}}" method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="tab-pane-inner" id="general">
|
||||
<fieldset>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.requests.form.time_between_check, class="time-check-widget") }}
|
||||
<span class="pure-form-message-inline">Default time for all watches, when the watch does not have a specific time setting.</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.requests.form.jitter_seconds, class="jitter_seconds") }}
|
||||
<span class="pure-form-message-inline">Example - 3 seconds random jitter could trigger up to 3 seconds earlier or up to 3 seconds later</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.filter_failure_notification_threshold_attempts, class="filter_failure_notification_threshold_attempts") }}
|
||||
<span class="pure-form-message-inline">After this many consecutive times that the CSS/xPath filter is missing, send a notification
|
||||
<br/>
|
||||
Set to <strong>0</strong> to disable
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{% if not hide_remove_pass %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{{ render_button(form.application.form.removepassword_button) }}
|
||||
{% else %}
|
||||
{{ render_field(form.application.form.password) }}
|
||||
<span class="pure-form-message-inline">Password protection for your changedetection.io application.</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="pure-form-message-inline">Password is locked.</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.base_url,
|
||||
placeholder="http://yoursite.com:5000/", class="m-d") }}
|
||||
<span class="pure-form-message-inline">
|
||||
Base URL used for the <code>{base_url}</code> token in
|
||||
notifications and RSS links.<br />Default value is the ENV var
|
||||
'BASE_URL' (Currently
|
||||
"{{settings_application['current_base_url']}}"),
|
||||
<a
|
||||
href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting"
|
||||
>read more here</a
|
||||
>.
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.base_url, placeholder="http://yoursite.com:5000/",
|
||||
class="m-d") }}
|
||||
<span class="pure-form-message-inline">
|
||||
Base URL used for the <code>{base_url}</code> token in notifications and RSS links.<br/>Default value is the ENV var 'BASE_URL' (Currently "{{settings_application['current_base_url']}}"),
|
||||
<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Configurable-BASE_URL-setting">read more here</a>.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
{{
|
||||
render_checkbox_field(form.application.form.extract_title_as_title)
|
||||
}}
|
||||
<span class="pure-form-message-inline"
|
||||
>Note: This will automatically apply to all existing
|
||||
watches.</span
|
||||
>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{
|
||||
render_checkbox_field(form.application.form.empty_pages_are_a_change)
|
||||
}}
|
||||
<span class="pure-form-message-inline"
|
||||
>When a page contains HTML, but no renderable text appears (empty
|
||||
page), is this considered a change?</span
|
||||
>
|
||||
</div>
|
||||
{% if form.requests.proxy %}
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_field(form.requests.form.proxy,
|
||||
class="fetch-backend-proxy") }}
|
||||
<span class="pure-form-message-inline">
|
||||
Choose a default proxy for all watches
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.application.form.extract_title_as_title) }}
|
||||
<span class="pure-form-message-inline">Note: This will automatically apply to all existing watches.</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.application.form.empty_pages_are_a_change) }}
|
||||
<span class="pure-form-message-inline">When a page contains HTML, but no renderable text appears (empty page), is this considered a change?</span>
|
||||
</div>
|
||||
{% if form.requests.proxy %}
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_field(form.requests.form.proxy, class="fetch-backend-proxy") }}
|
||||
<span class="pure-form-message-inline">
|
||||
Choose a default proxy for all watches
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane-inner" id="notifications">
|
||||
<fieldset>
|
||||
<div class="field-group">
|
||||
{{ render_common_settings_form(form.application.form, emailprefix,
|
||||
settings_application) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="tab-pane-inner" id="notifications">
|
||||
<fieldset>
|
||||
<div class="field-group">
|
||||
{{ render_common_settings_form(form.application.form, emailprefix, settings_application) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane-inner" id="fetching">
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_field(form.application.form.fetch_backend,
|
||||
class="fetch-backend") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<p>
|
||||
Use the <strong>Basic</strong> method (default) where your watched
|
||||
sites don't need Javascript to render.
|
||||
</p>
|
||||
<p>
|
||||
The <strong>Chrome/Javascript</strong> method requires a network
|
||||
connection to a running WebDriver+Chrome server, set by the ENV
|
||||
var 'WEBDRIVER_URL'.
|
||||
</p>
|
||||
</span>
|
||||
<br />
|
||||
Tip:
|
||||
<a
|
||||
href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support"
|
||||
>Connect using BrightData Proxies, find out more here.</a
|
||||
>
|
||||
</div>
|
||||
<fieldset class="pure-group" id="webdriver-override-options">
|
||||
<div class="pure-form-message-inline">
|
||||
<strong
|
||||
>If you're having trouble waiting for the page to be fully
|
||||
rendered (text missing etc), try increasing the 'wait' time
|
||||
here.</strong
|
||||
>
|
||||
<br />
|
||||
This will wait <i>n</i> seconds before extracting the text.
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.webdriver_delay) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="tab-pane-inner" id="fetching">
|
||||
<div class="pure-control-group inline-radio">
|
||||
{{ render_field(form.application.form.fetch_backend, class="fetch-backend") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<p>Use the <strong>Basic</strong> method (default) where your watched sites don't need Javascript to render.</p>
|
||||
<p>The <strong>Chrome/Javascript</strong> method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'. </p>
|
||||
</span>
|
||||
<br/>
|
||||
Tip: <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support">Connect using BrightData Proxies, find out more here.</a>
|
||||
</div>
|
||||
<fieldset class="pure-group" id="webdriver-override-options">
|
||||
<div class="pure-form-message-inline">
|
||||
<strong>If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here.</strong>
|
||||
<br/>
|
||||
This will wait <i>n</i> seconds before extracting the text.
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.application.form.webdriver_delay) }}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane-inner" id="filters">
|
||||
<fieldset class="pure-group">
|
||||
{{ render_checkbox_field(form.application.form.ignore_whitespace) }}
|
||||
<span class="pure-form-message-inline"
|
||||
>Ignore whitespace, tabs and new-lines/line-feeds when considering
|
||||
if a change was detected.<br />
|
||||
<i>Note:</i> Changing this will change the status of your existing
|
||||
watches, possibly trigger alerts etc.
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{
|
||||
render_checkbox_field(form.application.form.render_anchor_tag_content)
|
||||
}}
|
||||
<span class="pure-form-message-inline"
|
||||
>Render anchor tag content, default disabled, when enabled renders
|
||||
links as <code>(link text)[https://somesite.com]</code>
|
||||
<br />
|
||||
<i>Note:</i> Changing this could affect the content of your existing
|
||||
watches, possibly trigger alerts etc.
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{ render_field(form.application.form.global_subtractive_selectors,
|
||||
rows=5, placeholder="header
|
||||
<div class="tab-pane-inner" id="filters">
|
||||
|
||||
<fieldset class="pure-group">
|
||||
{{ render_checkbox_field(form.application.form.ignore_whitespace) }}
|
||||
<span class="pure-form-message-inline">Ignore whitespace, tabs and new-lines/line-feeds when considering if a change was detected.<br/>
|
||||
<i>Note:</i> Changing this will change the status of your existing watches, possibly trigger alerts etc.
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{ render_checkbox_field(form.application.form.render_anchor_tag_content) }}
|
||||
<span class="pure-form-message-inline">Render anchor tag content, default disabled, when enabled renders links as <code>(link text)[https://somesite.com]</code>
|
||||
<br/>
|
||||
<i>Note:</i> Changing this could affect the content of your existing watches, possibly trigger alerts etc.
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{ render_field(form.application.form.global_subtractive_selectors, rows=5, placeholder="header
|
||||
footer
|
||||
nav
|
||||
.stockticker") }}
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>
|
||||
Remove HTML element(s) by CSS selector before text conversion.
|
||||
</li>
|
||||
<li>
|
||||
Add multiple elements or CSS selectors per line to ignore
|
||||
multiple parts of the HTML.
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{ render_field(form.application.form.global_ignore_text, rows=5,
|
||||
placeholder="Some text to ignore in a line /some.regex\d{2}/ for
|
||||
case-INsensitive regex ") }}
|
||||
<span class="pure-form-message-inline"
|
||||
>Note: This is applied globally in addition to the per-watch
|
||||
rules.</span
|
||||
><br />
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>
|
||||
Note: This is applied globally in addition to the per-watch
|
||||
rules.
|
||||
</li>
|
||||
<li>
|
||||
Each line processed separately, any line matching will be
|
||||
ignored (removed before creating the checksum)
|
||||
</li>
|
||||
<li>
|
||||
Regular Expression support, wrap the entire line in forward
|
||||
slash <code>/regex/</code>
|
||||
</li>
|
||||
<li>
|
||||
Changing this will affect the comparison checksum which may
|
||||
trigger an alert
|
||||
</li>
|
||||
<li>Use the preview/show current tab to see ignores</li>
|
||||
</ul>
|
||||
</span>
|
||||
</fieldset>
|
||||
</div>
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li> Remove HTML element(s) by CSS selector before text conversion. </li>
|
||||
<li> Add multiple elements or CSS selectors per line to ignore multiple parts of the HTML. </li>
|
||||
</ul>
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="pure-group">
|
||||
{{ render_field(form.application.form.global_ignore_text, rows=5, placeholder="Some text to ignore in a line
|
||||
/some.regex\d{2}/ for case-INsensitive regex
|
||||
") }}
|
||||
<span class="pure-form-message-inline">Note: This is applied globally in addition to the per-watch rules.</span><br/>
|
||||
<span class="pure-form-message-inline">
|
||||
<ul>
|
||||
<li>Note: This is applied globally in addition to the per-watch rules.</li>
|
||||
<li>Each line processed separately, any line matching will be ignored (removed before creating the checksum)</li>
|
||||
<li>Regular Expression support, wrap the entire line in forward slash <code>/regex/</code></li>
|
||||
<li>Changing this will affect the comparison checksum which may trigger an alert</li>
|
||||
<li>Use the preview/show current tab to see ignores</li>
|
||||
</ul>
|
||||
</span>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane-inner" id="api">
|
||||
<p>
|
||||
Drive your changedetection.io via API, More about
|
||||
<a
|
||||
href="https://github.com/dgtlmoon/changedetection.io/wiki/API-Reference"
|
||||
>API access here</a
|
||||
>
|
||||
</p>
|
||||
<div class="tab-pane-inner" id="api">
|
||||
|
||||
<div class="pure-control-group">
|
||||
{{
|
||||
render_checkbox_field(form.application.form.api_access_token_enabled)
|
||||
}}
|
||||
<div class="pure-form-message-inline">
|
||||
Restrict API access limit by using <code>x-api-key</code> header
|
||||
</div>
|
||||
<br />
|
||||
<div class="pure-form-message-inline">
|
||||
<br />API Key <span id="api-key">{{api_key}}</span>
|
||||
<span style="display: none" id="api-key-copy">copy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>Drive your changedetection.io via API, More about <a href="https://github.com/dgtlmoon/changedetection.io/wiki/API-Reference">API access here</a></p>
|
||||
|
||||
<div id="actions">
|
||||
<div class="pure-control-group">
|
||||
{{ render_button(form.save_button) }}
|
||||
<a href="{{url_for('index')}}" class="pure-button button-cancel"
|
||||
>Back</a
|
||||
>
|
||||
<a
|
||||
href="{{url_for('clear_all_history')}}"
|
||||
class="pure-button button-cancel"
|
||||
>Clear Snapshot History</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.application.form.api_access_token_enabled) }}
|
||||
<div class="pure-form-message-inline">Restrict API access limit by using <code>x-api-key</code> header</div><br/>
|
||||
<div class="pure-form-message-inline"><br/>API Key <span id="api-key">{{api_key}}</span>
|
||||
<span style="display:none;" id="api-key-copy" >copy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="actions">
|
||||
<div class="pure-control-group">
|
||||
{{ render_button(form.save_button) }}
|
||||
<a href="{{url_for('index')}}" class="pure-button button-small button-cancel">Back</a>
|
||||
<a href="{{url_for('clear_all_history')}}" class="pure-button button-small button-cancel">Clear Snapshot History</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 262.86"><path fill-rule="nonzero" d="M316.78 16.55h-205.9c-30.5 0-58.22 12.48-78.31 32.57C12.47 69.21 0 96.93 0 127.44c0 30.5 12.47 58.22 32.57 78.31 20.09 20.1 47.81 32.57 78.31 32.57h193.25c21.54 15.43 47.9 24.54 76.26 24.54h.18c36.14 0 69.02-14.79 92.83-38.6 23.8-23.81 38.6-56.67 38.6-92.83 0-36.15-14.78-69.03-38.63-92.8C449.53 14.8 416.67 0 380.57 0h-.18c-23.02 0-44.72 6.02-63.61 16.55zm70.62 97.17.43.09c.82-3.45 2.83-6.19 6.04-8.16 3.2-1.98 6.53-2.57 10.01-1.75l.1-.43c-3.47-.82-6.2-2.83-8.17-6.03-1.98-3.22-2.57-6.55-1.75-10.01l-.43-.1c-.82 3.47-2.83 6.2-6.03 8.18-3.21 1.98-6.55 2.56-10.02 1.74l-.1.43c3.47.82 6.2 2.84 8.18 6.04 1.99 3.19 2.56 6.52 1.74 10zm36.87 16.77.53.12c1.02-4.35 3.55-7.78 7.58-10.26 4.02-2.49 8.2-3.22 12.56-2.19l.13-.53c-4.35-1.03-7.78-3.55-10.26-7.59-2.49-4.03-3.22-8.22-2.2-12.56l-.53-.12c-1.02 4.35-3.55 7.77-7.58 10.26-4.02 2.49-8.21 3.22-12.56 2.19l-.13.53c4.36 1.03 7.78 3.55 10.26 7.58 2.49 4.02 3.22 8.22 2.2 12.57zm-38.79-61.01c-15.69 7.67-26.98 23.26-28.29 41.93-1.96 27.88 19.05 52.06 46.92 54.02 13.23.93 25.64-3.32 35.22-11.02 4.75-3.82 9.66-.45 7.59 4.36-11.33 26.42-38.45 44.04-68.74 41.91-38.29-2.69-67.14-35.91-64.45-74.19C316.3 89.8 347.05 61.67 383.44 62c6.71.06 8.13 4.5 2.04 7.48zm-5.09-53.95h.18c63.75 0 115.91 52.15 115.91 115.9 0 63.75-52.23 115.91-115.91 115.91h-.18c-63.68 0-115.91-52.16-115.91-115.91s52.16-115.9 115.91-115.9z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg class="octicon octicon-mark-github v-align-middle" height="32" viewbox="0 0 16 16" version="1.1" width="32" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 749 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 256.04"><path d="M128.02 0h.18c22.03 0 42.83 5.66 61 15.6h210.38c30.89 0 59 12.65 79.38 33.04C499.35 68.99 512 97.1 512 128.02c0 30.92-12.66 59.03-33.02 79.4l-.42.38c-20.34 20.15-48.29 32.64-78.98 32.64H189.24c-18.17 9.93-38.98 15.6-61.04 15.6h-.18c-35.2 0-67.22-14.41-90.42-37.6C14.41 195.25 0 163.24 0 128.02s14.4-67.24 37.59-90.43l.91-.83C61.65 14.05 93.29 0 128.02 0zm-5.95 54.42c0-1.95.8-3.73 2.08-5 2.74-2.77 7.27-2.76 10.02-.01l.14.16a7.042 7.042 0 0 1 1.94 4.85v12.95c0 1.95-.8 3.73-2.08 5.01-2.75 2.75-7.27 2.75-10.02 0a7.084 7.084 0 0 1-2.08-5.01V54.42zm6.05 31.17c11.72 0 22.32 4.75 30 12.43 7.67 7.68 12.43 18.29 12.43 30 0 11.72-4.75 22.32-12.43 30s-18.28 12.43-30 12.43c-11.72 0-22.32-4.75-30.01-12.43-7.67-7.68-12.43-18.28-12.43-30 0-11.72 4.76-22.32 12.43-30 7.69-7.67 18.3-12.43 30.01-12.43zm-56.33-5.34a7.114 7.114 0 0 1-2.07-5.01c0-3.9 3.18-7.09 7.09-7.09 1.81 0 3.62.69 5 2.07l9.16 9.16a7.065 7.065 0 0 1 2.08 5.01c0 1.8-.7 3.62-2.08 5.01a7.057 7.057 0 0 1-5.01 2.08c-1.8 0-3.61-.7-5-2.07l-9.17-9.16zm-17.28 53.81c-1.95 0-3.73-.8-5-2.08-2.77-2.74-2.76-7.27-.01-10.01l.15-.14a7.04 7.04 0 0 1 4.86-1.94h12.94a7.082 7.082 0 0 1 7.09 7.09c0 1.95-.8 3.73-2.07 5.01a7.099 7.099 0 0 1-5.02 2.07H54.51zm25.82 50.28a7.049 7.049 0 0 1-5 2.07c-3.91 0-7.09-3.16-7.09-7.08 0-1.81.68-3.62 2.07-5.01l9.31-9.29a7.02 7.02 0 0 1 4.86-1.94 7.09 7.09 0 0 1 7.09 7.09c0 1.79-.69 3.6-2.08 4.99l-9.16 9.17zm53.82 17.29c0 1.94-.8 3.73-2.08 5-2.74 2.76-7.27 2.75-10.02 0l-.13-.15a7.033 7.033 0 0 1-1.94-4.85v-12.95c0-1.96.8-3.73 2.07-5.01 2.76-2.75 7.27-2.75 10.03 0a7.1 7.1 0 0 1 2.07 5.01v12.95zm50.28-25.83a7.055 7.055 0 0 1 2.07 5.01c0 3.89-3.18 7.09-7.08 7.09-1.81 0-3.63-.69-5.01-2.07l-9.16-9.16a7.095 7.095 0 0 1-2.07-5.02c0-3.9 3.18-7.09 7.08-7.09 1.8 0 3.61.7 5 2.08l9.17 9.16zm17.29-53.82c1.93 0 3.73.81 5 2.08 2.76 2.75 2.75 7.27 0 10.02l-.15.14a7.098 7.098 0 0 1-4.85 1.94h-12.95c-1.96 0-3.74-.8-5.01-2.08-2.76-2.75-2.76-7.27 0-10.02a7.049 7.049 0 0 1 5.01-2.08h12.95zM175.89 71.7a7.074 7.074 0 0 1 5-2.07c3.9 0 7.1 3.19 7.1 7.09 0 1.81-.69 3.62-2.07 5l-9.32 9.31a7.12 7.12 0 0 1-4.86 1.93c-3.91 0-7.09-3.18-7.09-7.09 0-1.8.7-3.61 2.08-5l9.16-9.17zm34.17-41.87c2.96 2.47 5.81 5.07 8.53 7.8 23.22 23.15 37.63 55.17 37.63 90.39s-14.42 67.23-37.6 90.42a130.2 130.2 0 0 1-8.5 7.77h189.46c26.83 0 51.24-10.91 69.02-28.5l.32-.35c17.79-17.79 28.85-42.35 28.85-69.34 0-26.99-11.06-51.55-28.85-69.35-17.77-17.8-42.33-28.84-69.34-28.84H210.06zm-82.04-14.71h.18c62.09 0 112.89 50.81 112.89 112.9 0 62.1-50.86 112.9-112.89 112.9h-.18c-62.03 0-112.9-50.8-112.9-112.9 0-62.09 50.81-112.9 112.9-112.9z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -80,15 +80,15 @@
|
||||
<td class="inline checkbox-uuid" ><input name="uuids" type="checkbox" value="{{ watch.uuid}} "/> <span>{{ loop.index }}</span></td>
|
||||
<td class="inline watch-controls">
|
||||
{% if not watch.paused %}
|
||||
<a class="state-off" href="{{url_for('index', op='pause', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="Pause checks" title="Pause checks" class="icon icon-pause"/></a>
|
||||
<a class="state-off" href="{{url_for('index', op='pause', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="Pause checks" title="Pause checks"/></a>
|
||||
{% else %}
|
||||
<a class="state-on" href="{{url_for('index', op='pause', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='play.svg')}}" alt="UnPause checks" title="UnPause checks" class="icon icon-unpause"/></a>
|
||||
<a class="state-on" href="{{url_for('index', op='pause', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='play.svg')}}" alt="UnPause checks" title="UnPause checks"/></a>
|
||||
{% endif %}
|
||||
<a class="link-mute state-{{'on' if watch.notification_muted else 'off'}}" href="{{url_for('index', op='mute', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='bell-off.svg')}}" alt="Mute notifications" title="Mute notifications" class="icon icon-mute"/></a>
|
||||
<a class="state-{{'on' if watch.notification_muted}}" href="{{url_for('index', op='mute', uuid=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='bell-off.svg')}}" alt="Mute notifications" title="Mute notifications"/></a>
|
||||
</td>
|
||||
<td class="title-col inline">{{watch.title if watch.title is not none and watch.title|length > 0 else watch.url}}
|
||||
<a class="external" target="_blank" rel="noopener" href="{{ watch.link.replace('source:','') }}"></a>
|
||||
<a class="link-spread" href="{{url_for('form_share_put_watch', uuid=watch.uuid)}}"><img style="height: 1em;display:inline-block;" src="{{url_for('static_content', group='images', filename='spread.svg')}}" class="icon icon-spread" /></a>
|
||||
<a href="{{url_for('form_share_put_watch', uuid=watch.uuid)}}"><img style="height: 1em;display:inline-block;" src="{{url_for('static_content', group='images', filename='spread.svg')}}" /></a>
|
||||
|
||||
{%if watch.fetch_backend == "html_webdriver" %}<img style="height: 1em; display:inline-block;" src="{{url_for('static_content', group='images', filename='Google-Chrome-icon.png')}}" />{% endif %}
|
||||
|
||||
@@ -111,13 +111,13 @@
|
||||
</td>
|
||||
<td>
|
||||
<a {% if watch.uuid in queued_uuids %}disabled="true"{% endif %} href="{{ url_for('form_watch_checknow', uuid=watch.uuid, tag=request.args.get('tag')) }}"
|
||||
class="recheck pure-button pure-button-primary">{% if watch.uuid in queued_uuids %}Queued{% else %}Recheck{% endif %}</a>
|
||||
<a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button pure-button-primary">Edit</a>
|
||||
class="recheck pure-button button-small pure-button-primary">{% if watch.uuid in queued_uuids %}Queued{% else %}Recheck{% endif %}</a>
|
||||
<a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button button-small pure-button-primary">Edit</a>
|
||||
{% if watch.history_n >= 2 %}
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button pure-button-primary diff-link">Diff</a>
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary diff-link">Diff</a>
|
||||
{% else %}
|
||||
{% if watch.history_n == 1 or (watch.history_n ==0 and watch.error_text_ctime )%}
|
||||
<a href="{{ url_for('preview_page', uuid=watch.uuid)}}" target="{{watch.uuid}}" class="pure-button pure-button-primary">Preview</a>
|
||||
<a href="{{ url_for('preview_page', uuid=watch.uuid)}}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary">Preview</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -57,12 +57,6 @@ services:
|
||||
# Used for fetching pages via WebDriver+Chrome where you need Javascript support.
|
||||
# Now working on arm64 (needs testing on rPi - tested on Oracle ARM instance)
|
||||
# replace image with seleniarm/standalone-chromium:4.0.0-20211213
|
||||
|
||||
# If WEBDRIVER or PLAYWRIGHT are enabled, changedetection container depends on that
|
||||
# and must wait before starting (substitute "browser-chrome" with "playwright-chrome" if last one is used)
|
||||
# depends_on:
|
||||
# browser-chrome:
|
||||
# condition: service_started
|
||||
|
||||
# browser-chrome:
|
||||
# hostname: browser-chrome
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
flask~=2.0
|
||||
flask_wtf
|
||||
flask-compress
|
||||
eventlet>=0.31.0
|
||||
validators
|
||||
timeago~=1.0
|
||||
@@ -58,3 +57,6 @@ jq~=1.3 ;python_version >= "3.8" and sys_platform == "linux"
|
||||
# Any current modern version, required so far for screenshot PNG->JPEG conversion but will be used more in the future
|
||||
pillow
|
||||
# playwright is installed at Dockerfile build time because it's not available on all platforms
|
||||
|
||||
# For shutting down playwright BrowserSteps nicely
|
||||
psutil
|
||||