ui-improvements updates
@@ -283,6 +283,8 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
@app.route("/", methods=['GET'])
|
||||
@login_required
|
||||
def index():
|
||||
import uuid
|
||||
|
||||
limit_tag = request.args.get('tag')
|
||||
pause_uuid = request.args.get('pause')
|
||||
|
||||
@@ -292,13 +294,18 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
if pause_uuid:
|
||||
try:
|
||||
validate = uuid.UUID(str(pause_uuid))
|
||||
datastore.data['watching'][pause_uuid]['paused'] ^= True
|
||||
datastore.needs_write = True
|
||||
except ValueError:
|
||||
|
||||
action = True if pause_uuid == 'pause-all' else False
|
||||
|
||||
return redirect(url_for('index', tag = limit_tag))
|
||||
except KeyError:
|
||||
pass
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if datastore.data['watching'][watch_uuid]['tag'] == limit_tag or limit_tag is None :
|
||||
datastore.data['watching'][watch_uuid]['paused'] = action
|
||||
|
||||
datastore.needs_write = True
|
||||
return redirect(url_for('index', tag = limit_tag))
|
||||
# Sort by last_changed and add the uuid which is usually the key..
|
||||
sorted_watches = []
|
||||
for uuid, watch in datastore.data['watching'].items():
|
||||
@@ -630,13 +637,139 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
@login_required
|
||||
def mark_all_viewed():
|
||||
|
||||
limit_tag = request.args.get('tag')
|
||||
|
||||
# Save the current newest history as the most recently viewed
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
datastore.set_last_viewed(watch_uuid, watch['newest_history_key'])
|
||||
try:
|
||||
for watch_uuid, watch in datastore.data['watching'].items():
|
||||
if datastore.data['watching'][watch_uuid]['tag'] == limit_tag or limit_tag is None :
|
||||
datastore.set_last_viewed(watch_uuid, watch['newest_history_key'])
|
||||
|
||||
datastore.needs_write = True
|
||||
|
||||
flash("Cleared all statuses.")
|
||||
return redirect(url_for('index'))
|
||||
return redirect(url_for('index', tag = limit_tag))
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# process selected
|
||||
@app.route("/api/process-selected", methods=['GET', "POST"])
|
||||
@login_required
|
||||
def process_selected():
|
||||
|
||||
if request.method == 'POST' :
|
||||
func = request.form.get('func')
|
||||
limit_tag = request.form.get('tag')
|
||||
uuids = request.form.get('uuids')
|
||||
|
||||
if request.method == 'GET' :
|
||||
func = request.args.get('func')
|
||||
limit_tag = request.args.get('tag')
|
||||
uuids = request.args.get('uuids')
|
||||
|
||||
if uuids == '' :
|
||||
flash("No watches selected.")
|
||||
return render_template('index')
|
||||
|
||||
else :
|
||||
|
||||
if func == 'recheck_selected' :
|
||||
|
||||
i = 0
|
||||
|
||||
running_uuids = []
|
||||
for t in running_update_threads:
|
||||
running_uuids.append(t.current_uuid)
|
||||
|
||||
try :
|
||||
for uuid in uuids.split(',') :
|
||||
if uuid not in running_uuids and not datastore.data['watching'][uuid]['paused']:
|
||||
update_q.put(uuid)
|
||||
i += 1
|
||||
|
||||
except KeyError :
|
||||
pass
|
||||
|
||||
flash("Rechecking {0} watch{1}.".format(i, "" if i == 1 else "es"))
|
||||
|
||||
# Clear selected statuses, so we do not see the 'unviewed' class
|
||||
elif func == 'mark_selected_viewed' :
|
||||
|
||||
try :
|
||||
for uuid in uuids.split(',') :
|
||||
datastore.data['watching'][uuid]['last_viewed'] = datastore.data['watching'][uuid]['newest_history_key']
|
||||
|
||||
except KeyError :
|
||||
pass
|
||||
|
||||
datastore.needs_write = True
|
||||
|
||||
# Reset selected statuses, so we see the 'unviewed' class
|
||||
# both funcs will contain the uuid list from the processChecked javascript function
|
||||
elif func == 'mark_selected_notviewed' or func == 'mark_all_notviewed' :
|
||||
|
||||
# count within limit_tag and count successes and capture unchanged
|
||||
tagged = 0
|
||||
marked = 0
|
||||
unchanged = []
|
||||
|
||||
try :
|
||||
for uuid in uuids.split(',') :
|
||||
# increment count with limit_tag
|
||||
tagged += 1
|
||||
dates = list(datastore.data['watching'][uuid]['history'].keys())
|
||||
# Convert to int, sort and back to str again
|
||||
dates = [int(i) for i in dates]
|
||||
dates.sort(reverse=True)
|
||||
dates = [str(i) for i in dates]
|
||||
|
||||
# must be more than 1 history to mark as not viewed
|
||||
if len(dates) > 1 :
|
||||
# Save the next earliest history as the most recently viewed
|
||||
datastore.set_last_viewed(uuid, dates[1])
|
||||
# increment successes
|
||||
marked += 1
|
||||
|
||||
else :
|
||||
if datastore.data['watching'][uuid]['title'] :
|
||||
unchanged.append(datastore.data['watching'][uuid]['title'])
|
||||
else :
|
||||
unchanged.append(datastore.data['watching'][uuid]['url'])
|
||||
|
||||
except KeyError :
|
||||
pass
|
||||
|
||||
datastore.needs_write = True
|
||||
|
||||
if marked < tagged :
|
||||
flash("The following {} not have enough history to be remarked:".format("watch does" if len(unchanged) == 1 else "watches do"), "notice")
|
||||
for i in range(len(unchanged)):
|
||||
flash(unchanged[i], "notice")
|
||||
|
||||
elif func == 'delete_selected' :
|
||||
|
||||
# reachable only after confirmation in javascript processChecked(func, tag) function
|
||||
try :
|
||||
i = 0
|
||||
for uuid in uuids.split(',') :
|
||||
datastore.delete(uuid)
|
||||
i += 1
|
||||
|
||||
except KeyError :
|
||||
pass
|
||||
|
||||
datastore.needs_write = True
|
||||
|
||||
flash("{0} {1} deleted.".format(i, "watch was" if (i) == 1 else "watches were"))
|
||||
|
||||
else :
|
||||
|
||||
flash("Invalid parameter received.")
|
||||
|
||||
if limit_tag == None or limit_tag == 'None' :
|
||||
return redirect(url_for('index'))
|
||||
else :
|
||||
return redirect(url_for('index', tag = limit_tag))
|
||||
|
||||
@app.route("/diff/<string:uuid>", methods=['GET'])
|
||||
@login_required
|
||||
def diff_history_page(uuid):
|
||||
@@ -797,6 +930,10 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
if form.validate():
|
||||
|
||||
# get action parameter (add paused button value is 'add', watch button value is 'watch'
|
||||
#action = request.form.get('action')
|
||||
add_paused = request.form.get('add-paused')
|
||||
|
||||
url = request.form.get('url').strip()
|
||||
if datastore.url_exists(url):
|
||||
flash('The URL {} already exists'.format(url), "error")
|
||||
@@ -804,10 +941,17 @@ def changedetection_app(config=None, datastore_o=None):
|
||||
|
||||
# @todo add_watch should throw a custom Exception for validation etc
|
||||
new_uuid = datastore.add_watch(url=url, tag=request.form.get('tag').strip())
|
||||
# Straight into the queue.
|
||||
update_q.put(new_uuid)
|
||||
|
||||
if add_paused :
|
||||
datastore.data['watching'][new_uuid]['paused'] = True
|
||||
datastore.needs_write = True
|
||||
flash("Watch added in a paused state.")
|
||||
|
||||
else : # watch now
|
||||
# Straight into the queue.
|
||||
update_q.put(new_uuid)
|
||||
|
||||
flash("Watch added.")
|
||||
flash("Watch added.")
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash("Error")
|
||||
|
||||
27
changedetectionio/static/images/notviewed.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg2" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:ns1="http://sozi.baierouge.fr" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="9.5px" height="15px"
|
||||
viewBox="0 0 9.5 15" style="enable-background:new 0 0 9.5 15;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078E7;}
|
||||
.st1{fill:#0078E7;stroke:#0078E7;stroke-width:0.1;stroke-miterlimit:4.6604;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
|
||||
</style>
|
||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="88.93244" inkscape:cy="-6.4524562" inkscape:document-units="px" inkscape:guide-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="822" inkscape:window-maximized="0" inkscape:window-width="1274" inkscape:window-x="0" inkscape:window-y="25" inkscape:zoom="1" pagecolor="#ffffff" showgrid="false" showguides="true">
|
||||
</sodipodi:namedview>
|
||||
<path id="path3740" sodipodi:nodetypes="cccccc" class="st0" d="M2.2,0C1.8,0,0.7,0.1,0,1.5v1.3h2.2V0C2.3,0.1,2.2,0,2.2,0L2.2,0z"
|
||||
/>
|
||||
<path id="rect3728" sodipodi:nodetypes="ccccc" class="st0" d="M0.3,1.7l-0.2,1h2.1V0.2C1.1,0.4,0.6,1.2,0.3,1.7L0.3,1.7z"/>
|
||||
<path id="path3655" class="st0" d="M9.5,2.6L9.5,2.6L2.3,0L2,0.2l7.2,2.6v0.1"/>
|
||||
<path id="path3645" sodipodi:nodetypes="cccc" class="st0" d="M9.2,2.8v10.5L9.4,13V2.5"/>
|
||||
<path id="rect3517" sodipodi:nodetypes="ccccc" class="st0" d="M2,0.2l7.2,2.6v10.6L2,10.8V0.2z"/>
|
||||
<path id="path3657" sodipodi:nodetypes="cc" class="st1" d="M2.1,0.2l7.1,2.6"/>
|
||||
<path id="path3684" sodipodi:nodetypes="cccccc" class="st2" d="M0.5,9.6l0.1-7.2L0.4,2.3c0,0,0.8-1.2,1.6-1.3l6.8,2.4v0.1"/>
|
||||
<path id="path3679" class="st2" d="M8.9,3.4L8.7,13l-1.4,1.3l-6.8-2.4V9.5"/>
|
||||
<path id="path3669" class="st0" d="M7.5,4.2L7.5,4.2L0.3,1.6L0,1.9l7.2,2.6v0.1"/>
|
||||
<path id="path3671" sodipodi:nodetypes="cccc" class="st0" d="M7.2,4.5V15l0.2-0.3V4.2"/>
|
||||
<path id="path3673" sodipodi:nodetypes="ccccc" class="st0" d="M0,1.9l7.2,2.6V15L0,12.4V1.9z"/>
|
||||
<path id="path3675" sodipodi:nodetypes="cc" class="st1" d="M0.1,1.9l7.1,2.6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1,84 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 15 14.998326"
|
||||
xml:space="preserve"
|
||||
width="15"
|
||||
height="14.998326"><metadata
|
||||
id="metadata39"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs37" />
|
||||
<path
|
||||
id="path2"
|
||||
style="fill:#1b98f8;fill-opacity:1;stroke-width:0.0292893"
|
||||
d="M 7.4975161,6.5052867e-4 C 4.549072,-0.04028702 1.7055675,1.8548221 0.58868606,4.5801341 -0.57739762,7.2574642 0.02596981,10.583326 2.069916,12.671949 4.0364753,14.788409 7.2763651,15.56067 9.989207,14.57284 12.801145,13.617602 14.87442,10.855325 14.985833,7.8845744 15.172496,4.9966544 13.49856,2.1100704 10.911002,0.8209349 9.8598067,0.28073592 8.6791261,-0.00114855 7.4975161,6.5052867e-4 Z M 6.5602569,10.251923 c -0.00509,0.507593 -0.5693885,0.488472 -0.9352002,0.468629 -0.3399386,0.0018 -0.8402048,0.07132 -0.9297965,-0.374189 -0.015842,-1.8973128 -0.015872,-3.7979649 0,-5.6952784 0.1334405,-0.5224315 0.7416869,-0.3424086 1.1377562,-0.374189 0.3969969,-0.084515 0.8245634,0.1963256 0.7272405,0.6382917 0,1.7789118 0,3.5578239 0,5.3367357 z m 3.7490371,0 c -0.0051,0.507593 -0.5693888,0.488472 -0.9352005,0.468629 -0.3399386,0.0018 -0.8402048,0.07132 -0.9297965,-0.374189 -0.015842,-1.8973128 -0.015872,-3.7979649 0,-5.6952784 0.1334405,-0.5224315 0.7416869,-0.3424086 1.1377562,-0.374189 0.3969969,-0.084515 0.8245638,0.1963256 0.7272408,0.6382917 0,1.7789118 0,3.5578239 0,5.3367357 z" />
|
||||
<g
|
||||
id="g4"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g6"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g8"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g10"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g12"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g14"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g16"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g18"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g20"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g22"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g24"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g26"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g28"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g30"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g
|
||||
id="g32"
|
||||
transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="Capa_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="15px" height="15px"
|
||||
viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078E7;}
|
||||
</style>
|
||||
<path id="path2" class="st0" d="M7.5,0C4.5,0,1.7,1.9,0.6,4.6c-1.2,2.7-0.6,6,1.5,8.1c2,2.1,5.2,2.9,7.9,1.9c2.8-1,4.9-3.7,5-6.7
|
||||
c0.2-2.9-1.5-5.8-4.1-7.1C9.9,0.3,8.7,0,7.5,0z M6.6,10.3c0,0.5-0.6,0.5-0.9,0.5c-0.3,0-0.8,0.1-0.9-0.4c0-1.9,0-3.8,0-5.7
|
||||
c0.1-0.5,0.7-0.3,1.1-0.4c0.4-0.1,0.8,0.2,0.7,0.6C6.6,6.7,6.6,8.5,6.6,10.3z M10.3,10.3c0,0.5-0.6,0.5-0.9,0.5
|
||||
c-0.3,0-0.8,0.1-0.9-0.4c0-1.9,0-3.8,0-5.7c0.1-0.5,0.7-0.3,1.1-0.4c0.4-0.1,0.8,0.2,0.7,0.6C10.3,6.7,10.3,8.5,10.3,10.3z"/>
|
||||
<g id="g4" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g6" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g8" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g10" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g12" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g14" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g16" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g18" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g20" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g22" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g24" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g26" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g28" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g30" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
<g id="g32" transform="translate(-0.01903604,0.02221043)">
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.0 KiB |
12
changedetectionio/static/images/play.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="15px" height="15px" viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078E7;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path id="path2" class="st0" d="M7.5,0C3.4,0.1,0,3.4,0,7.5S3.4,15,7.5,15S15,11.6,15,7.5c0-2.8-1.6-5.4-4.1-6.7
|
||||
C9.9,0.3,8.7,0.1,7.5,0z"/>
|
||||
<polygon class="st1" points="11.4,8 5.8,4.8 5.8,11.3 "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 660 B |
BIN
changedetectionio/static/images/search.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
9
changedetectionio/static/images/sortable.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="10.8px" height="15px" viewBox="0 0 10.8 15" style="enable-background:new 0 0 10.8 15;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078E7;stroke:#0078E7;stroke-width:1.25;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path class="st0" d="M5.5,1l4.1,5.1H1.3L5.5,1z M5.5,14L1.3,8.9h8.2L5.5,14z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 580 B |
30
changedetectionio/static/images/viewed.svg
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="svg2" inkscape:export-filename="/home/piotr/priv/zm_szablon/book.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.46" sodipodi:docname="book.svg" sodipodi:version="0.32" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:ns1="http://sozi.baierouge.fr" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16.3px" height="11.6px"
|
||||
viewBox="0 0 16.3 11.6" style="enable-background:new 0 0 16.3 11.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#0078E7;stroke:#007EC0;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#1187C3;}
|
||||
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;stroke:#0078E7;stroke-width:0.5;}
|
||||
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#1187C3;stroke:#007EC0;stroke-width:0.5;}
|
||||
</style>
|
||||
<sodipodi:namedview bordercolor="#666666" borderopacity="1.0" gridtolerance="10000" guidetolerance="10" id="base" inkscape:current-layer="layer1" inkscape:cx="394.36154" inkscape:cy="818.28215" inkscape:document-units="px" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="725" inkscape:window-width="1279" inkscape:window-x="1" inkscape:window-y="49" inkscape:zoom="0.98994949" objecttolerance="10" pagecolor="#ffffff" showgrid="false">
|
||||
</sodipodi:namedview>
|
||||
<path id="path2416" sodipodi:nodetypes="cccsccccccccccccccc" class="st0" d="M14.2,6.5l1.4,3.1l-6.2,0.8c0,0,0,0.2-0.4,0.1
|
||||
c-0.3,0-0.6-0.3-0.6-0.6l-6.6,1.2L0.6,4.2h0.3v0.1H0.8L2,10.9l6.4-1.1V10h0.4c0,0,0,0.4,0.5,0.4l0.1-0.1l6-0.8L14.2,6.5L14.2,6.5z"
|
||||
/>
|
||||
<path id="path2400" sodipodi:nodetypes="cccccc" class="st1" d="M1,4.3H0.9L2,10.9l6.4-1.1c0,0-1.7-0.9-6.1,1L1,4.3L1,4.3z"/>
|
||||
<path id="path2402" sodipodi:nodetypes="ccccccccccccccc" class="st2" d="M1,4.1V3.6h0.1V3.2l0.2-0.3h0.1V2.6V2.5l1.4,6.4L2.7,9.2
|
||||
v0.2L2.6,9.5L2.4,9.6l-0.1,1.1L1,4.1L1,4.1z"/>
|
||||
<path id="path2388" sodipodi:nodetypes="cccccccc" class="st2" d="M2.3,10.8l0.1-1.2l0.3-0.3V9.2l0.2-0.3c0,0,5.5-0.2,5.9,0.8
|
||||
L8.4,9.8C8.4,9.8,6.7,8.9,2.3,10.8L2.3,10.8z"/>
|
||||
<path id="path2394" class="st2" d="M2.2,2.5H1.5l1.4,6.4c0,0,5.3-0.2,5.9,0.8c0,0-0.1-1.1-5.4-1.6L2.2,2.5L2.2,2.5z"/>
|
||||
<path id="path2398" class="st2" d="M2,1.3L3.4,8c0,0,5,0.5,5.4,1.7l-2-6.3C6.8,3.3,6.8,2.3,2,1.3L2,1.3z"/>
|
||||
<path id="path2403" sodipodi:nodetypes="ccccc" class="st2" d="M6.9,3.3l5-2.9l2.5,5.9L8.8,9.7L6.9,3.3L6.9,3.3z"/>
|
||||
<path id="path2411" sodipodi:nodetypes="ccccccc" class="st3" d="M8.5,10V9.8l0.3-0.1c0,0,0.2,0.6,0.6,0.6v0.1c0,0-0.5,0-0.5-0.5
|
||||
L8.5,10L8.5,10z"/>
|
||||
<path id="path2415" class="st2" d="M8.8,9.7c0,0,0.2,0.6,0.6,0.5l6-0.8l-0.3-0.6h-0.3v0C14.9,8.8,9.8,9,8.8,9.7L8.8,9.7z"/>
|
||||
<path id="path2404" sodipodi:nodetypes="ccccc" class="st2" d="M15.1,8.9l-1-2.3L8.8,9.7v0.1c0,0,0.5-0.6,6-0.9V9L15.1,8.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
338
changedetectionio/static/js/tbltools.js
Normal file
@@ -0,0 +1,338 @@
|
||||
// table tools
|
||||
|
||||
// must be a var for keyChar and keyCode use
|
||||
var CONSTANT_ESCAPE_KEY = 27;
|
||||
|
||||
// global sorting vars (new window is always last_changed, descending)
|
||||
var loading;
|
||||
var sort_column;
|
||||
var sort_order;
|
||||
|
||||
// restore scroll position on submit/reload
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
var scrollpos = sessionStorage.getItem('scrollpos');
|
||||
if (scrollpos) window.scrollTo(0, scrollpos);
|
||||
});
|
||||
// mobile scroll position retention
|
||||
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
document.addEventListener("visibilitychange", function() {
|
||||
storeScrollAndSearch();
|
||||
});
|
||||
}
|
||||
else {
|
||||
// non-mobile scroll position retention
|
||||
window.onbeforeunload = function(e) {
|
||||
storeScrollAndSearch();
|
||||
};
|
||||
}
|
||||
function storeScrollAndSearch() {
|
||||
sessionStorage.setItem('scrollpos', window.pageYOffset);
|
||||
sessionStorage.setItem('searchtxt', document.getElementById("txtInput").value);
|
||||
}
|
||||
|
||||
// page load functions
|
||||
function load_functions() {
|
||||
// loading
|
||||
loading = true;
|
||||
// retain checked items
|
||||
checkChange();
|
||||
// retrieve saved sorting
|
||||
getSort();
|
||||
// sort
|
||||
sortTable(sort_column);
|
||||
// search
|
||||
if (isSessionStorageSupported()) {
|
||||
// retrieve search
|
||||
if ( sessionStorage.getItem("searchtxt") != null ) {
|
||||
document.getElementById("txtInput").value = sessionStorage.getItem("searchtxt");
|
||||
tblSearch(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sorting
|
||||
function sortTable(n) {
|
||||
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
|
||||
table = document.getElementById("watch-table");
|
||||
switching = true;
|
||||
//Set the sorting direction, either default 9, 1 or saved
|
||||
if (loading) {
|
||||
getSort();
|
||||
n = sort_column;
|
||||
dir = (sort_order == 0) ? "asc" : "desc";
|
||||
loading = false;
|
||||
}
|
||||
else {
|
||||
dir = "asc";
|
||||
}
|
||||
/*Make a loop that will continue until
|
||||
no switching has been done:*/
|
||||
while (switching) {
|
||||
//start by saying: no switching is done:
|
||||
switching = false;
|
||||
rows = table.rows;
|
||||
/*Loop through all table rows (except the
|
||||
first, which contains table headers):*/
|
||||
for (i = 1; i < (rows.length - 1); i++) {
|
||||
//start by saying there should be no switching:
|
||||
shouldSwitch = false;
|
||||
/*Get the two elements you want to compare,
|
||||
one from current row and one from the next:*/
|
||||
x = rows[i].getElementsByTagName("TD")[n];
|
||||
y = rows[i + 1].getElementsByTagName("TD")[n];
|
||||
x = x.innerHTML.toLowerCase();
|
||||
y = y.innerHTML.toLowerCase();
|
||||
/* handle # columns */
|
||||
if (!isNaN(x)) {
|
||||
x = parseFloat(x);
|
||||
y = parseFloat(y);
|
||||
}
|
||||
/*check if the two rows should switch place,
|
||||
based on the direction, asc or desc:*/
|
||||
if (dir == "asc") {
|
||||
if (x > y) {
|
||||
//if so, mark as a switch and break the loop:
|
||||
shouldSwitch= true;
|
||||
break;
|
||||
}
|
||||
} else if (dir == "desc") {
|
||||
if (x < y) {
|
||||
//if so, mark as a switch and break the loop:
|
||||
shouldSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shouldSwitch) {
|
||||
/*If a switch has been marked, make the switch
|
||||
and mark that a switch has been done:*/
|
||||
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
|
||||
switching = true;
|
||||
//Each time a switch is done, increase this count by 1:
|
||||
switchcount ++;
|
||||
} else {
|
||||
/*If no switching has been done AND the direction is "asc",
|
||||
set the direction to "desc" and run the while loop again.*/
|
||||
if (switchcount == 0 && dir == "asc") {
|
||||
dir = "desc";
|
||||
switching = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// hide all asc/desc sort arrows
|
||||
sortimgs = document.querySelectorAll('[id^="sort-"]');
|
||||
for (var i = 0; i < sortimgs.length; i++) {
|
||||
sortimgs[i].style.display = "none";
|
||||
}
|
||||
// show current asc/desc sort arrow and set sort_order var
|
||||
if (dir == "asc") {
|
||||
document.getElementById("sort-" + n + "a").style.display = "";
|
||||
}
|
||||
else {
|
||||
document.getElementById("sort-" + n + "d").style.display = "";
|
||||
}
|
||||
// show all sortable indicators
|
||||
sortableimgs = document.querySelectorAll('[id^="sortable-"]');
|
||||
for (var i = 0; i < sortableimgs.length; i++) {
|
||||
sortableimgs[i].style.display = "";
|
||||
}
|
||||
// hide sortable indicator from current column
|
||||
document.getElementById("sortable-" + n).style.display = "none";
|
||||
// save sorting
|
||||
sessionStorage.setItem("sort_column", n);
|
||||
sessionStorage.setItem("sort_order", (dir == "asc") ? 0 : 1);
|
||||
// restripe
|
||||
restripe();
|
||||
}
|
||||
|
||||
// check/uncheck all checkboxes
|
||||
function checkAll(e) {
|
||||
var checkboxes = document.getElementsByName('check');
|
||||
var checkboxFunctions = document.querySelectorAll('[id=checkbox-functions]');
|
||||
if (e.checked) {
|
||||
for (var i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = true;
|
||||
}
|
||||
for (var i = 0; i < checkboxFunctions.length; i++) {
|
||||
checkboxFunctions[i].style.display = "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = false;
|
||||
}
|
||||
for (var i = 0; i < checkboxFunctions.length; i++) {
|
||||
checkboxFunctions[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check/uncheck checkall checkbox if all other checkboxes are checked/unchecked
|
||||
function checkChange(){
|
||||
var totalCheckbox = document.querySelectorAll('input[name="check"]').length;
|
||||
var totalChecked = document.querySelectorAll('input[name="check"]:checked').length;
|
||||
var checkboxFunctions = document.querySelectorAll('[id=checkbox-functions]');
|
||||
if(totalCheckbox == totalChecked) {
|
||||
document.getElementsByName("showhide")[0].checked=true;
|
||||
}
|
||||
else {
|
||||
document.getElementsByName("showhide")[0].checked=false;
|
||||
}
|
||||
if(totalChecked == 0) {
|
||||
for (var i = 0; i < checkboxFunctions.length; i++) {
|
||||
checkboxFunctions[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < checkboxFunctions.length; i++) {
|
||||
checkboxFunctions[i].style.display = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search watches in Title column
|
||||
function tblSearch(evt) {
|
||||
var code = evt.charCode || evt.keyCode;
|
||||
if (code == CONSTANT_ESCAPE_KEY) {
|
||||
document.getElementById("txtInput").value = '';
|
||||
}
|
||||
var input, filter, table, tr, td, i, txtValue;
|
||||
input = document.getElementById("txtInput");
|
||||
filter = input.value.toUpperCase();
|
||||
table = document.getElementById("watch-table");
|
||||
tr = table.getElementsByTagName("tr");
|
||||
for (i = 0; i < tr.length; i++) {
|
||||
td = tr[i].getElementsByTagName("td")[5]; // col 5 is the hidden title/url column
|
||||
if (td) {
|
||||
txtValue = td.textContent || td.innerText;
|
||||
if (txtValue.toUpperCase().indexOf(filter) > -1) {
|
||||
tr[i].style.display = "";
|
||||
}
|
||||
else {
|
||||
tr[i].style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
restripe();
|
||||
}
|
||||
|
||||
// restripe after searching
|
||||
function restripe () {
|
||||
var visrows = [];
|
||||
var table = document.getElementById("watch-table");
|
||||
var rows = table.getElementsByTagName("tr");
|
||||
|
||||
for (i = 0; i < rows.length; i++) {
|
||||
if (rows[i].style.display !== "none") {
|
||||
visrows.push(rows[i]);
|
||||
}
|
||||
}
|
||||
for (var i=0 ; i<visrows.length; i++) {
|
||||
var row = visrows[i];
|
||||
var cells = row.getElementsByTagName("td");
|
||||
for(var j=0; j<cells.length; j++) {
|
||||
if( i%2==0 ) {
|
||||
cells[j].style.background = "#ffffff";
|
||||
} else {
|
||||
cells[j].style.background = "#f2f2f2";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get checked or all uuids
|
||||
function getChecked(items) {
|
||||
if ( items === undefined ) {
|
||||
var checkedArr = document.querySelectorAll('input[name="check"]:checked');
|
||||
}
|
||||
else {
|
||||
var checkedArr = document.querySelectorAll('input[name="check"]');
|
||||
}
|
||||
if ( checkedArr.length > 0 ) {
|
||||
let output = [];
|
||||
for (var i = 0; i < checkedArr.length; i++ ) {
|
||||
output.push( checkedArr[i].parentNode.parentNode.getAttribute("id") );
|
||||
}
|
||||
var uuids = "";
|
||||
for (var i = 0; i < checkedArr.length; i++ ) {
|
||||
if (i < checkedArr.length - 1 ) {
|
||||
uuids += output[i] + ",";
|
||||
} else {
|
||||
uuids += output[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uuids = '';
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
// process selected watches
|
||||
function processChecked(func, tag) {
|
||||
if ( func == 'mark_all_notviewed' ) {
|
||||
uuids = getChecked('all');
|
||||
}
|
||||
else {
|
||||
uuids = getChecked();
|
||||
}
|
||||
// confirm if deleting
|
||||
if ( func == 'delete_selected' && uuids.length > 0 ) {
|
||||
result = confirm('Deletions cannot be undone.\n\nAre you sure you want to continue?');
|
||||
if ( result == false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// href locations
|
||||
var currenturl = window.location;
|
||||
var posturl = location.protocol + '//' + location.host + '/api/process-selected';
|
||||
// posting vars
|
||||
const XHR = new XMLHttpRequest(),
|
||||
FD = new FormData();
|
||||
// fill form data
|
||||
FD.append('func', func);
|
||||
FD.append('tag', tag);
|
||||
FD.append('uuids', uuids);
|
||||
// success
|
||||
XHR.addEventListener( 'load', function( event ) {
|
||||
window.location = currenturl;
|
||||
});
|
||||
// error
|
||||
XHR.addEventListener(' error', function( event ) {
|
||||
alert( 'Error posting request.' );
|
||||
});
|
||||
// set up request
|
||||
XHR.open( 'POST', posturl );
|
||||
// send
|
||||
XHR.send( FD );
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
document.getElementById("txtInput").value = '';
|
||||
tblSearch(CONSTANT_ESCAPE_KEY);
|
||||
}
|
||||
|
||||
function isSessionStorageSupported() {
|
||||
var storage = window.sessionStorage;
|
||||
try {
|
||||
storage.setItem('test', 'test');
|
||||
storage.removeItem('test');
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getSort() {
|
||||
if (isSessionStorageSupported()) {
|
||||
// retrieve sort settings if set
|
||||
if ( sessionStorage.getItem("sort_column") != null ) {
|
||||
sort_column = sessionStorage.getItem("sort_column");
|
||||
sort_order = sessionStorage.getItem("sort_order");
|
||||
}
|
||||
else {
|
||||
sort_column = 9; // last changed
|
||||
sort_order = 1; // desc
|
||||
//alert("Your web browser does not support retaining sorting and page position.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
<body onload="load_functions()">
|
||||
|
||||
<div class="header">
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
{% else %}
|
||||
<a class="pure-menu-heading" href="{{url_for('index')}}"><strong>Change</strong>Detection.io</a>
|
||||
{% endif %}
|
||||
{% if current_diff_url %}
|
||||
{% 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>
|
||||
{% else %}
|
||||
{% if new_version_available and not (has_password and not current_user.is_authenticated) %}
|
||||
@@ -34,6 +34,9 @@
|
||||
<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">
|
||||
<span class="search-box"><input type="text" id="txtInput" onkeyup="tblSearch(event)" onmouseup="clearSearch(event)" placeholder="Title..." /></span>
|
||||
</li>
|
||||
<li class="pure-menu-item">
|
||||
<a href="{{ url_for('get_backup')}}" class="pure-menu-link">BACKUP</a>
|
||||
</li>
|
||||
|
||||
@@ -2,99 +2,181 @@
|
||||
{% block content %}
|
||||
{% from '_helpers.jinja' import render_simple_field %}
|
||||
|
||||
<script type="text/javascript" src="{{url_for('static_content', group='js', filename='tbltools.js')}}"></script>
|
||||
|
||||
<div class="box">
|
||||
|
||||
<form class="pure-form" action="{{ url_for('api_watch_add') }}" method="POST" id="new-watch-form">
|
||||
<fieldset>
|
||||
<legend>Add a new change detection watch</legend>
|
||||
{{ render_simple_field(form.url, placeholder="https://...", required=true) }}
|
||||
{{ render_simple_field(form.tag, value=active_tag if active_tag else '', placeholder="tag") }}
|
||||
<button type="submit" class="pure-button pure-button-primary">Watch</button>
|
||||
</fieldset>
|
||||
<!-- add extra stuff, like do a http POST and send headers -->
|
||||
<!-- user/pass r = requests.get('https://api.github.com/user', auth=('user', 'pass')) -->
|
||||
</form>
|
||||
<div>
|
||||
<a href="{{url_for('index')}}" class="pure-button button-tag {{'active' if not active_tag }}">All</a>
|
||||
{% for tag in tags %}
|
||||
{% if tag != "" %}
|
||||
<a href="{{url_for('index', tag=tag) }}" class="pure-button button-tag {{'active' if active_tag == tag }}">{{ tag }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<form class="pure-form" action="{{ url_for('api_watch_add') }}" method="POST" id="new-watch-form">
|
||||
<fieldset>
|
||||
<legend>Add a new change detection watch</legend>
|
||||
{{ render_simple_field(form.url, placeholder="https://...", required=true) }}
|
||||
{{ render_simple_field(form.tag, value=active_tag if active_tag else '', placeholder="tag") }}
|
||||
<div id="watch-actions"><button type="submit" class="pure-button pure-button-primary" name="action" value="watch">Watch</button>
|
||||
<span id="add-paused"><label><input type="checkbox" name="add-paused"> Add Paused</label></span></div>
|
||||
</fieldset>
|
||||
<!-- add extra stuff, like do a http POST and send headers -->
|
||||
<!-- user/pass r = requests.get('https://api.github.com/user', auth=('user', 'pass')) -->
|
||||
</form>
|
||||
<div id="watch-table-wrapper">
|
||||
<table class="pure-table pure-table-striped watch-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>Last Checked</th>
|
||||
<th>Last Changed</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div id="flexlayout">
|
||||
<div id="categories">
|
||||
<a href="{{url_for('index')}}" class="pure-button button-tag {{'active' if not active_tag }}">All</a>
|
||||
{% for tag in tags %}
|
||||
{% if tag != "" %}
|
||||
<a href="{{url_for('index', tag=tag) }}" class="pure-button button-tag {{'active' if active_tag == tag }}">{{ tag }}</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
<div id="controls-top">
|
||||
<ul id="post-list-buttons-top">
|
||||
<span id="checkbox-functions" style="display: none;">
|
||||
<li>
|
||||
<a href="javascript:processChecked('recheck_selected', '{{ active_tag }}');" class="pure-button button-tag ">Recheck
|
||||
Selected {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('mark_selected_notviewed', '{{ active_tag }}');" class="pure-button button-tag ">Mark
|
||||
Selected as Unviewed {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('mark_selected_viewed', '{{ active_tag }}');" class="pure-button button-tag ">Mark
|
||||
Selected as Viewed {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('delete_selected', '{{ active_tag }}');" class="pure-button button-tag ">Delete
|
||||
Selected {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
</span>
|
||||
<span id="recheck-and-rss">
|
||||
<li>
|
||||
<a href="{{ url_for('api_watch_checknow', tag=active_tag) }}" class="pure-button button-tag ">Recheck
|
||||
All {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('index', tag=active_tag , rss=true)}}"><img id="feed-icon" src="{{url_for('static_content', group='images', filename='Generic_Feed-icon.svg')}}" height="15px"></a>
|
||||
</li>
|
||||
</span>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table class="pure-table pure-table-striped watch-table" id="watch-table">
|
||||
<thead>
|
||||
<tr id="header">
|
||||
<th onclick="sortTable(0)"><span id="clickable" title="Sort by Index"># <span id="sortable-0"><img src="{{url_for('static_content', group='images', filename='sortable.svg')}}" /></span><span id="sortarrow"><span id="sort-0a" style="display:none;">▲</span><span id="sort-0d" style="display:none;">▼</span></span></span></th>
|
||||
<th><input type="checkbox" name="showhide" onchange="checkAll(this)" title="Check/Uncheck All"></th>
|
||||
<th id="play-pause">
|
||||
<a href="{{url_for('index', pause='pause-all', tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="Pause" title="Pause All {%if active_tag%}in "{{active_tag}}" {%endif%}"/></a>
|
||||
<a href="{{url_for('index', pause='resume-all', tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='play.svg')}}" alt="Resume" title="Resume All {%if active_tag%}in "{{active_tag}}" {%endif%}"/></a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="javascript:processChecked('mark_all_notviewed', '{{ active_tag }}');"><img src="{{url_for('static_content', group='images', filename='notviewed.svg')}}" alt="Uniewed" title="Mark All{%if active_tag%} in "{{active_tag}}"{%endif%} as Unviewed"/></a>
|
||||
<a href="{{url_for('mark_all_viewed', tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='viewed.svg')}}" alt="Viewed" title="Mark All{%if active_tag%} in "{{active_tag}}"{%endif%} as Viewed"/></a>
|
||||
</th>
|
||||
<th onclick="sortTable(5)"><span id="clickable" title="Sort by Title">Title <span id="sortable-5"><img src="{{url_for('static_content', group='images', filename='sortable.svg')}}" /></span><span id="sortarrow"><span id="sort-5a" style="display:none;">▲</span><span id="sort-5d" style="display:none;">▼</span></span></span></th>
|
||||
<th id="hidden"></th>
|
||||
<th onclick="sortTable(7)"><span id="clickable" title="Sort by Last Checked">Checked <span id="sortable-7"><img src="{{url_for('static_content', group='images', filename='sortable.svg')}}" /></span><span id="sortarrow"><span id="sort-7a" style="display:none;">▲</span><span id="sort-7d" style="display:none;">▼</span></span></span></th>
|
||||
<th id="hidden"></th>
|
||||
<th onclick="sortTable(9)"><span id="clickable" title="Sort by Last Changed">Changed <span id="sortable-9" style="display:none;"><img src="{{url_for('static_content', group='images', filename='sortable.svg')}}" /></span><span id="sortarrow"><span id="sort-9a" style="display:none;">▲</span><span id="sort-9d">▼</span></span></span></th>
|
||||
<th id="hidden"></th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for watch in watches %}
|
||||
<tr id="{{ watch.uuid }}"
|
||||
class="{{ loop.cycle('pure-table-odd', 'pure-table-even') }}
|
||||
{% if watch.last_error is defined and watch.last_error != False %}error{% endif %}
|
||||
{% if watch.paused is defined and watch.paused != False %}paused{% endif %}
|
||||
{% if watch.newest_history_key| int > watch.last_viewed| int %}unviewed{% endif %}">
|
||||
<td class="inline low-res">{{ loop.index }}</td> <!-- col 0, sort by index -->
|
||||
<td class="inline low-res"><input id="{{watch.uuid}}" type="checkbox" name="check" onchange="checkChange();"></td>
|
||||
<td class="inline low-res">
|
||||
{% if watch.paused %}
|
||||
<a href="{{url_for('index', pause=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='play.svg')}}" alt="Resume" title="Resume"/></a>
|
||||
{% else %}
|
||||
<a href="{{url_for('index', pause=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="Pause" title="Pause"/></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="inline low-res">
|
||||
{% if watch.newest_history_key| int > watch.last_viewed| int %}
|
||||
<a href="{{url_for('process_selected', func='mark_selected_viewed', uuids=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='viewed.svg')}}" alt="Viewed" title="Mark as Viewed"/></a>
|
||||
{% else %}
|
||||
<a href="{{url_for('process_selected', func='mark_selected_notviewed', uuids=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='notviewed.svg')}}" alt="Unviewed" title="Mark as Unviewed"/></a>
|
||||
{% endif %}
|
||||
</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 inline-hover-img" target="_blank" rel="noopener" href="{{ watch.url }}"></a>
|
||||
{%if watch.fetch_backend == "html_webdriver" %}<img style="height: 1em; display:inline-block;" src="/static/images/Google-Chrome-icon.png" />{% endif %}
|
||||
|
||||
{% for watch in watches %}
|
||||
<tr id="{{ watch.uuid }}"
|
||||
class="{{ loop.cycle('pure-table-odd', 'pure-table-even') }}
|
||||
{% if watch.last_error is defined and watch.last_error != False %}error{% endif %}
|
||||
{% if watch.paused is defined and watch.paused != False %}paused{% endif %}
|
||||
{% if watch.newest_history_key| int > watch.last_viewed| int %}unviewed{% endif %}">
|
||||
<td class="inline">{{ loop.index }}</td>
|
||||
<td class="inline paused-state state-{{watch.paused}}"><a href="{{url_for('index', pause=watch.uuid, tag=active_tag)}}"><img src="{{url_for('static_content', group='images', filename='pause.svg')}}" alt="Pause" title="Pause"/></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.url }}"></a>
|
||||
{%if watch.fetch_backend == "html_webdriver" %}<img style="height: 1em; display:inline-block;" src="static/images/Google-Chrome-icon.png" />{% endif %}
|
||||
|
||||
{% if watch.last_error is defined and watch.last_error != False %}
|
||||
<div class="fetch-error">{{ watch.last_error }}</div>
|
||||
{% endif %}
|
||||
{% if not active_tag %}
|
||||
<span class="watch-tag-list">{{ watch.tag}}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="last-checked">{{watch|format_last_checked_time}}</td>
|
||||
<td class="last-changed">{% if watch.history|length >= 2 and watch.last_changed %}
|
||||
{{watch.last_changed|format_timestamp_timeago}}
|
||||
{% else %}
|
||||
Not yet
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('api_watch_checknow', uuid=watch.uuid, tag=request.args.get('tag')) }}"
|
||||
class="pure-button button-small pure-button-primary">Recheck</a>
|
||||
<a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button button-small pure-button-primary">Edit</a>
|
||||
{% if watch.history|length >= 2 %}
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary">Diff</a>
|
||||
{% else %}
|
||||
{% if watch.history|length == 1 %}
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<ul id="post-list-buttons">
|
||||
{% if has_unviewed %}
|
||||
<li>
|
||||
<a href="{{url_for('mark_all_viewed', tag=request.args.get('tag')) }}" class="pure-button button-tag ">Mark all viewed</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="{{ url_for('api_watch_checknow', tag=active_tag) }}" class="pure-button button-tag ">Recheck
|
||||
all {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('rss', tag=active_tag , token=app_rss_token)}}"><img id="feed-icon" src="{{url_for('static_content', group='images', filename='Generic_Feed-icon.svg')}}" height="15px"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% if watch.last_error is defined and watch.last_error != False %}
|
||||
<div class="fetch-error">{{ watch.last_error }}</div>
|
||||
{% endif %}
|
||||
{% if not active_tag %}
|
||||
<span class="watch-tag-list">{{ watch.tag}}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td id="hidden">{{ watch.title if watch.title else watch.url }}</td> <!-- col 5, title sorting -->
|
||||
<td class="last-checked">{{watch|format_last_checked_time}}</td>
|
||||
<td id="hidden">{{ watch.last_checked }}</td> <!-- col 7, last_checked sorting -->
|
||||
<td class="last-changed">{% if watch.history|length >= 2 and watch.last_changed %}
|
||||
{{watch.last_changed|format_timestamp_timeago}}
|
||||
{% else %}
|
||||
Not yet
|
||||
{% endif %}
|
||||
</td>
|
||||
<td id="hidden">{{ watch.last_changed }}</td> <!-- col 9, last_changed sorting -->
|
||||
<td id="actions">
|
||||
<a href="{{ url_for('api_watch_checknow', uuid=watch.uuid, tag=request.args.get('tag')) }}"
|
||||
class="pure-button button-small pure-button-primary">Recheck</a>
|
||||
<a href="{{ url_for('edit_page', uuid=watch.uuid)}}" class="pure-button button-small pure-button-primary">Edit</a>
|
||||
{% if watch.history|length >= 2 %}
|
||||
<a href="{{ url_for('diff_history_page', uuid=watch.uuid) }}" target="{{watch.uuid}}" class="pure-button button-small pure-button-primary">Diff</a>
|
||||
{% else %}
|
||||
{% if watch.history|length == 1 %}
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="controls-bottom">
|
||||
<ul id="post-list-buttons">
|
||||
<span id="recheck-and-rss">
|
||||
<li>
|
||||
<a href="{{ url_for('index', tag=active_tag , rss=true)}}"><img id="feed-icon" src="{{url_for('static_content', group='images', filename='Generic_Feed-icon.svg')}}" height="15px"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('api_watch_checknow', tag=active_tag) }}" class="pure-button button-tag ">Recheck
|
||||
All {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
</span>
|
||||
<span id="checkbox-functions" style="display: none;">
|
||||
<li>
|
||||
<a href="javascript:processChecked('delete_selected', '{{ active_tag }}');" class="pure-button button-tag ">Delete
|
||||
Selected {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('mark_selected_viewed', '{{ active_tag }}');" class="pure-button button-tag ">Mark
|
||||
Selected as Viewed {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('mark_selected_notviewed', '{{ active_tag }}');" class="pure-button button-tag ">Mark
|
||||
Selected as Unviewed {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:processChecked('recheck_selected', '{{ active_tag }}');" class="pure-button button-tag ">Recheck
|
||||
Selected {% if active_tag%}in "{{active_tag}}"{%endif%}</a>
|
||||
</li>
|
||||
</span>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||